File: encryptor.go

package info (click to toggle)
golang-github-apache-arrow-go 18.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 32,200 kB
  • sloc: asm: 477,547; ansic: 5,369; cpp: 759; sh: 585; makefile: 319; python: 190; sed: 5
file content (237 lines) | stat: -rw-r--r-- 7,667 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package encryption

import (
	"io"

	"github.com/apache/arrow-go/v18/arrow/memory"
	"github.com/apache/arrow-go/v18/parquet"
)

// FileEncryptor is the interface for constructing encryptors for the different
// sections of a parquet file.
type FileEncryptor interface {
	// GetFooterEncryptor returns an encryptor for the footer metadata
	GetFooterEncryptor() Encryptor
	// GetFooterSigningEncryptor returns an encryptor for creating the signature
	// for the footer as opposed to encrypting the footer bytes directly.
	GetFooterSigningEncryptor() Encryptor
	// GetColumnMetaEncryptor returns an encryptor for the metadata only of the requested
	// column path string.
	GetColumnMetaEncryptor(columnPath string) Encryptor
	// GetColumnDataEncryptor returns an encryptor for the column data ONLY of
	// the requested column path string.
	GetColumnDataEncryptor(columnPath string) Encryptor
	// WipeOutEncryptionKeys deletes the keys that were used for encryption,
	// called after every successfully encrypted file to ensure against accidental
	// key re-use.
	WipeOutEncryptionKeys()
}

type fileEncryptor struct {
	props                  *parquet.FileEncryptionProperties
	columnDataMap          map[string]Encryptor
	columnMetaDataMap      map[string]Encryptor
	footerSigningEncryptor Encryptor
	footerEncryptor        Encryptor

	// Key must be 16, 24, or 32 bytes in length thus there could be up to
	// three types of meta_encryptors and data_encryptors
	metaEncryptor *aesEncryptor
	dataEncryptor *aesEncryptor

	mem memory.Allocator
}

// NewFileEncryptor returns a new encryptor using the given encryption properties.
//
// Panics if the properties passed have already been used to construct an encryptor
// ie: props.IsUtilized returns true. If mem is nil, will default to memory.DefaultAllocator
func NewFileEncryptor(props *parquet.FileEncryptionProperties, mem memory.Allocator) FileEncryptor {
	if props.IsUtilized() {
		panic("re-using encryption properties for another file")
	}

	props.SetUtilized()
	if mem == nil {
		mem = memory.DefaultAllocator
	}

	return &fileEncryptor{
		props:             props,
		mem:               mem,
		columnDataMap:     make(map[string]Encryptor),
		columnMetaDataMap: make(map[string]Encryptor),
	}
}

func (e *fileEncryptor) WipeOutEncryptionKeys() {
	e.props.WipeOutEncryptionKeys()
}

func (e *fileEncryptor) GetFooterEncryptor() Encryptor {
	if e.footerEncryptor == nil {
		alg := e.props.Algorithm().Algo
		footerAad := CreateFooterAad(e.props.FileAad())
		footerKey := e.props.FooterKey()
		enc := e.getMetaAesEncryptor(alg)
		e.footerEncryptor = &encryptor{
			aesEncryptor: enc,
			key:          []byte(footerKey),
			fileAad:      e.props.FileAad(),
			aad:          footerAad,
			mem:          e.mem,
		}
	}
	return e.footerEncryptor
}

func (e *fileEncryptor) GetFooterSigningEncryptor() Encryptor {
	if e.footerSigningEncryptor == nil {
		alg := e.props.Algorithm().Algo
		footerAad := CreateFooterAad(e.props.FileAad())
		footerKey := e.props.FooterKey()
		enc := e.getMetaAesEncryptor(alg)
		e.footerSigningEncryptor = &encryptor{
			aesEncryptor: enc,
			key:          []byte(footerKey),
			fileAad:      e.props.FileAad(),
			aad:          footerAad,
			mem:          e.mem,
		}
	}
	return e.footerSigningEncryptor
}

