1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
20 "github.com/apache/arrow/go/v6/arrow/memory"
21 "github.com/apache/arrow/go/v6/parquet"
24 // FileDecryptor is an interface used by the filereader for decrypting an
25 // entire parquet file as we go, usually constructed from the DecryptionProperties
26 type FileDecryptor interface {
27 // Returns the key for decrypting the footer if provided
29 // Provides the file level AAD security bytes
31 // return which algorithm this decryptor was constructed for
32 Algorithm() parquet.Cipher
33 // return the FileDecryptionProperties that were used for this decryptor
34 Properties() *parquet.FileDecryptionProperties
35 // Clear out the decryption keys, this is automatically called after every
36 // successfully decrypted file to ensure that keys aren't kept around.
37 WipeOutDecryptionKeys()
38 // GetFooterDecryptor returns a Decryptor interface for use to decrypt the footer
40 GetFooterDecryptor() Decryptor
41 // GetFooterDecryptorForColumnMeta returns a Decryptor interface for Column Metadata
42 // in the file footer using the AAD bytes provided.
43 GetFooterDecryptorForColumnMeta(aad string) Decryptor
44 // GetFooterDecryptorForColumnData returns the decryptor that can be used for decrypting
45 // actual column data footer bytes, not column metadata.
46 GetFooterDecryptorForColumnData(aad string) Decryptor
47 // GetColumnMetaDecryptor returns a decryptor for the requested column path, key and AAD bytes
48 // but only for decrypting the row group level metadata
49 GetColumnMetaDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor
50 // GetColumnDataDecryptor returns a decryptor for the requested column path, key, and AAD bytes
51 // but only for the rowgroup column data.
52 GetColumnDataDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor
55 type fileDecryptor struct {
56 // the properties contains the key retriever for us to get keys
57 // from the key metadata
58 props *parquet.FileDecryptionProperties
59 // concatenation of aad_prefix (if exists) and aad_file_unique
61 columnDataMap map[string]Decryptor
62 columnMetaDataMap map[string]Decryptor
63 footerMetadataDecryptor Decryptor
64 footerDataDecryptor Decryptor
66 footerKeyMetadata string
67 metaDecryptor *aesDecryptor
68 dataDecryptor *aesDecryptor
72 // NewFileDecryptor constructs a decryptor from the provided configuration of properties, cipher and key metadata. Using the provided memory allocator or
73 // the default allocator if one isn't provided.
74 func NewFileDecryptor(props *parquet.FileDecryptionProperties, fileAad string, alg parquet.Cipher, keymetadata string, mem memory.Allocator) FileDecryptor {
76 mem = memory.DefaultAllocator
78 return &fileDecryptor{
82 footerKeyMetadata: keymetadata,
84 columnDataMap: make(map[string]Decryptor),
85 columnMetaDataMap: make(map[string]Decryptor),
89 func (d *fileDecryptor) FileAad() string { return d.fileAad }
90 func (d *fileDecryptor) Properties() *parquet.FileDecryptionProperties { return d.props }
91 func (d *fileDecryptor) Algorithm() parquet.Cipher { return d.alg }
92 func (d *fileDecryptor) GetFooterKey() string {
93 footerKey := d.props.FooterKey()
95 if d.footerKeyMetadata == "" {
96 panic("no footer key or key metadata")
98 if d.props.KeyRetriever == nil {
99 panic("no footer key or key retriever")
101 footerKey = d.props.KeyRetriever.GetKey([]byte(d.footerKeyMetadata))
104 panic("invalid footer encryption key. Could not parse footer metadata")
109 func (d *fileDecryptor) GetFooterDecryptor() Decryptor {
110 aad := CreateFooterAad(d.fileAad)
111 return d.getFooterDecryptor(aad, true)
114 func (d *fileDecryptor) GetFooterDecryptorForColumnMeta(aad string) Decryptor {
115 return d.getFooterDecryptor(aad, true)
118 func (d *fileDecryptor) GetFooterDecryptorForColumnData(aad string) Decryptor {
119 return d.getFooterDecryptor(aad, false)
122 func (d *fileDecryptor) GetColumnMetaDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor {
123 return d.getColumnDecryptor(columnPath, columnKeyMetadata, aad, true)
126 func (d *fileDecryptor) GetColumnDataDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor {
127 return d.getColumnDecryptor(columnPath, columnKeyMetadata, aad, false)
130 func (d *fileDecryptor) WipeOutDecryptionKeys() {
131 d.props.WipeOutDecryptionKeys()
134 func (d *fileDecryptor) getFooterDecryptor(aad string, metadata bool) Decryptor {
136 if d.footerMetadataDecryptor != nil {
137 return d.footerMetadataDecryptor
140 if d.footerDataDecryptor != nil {
141 return d.footerDataDecryptor
145 footerKey := d.GetFooterKey()
147 // Create both data and metadata decryptors to avoid redundant retrieval of key
148 // from the key_retriever.
149 aesMetaDecrypt := d.getMetaAesDecryptor()
150 aesDataDecrypt := d.getDataAesDecryptor()
152 d.footerMetadataDecryptor = &decryptor{
153 decryptor: aesMetaDecrypt,
154 key: []byte(footerKey),
155 fileAad: []byte(d.fileAad),
159 d.footerDataDecryptor = &decryptor{
160 decryptor: aesDataDecrypt,
161 key: []byte(footerKey),
162 fileAad: []byte(d.fileAad),
168 return d.footerMetadataDecryptor
170 return d.footerDataDecryptor
173 func (d *fileDecryptor) getColumnDecryptor(columnPath, columnMeta, aad string, metadata bool) Decryptor {
175 if res, ok := d.columnMetaDataMap[columnPath]; ok {
180 if res, ok := d.columnDataMap[columnPath]; ok {
186 columnKey := d.props.ColumnKey(columnPath)
187 // No explicit column key given via API. Retrieve via key metadata.
188 if columnKey == "" && columnMeta != "" && d.props.KeyRetriever != nil {
189 columnKey = d.props.KeyRetriever.GetKey([]byte(columnMeta))
192 panic("hidden column exception, path=" + columnPath)
195 aesDataDecrypt := d.getDataAesDecryptor()
196 aesMetaDecrypt := d.getMetaAesDecryptor()
198 d.columnDataMap[columnPath] = &decryptor{
199 decryptor: aesDataDecrypt,
200 key: []byte(columnKey),
201 fileAad: []byte(d.fileAad),
205 d.columnMetaDataMap[columnPath] = &decryptor{
206 decryptor: aesMetaDecrypt,
207 key: []byte(columnKey),
208 fileAad: []byte(d.fileAad),
214 return d.columnMetaDataMap[columnPath]
216 return d.columnDataMap[columnPath]
219 func (d *fileDecryptor) getMetaAesDecryptor() *aesDecryptor {
220 if d.metaDecryptor == nil {
221 d.metaDecryptor = newAesDecryptor(d.alg, true)
223 return d.metaDecryptor
226 func (d *fileDecryptor) getDataAesDecryptor() *aesDecryptor {
227 if d.dataDecryptor == nil {
228 d.dataDecryptor = newAesDecryptor(d.alg, false)
230 return d.dataDecryptor
233 // Decryptor is the basic interface for any decryptor generated from a FileDecryptor
234 type Decryptor interface {
235 // returns the File Level AAD bytes
237 // returns the current allocator that was used for any extra allocations of buffers
238 Allocator() memory.Allocator
239 // returns the CiphertextSizeDelta from the decryptor
240 CiphertextSizeDelta() int
241 // Decrypt just returns the decrypted plaintext from the src ciphertext
242 Decrypt(src []byte) []byte
243 // set the AAD bytes of the decryptor to the provided string
247 type decryptor struct {
248 decryptor *aesDecryptor
255 func (d *decryptor) Allocator() memory.Allocator { return d.mem }
256 func (d *decryptor) FileAad() string { return string(d.fileAad) }
257 func (d *decryptor) UpdateAad(aad string) { d.aad = []byte(aad) }
258 func (d *decryptor) CiphertextSizeDelta() int { return d.decryptor.CiphertextSizeDelta() }
259 func (d *decryptor) Decrypt(src []byte) []byte {
260 return d.decryptor.Decrypt(src, d.key, d.aad)