1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package encryption
22
23 import (
24 "bytes"
25 "crypto/aes"
26 "crypto/cipher"
27 "crypto/rand"
28 "encoding/binary"
29 "fmt"
30 "io"
31
32 "github.com/apache/arrow/go/v15/parquet"
33 )
34
35
36 const (
37 GcmTagLength = 16
38 NonceLength = 12
39
40 gcmMode = 0
41 ctrMode = 1
42 ctrIVLen = 16
43 bufferSizeLength = 4
44 )
45
46
47
48 const (
49 FooterModule int8 = iota
50 ColumnMetaModule
51 DataPageModule
52 DictPageModule
53 DataPageHeaderModule
54 DictPageHeaderModule
55 ColumnIndexModule
56 OffsetIndexModule
57 )
58
59 type aesEncryptor struct {
60 mode int
61 ciphertextSizeDelta int
62 }
63
64
65
66 func NewAesEncryptor(alg parquet.Cipher, metadata bool) *aesEncryptor {
67 ret := &aesEncryptor{}
68 ret.ciphertextSizeDelta = bufferSizeLength + NonceLength
69 if metadata || alg == parquet.AesGcm {
70 ret.mode = gcmMode
71 ret.ciphertextSizeDelta += GcmTagLength
72 } else {
73 ret.mode = ctrMode
74 }
75
76 return ret
77 }
78
79
80
81 func (a *aesEncryptor) CiphertextSizeDelta() int { return a.ciphertextSizeDelta }
82
83
84
85 func (a *aesEncryptor) SignedFooterEncrypt(w io.Writer, footer, key, aad, nonce []byte) int {
86 if a.mode != gcmMode {
87 panic("must use AES GCM (metadata) encryptor")
88 }
89
90 block, err := aes.NewCipher(key)
91 if err != nil {
92 panic(err)
93 }
94
95 aead, err := cipher.NewGCM(block)
96 if err != nil {
97 panic(err)
98 }
99 if aead.NonceSize() != NonceLength {
100 panic(fmt.Errorf("nonce size mismatch %d, %d", aead.NonceSize(), NonceLength))
101 }
102 if aead.Overhead() != GcmTagLength {
103 panic(fmt.Errorf("tagsize mismatch %d %d", aead.Overhead(), GcmTagLength))
104 }
105
106 ciphertext := aead.Seal(nil, nonce, footer, aad)
107 bufferSize := uint32(len(ciphertext) + len(nonce))
108
109 if err := binary.Write(w, binary.LittleEndian, bufferSize); err != nil {
110 panic(err)
111 }
112 w.Write(nonce)
113 w.Write(ciphertext)
114 return bufferSizeLength + int(bufferSize)
115 }
116
117
118
119 func (a *aesEncryptor) Encrypt(w io.Writer, src, key, aad []byte) int {
120 block, err := aes.NewCipher(key)
121 if err != nil {
122 panic(err)
123 }
124
125 nonce := make([]byte, NonceLength)
126 rand.Read(nonce)
127
128 if a.mode == gcmMode {
129 aead, err := cipher.NewGCM(block)
130 if err != nil {
131 panic(err)
132 }
133 if aead.NonceSize() != NonceLength {
134 panic(fmt.Errorf("nonce size mismatch %d, %d", aead.NonceSize(), NonceLength))
135 }
136 if aead.Overhead() != GcmTagLength {
137 panic(fmt.Errorf("tagsize mismatch %d %d", aead.Overhead(), GcmTagLength))
138 }
139
140 ciphertext := aead.Seal(nil, nonce, src, aad)
141 bufferSize := len(ciphertext) + len(nonce)
142
143 if err := binary.Write(w, binary.LittleEndian, uint32(bufferSize)); err != nil {
144 panic(err)
145 }
146 w.Write(nonce)
147 w.Write(ciphertext)
148 return bufferSizeLength + bufferSize
149 }
150
151
152
153
154
155 iv := make([]byte, ctrIVLen)
156 copy(iv, nonce)
157 iv[ctrIVLen-1] = 1
158
159 bufferSize := NonceLength + len(src)
160
161 if err := binary.Write(w, binary.LittleEndian, uint32(bufferSize)); err != nil {
162 panic(err)
163 }
164 w.Write(nonce)
165 cipher.StreamWriter{S: cipher.NewCTR(block, iv), W: w}.Write(src)
166 return bufferSizeLength + bufferSize
167 }
168
169 type aesDecryptor struct {
170 mode int
171 ciphertextSizeDelta int
172 }
173
174
175
176 func newAesDecryptor(alg parquet.Cipher, metadata bool) *aesDecryptor {
177 ret := &aesDecryptor{}
178 ret.ciphertextSizeDelta = bufferSizeLength + NonceLength
179 if metadata || alg == parquet.AesGcm {
180 ret.mode = gcmMode
181 ret.ciphertextSizeDelta += GcmTagLength
182 } else {
183 ret.mode = ctrMode
184 }
185
186 return ret
187 }
188
189
190
191
192 func (a *aesDecryptor) CiphertextSizeDelta() int { return a.ciphertextSizeDelta }
193
194
195 func (a *aesDecryptor) DecryptFrom(r io.Reader, key, aad []byte) []byte {
196 block, err := aes.NewCipher(key)
197 if err != nil {
198 panic(err)
199 }
200
201 var writtenCiphertextLen uint32
202 if err := binary.Read(r, binary.LittleEndian, &writtenCiphertextLen); err != nil {
203 panic(err)
204 }
205
206 cipherText := make([]byte, writtenCiphertextLen)
207 if n, err := io.ReadFull(r, cipherText); n != int(writtenCiphertextLen) || err != nil {
208 panic(err)
209 }
210
211 nonce := cipherText[:NonceLength]
212 cipherText = cipherText[NonceLength:]
213 if a.mode == gcmMode {
214 aead, err := cipher.NewGCM(block)
215 if err != nil {
216 panic(err)
217 }
218
219 plain, err := aead.Open(cipherText[:0], nonce, cipherText, aad)
220 if err != nil {
221 panic(err)
222 }
223 return plain
224 }
225
226
227
228
229
230 iv := make([]byte, ctrIVLen)
231 copy(iv, nonce)
232 iv[ctrIVLen-1] = 1
233
234 stream := cipher.NewCTR(block, iv)
235
236 stream.XORKeyStream(cipherText, cipherText)
237 return cipherText
238 }
239
240
241
242 func (a *aesDecryptor) Decrypt(cipherText, key, aad []byte) []byte {
243 block, err := aes.NewCipher(key)
244 if err != nil {
245 panic(err)
246 }
247
248 writtenCiphertextLen := binary.LittleEndian.Uint32(cipherText)
249 cipherLen := writtenCiphertextLen + bufferSizeLength
250 nonce := cipherText[bufferSizeLength : bufferSizeLength+NonceLength]
251
252 if a.mode == gcmMode {
253 aead, err := cipher.NewGCM(block)
254 if err != nil {
255 panic(err)
256 }
257
258 plain, err := aead.Open(nil, nonce, cipherText[bufferSizeLength+NonceLength:cipherLen], aad)
259 if err != nil {
260 panic(err)
261 }
262 return plain
263 }
264
265
266
267
268
269 iv := make([]byte, ctrIVLen)
270 copy(iv, nonce)
271 iv[ctrIVLen-1] = 1
272
273 stream := cipher.NewCTR(block, iv)
274 dst := make([]byte, len(cipherText)-bufferSizeLength-NonceLength)
275 stream.XORKeyStream(dst, cipherText[bufferSizeLength+NonceLength:])
276 return dst
277 }
278
279
280
281
282 func CreateModuleAad(fileAad string, moduleType int8, rowGroupOrdinal, columnOrdinal, pageOrdinal int16) string {
283 buf := bytes.NewBuffer([]byte(fileAad))
284 buf.WriteByte(byte(moduleType))
285
286 if moduleType == FooterModule {
287 return buf.String()
288 }
289
290 binary.Write(buf, binary.LittleEndian, rowGroupOrdinal)
291 binary.Write(buf, binary.LittleEndian, columnOrdinal)
292 if DataPageModule != moduleType && DataPageHeaderModule != moduleType {
293 return buf.String()
294 }
295
296 binary.Write(buf, binary.LittleEndian, pageOrdinal)
297 return buf.String()
298 }
299
300
301
302 func CreateFooterAad(aadPrefix string) string {
303 return CreateModuleAad(aadPrefix, FooterModule, -1, -1, -1)
304 }
305
306
307
308 func QuickUpdatePageAad(aad []byte, newPageOrdinal int16) {
309 binary.LittleEndian.PutUint16(aad[len(aad)-2:], uint16(newPageOrdinal))
310 }
311
View as plain text