func (e *fileEncryptor) getMetaAesEncryptor(alg parquet.Cipher) *aesEncryptor {
	if e.metaEncryptor == nil {
		e.metaEncryptor = NewAesEncryptor(alg, true)
	}
	return e.metaEncryptor
}

func (e *fileEncryptor) getDataAesEncryptor(alg parquet.Cipher) *aesEncryptor {
	if e.dataEncryptor == nil {
		e.dataEncryptor = NewAesEncryptor(alg, false)
	}
	return e.dataEncryptor
}

func (e *fileEncryptor) GetColumnMetaEncryptor(columnPath string) Encryptor {
	return e.getColumnEncryptor(columnPath, true)
}

func (e *fileEncryptor) GetColumnDataEncryptor(columnPath string) Encryptor {
	return e.getColumnEncryptor(columnPath, false)
}

func (e *fileEncryptor) getColumnEncryptor(columnPath string, metadata bool) Encryptor {
	if metadata {
		if enc, ok := e.columnMetaDataMap[columnPath]; ok {
			return enc
		}
	} else {
		if enc, ok := e.columnDataMap[columnPath]; ok {
			return enc
		}
	}

	columnProp := e.props.ColumnEncryptionProperties(columnPath)
	if columnProp == nil {
		return nil
	}

	var key string
	if columnProp.IsEncryptedWithFooterKey() {
		key = e.props.FooterKey()
	} else {
		key = columnProp.Key()
	}

	alg := e.props.Algorithm().Algo
	var enc *aesEncryptor
	if metadata {
		enc = e.getMetaAesEncryptor(alg)
	} else {
		enc = e.getDataAesEncryptor(alg)
	}

	fileAad := e.props.FileAad()
	ret := &encryptor{
		aesEncryptor: enc,
		key:          []byte(key),
		fileAad:      fileAad,
		aad:          "",
		mem:          e.mem,
	}
	if metadata {
		e.columnMetaDataMap[columnPath] = ret
	} else {
		e.columnDataMap[columnPath] = ret
	}
	return ret
}

// Encryptor is the basic interface for encryptors, for now there's only the single
// aes encryptor implementation, but having it as an interface allows easy addition
// manipulation of encryptor implementations in the future.
type Encryptor interface {
	// FileAad returns the file level AAD bytes for this encryptor
	FileAad() string
	// UpdateAad sets the aad bytes for encryption to the provided string
	UpdateAad(string)
	// Allocator returns the allocator that was used to construct the encryptor
	Allocator() memory.Allocator
	// CiphertextSizeDelta returns the extra bytes that will be added to the ciphertext
	// for a total size of len(plaintext) + CiphertextSizeDelta bytes
	CiphertextSizeDelta() int
	// Encrypt writes the encrypted ciphertext for src to w and returns the total
	// number of bytes written.
	Encrypt(w io.Writer, src []byte) int
	// EncryptColumnMetaData returns true if the column metadata should be encrypted based on the
	// column encryption settings and footer encryption setting.
	EncryptColumnMetaData(encryptFooter bool, properties *parquet.ColumnEncryptionProperties) bool
}

type encryptor struct {
	aesEncryptor *aesEncryptor
	key          []byte
	fileAad      string
	aad          string
	mem          memory.Allocator
}

func (e *encryptor) FileAad() string             { return e.fileAad }
func (e *encryptor) UpdateAad(aad string)        { e.aad = aad }
func (e *encryptor) Allocator() memory.Allocator { return e.mem }
func (e *encryptor) CiphertextSizeDelta() int    { return e.aesEncryptor.CiphertextSizeDelta() }

func (e *encryptor) EncryptColumnMetaData(encryptFooter bool, properties *parquet.ColumnEncryptionProperties) bool {
	if properties == nil || !properties.IsEncrypted() {
		return false
	}
	if !encryptFooter {
		return false
	}
	// if not encrypted with footer key then encrypt the metadata
	return !properties.IsEncryptedWithFooterKey()
}

func (e *encryptor) Encrypt(w io.Writer, src []byte) int {
	return e.aesEncryptor.Encrypt(w, src, e.key, []byte(e.aad))
}