github.com/apache/arrow/go/v14@v14.0.1/parquet/internal/encryption/decryptor.go (about) 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 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 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. 16 17 package encryption 18 19 import ( 20 "io" 21 22 "github.com/apache/arrow/go/v14/arrow/memory" 23 "github.com/apache/arrow/go/v14/parquet" 24 ) 25 26 // FileDecryptor is an interface used by the filereader for decrypting an 27 // entire parquet file as we go, usually constructed from the DecryptionProperties 28 type FileDecryptor interface { 29 // Returns the key for decrypting the footer if provided 30 GetFooterKey() string 31 // Provides the file level AAD security bytes 32 FileAad() string 33 // return which algorithm this decryptor was constructed for 34 Algorithm() parquet.Cipher 35 // return the FileDecryptionProperties that were used for this decryptor 36 Properties() *parquet.FileDecryptionProperties 37 // Clear out the decryption keys, this is automatically called after every 38 // successfully decrypted file to ensure that keys aren't kept around. 39 WipeOutDecryptionKeys() 40 // GetFooterDecryptor returns a Decryptor interface for use to decrypt the footer 41 // of a parquet file. 42 GetFooterDecryptor() Decryptor 43 // GetFooterDecryptorForColumnMeta returns a Decryptor interface for Column Metadata 44 // in the file footer using the AAD bytes provided. 45 GetFooterDecryptorForColumnMeta(aad string) Decryptor 46 // GetFooterDecryptorForColumnData returns the decryptor that can be used for decrypting 47 // actual column data footer bytes, not column metadata. 48 GetFooterDecryptorForColumnData(aad string) Decryptor 49 // GetColumnMetaDecryptor returns a decryptor for the requested column path, key and AAD bytes 50 // but only for decrypting the row group level metadata 51 GetColumnMetaDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor 52 // GetColumnDataDecryptor returns a decryptor for the requested column path, key, and AAD bytes 53 // but only for the rowgroup column data. 54 GetColumnDataDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor 55 } 56 57 type fileDecryptor struct { 58 // the properties contains the key retriever for us to get keys 59 // from the key metadata 60 props *parquet.FileDecryptionProperties 61 // concatenation of aad_prefix (if exists) and aad_file_unique 62 fileAad string 63 columnDataMap map[string]Decryptor 64 columnMetaDataMap map[string]Decryptor 65 footerMetadataDecryptor Decryptor 66 footerDataDecryptor Decryptor 67 alg parquet.Cipher 68 footerKeyMetadata string 69 metaDecryptor *aesDecryptor 70 dataDecryptor *aesDecryptor 71 mem memory.Allocator 72 } 73 74 // NewFileDecryptor constructs a decryptor from the provided configuration of properties, cipher and key metadata. Using the provided memory allocator or 75 // the default allocator if one isn't provided. 76 func NewFileDecryptor(props *parquet.FileDecryptionProperties, fileAad string, alg parquet.Cipher, keymetadata string, mem memory.Allocator) FileDecryptor { 77 if mem == nil { 78 mem = memory.DefaultAllocator 79 } 80 return &fileDecryptor{ 81 fileAad: fileAad, 82 props: props, 83 alg: alg, 84 footerKeyMetadata: keymetadata, 85 mem: mem, 86 columnDataMap: make(map[string]Decryptor), 87 columnMetaDataMap: make(map[string]Decryptor), 88 } 89 } 90 91 func (d *fileDecryptor) FileAad() string { return d.fileAad } 92 func (d *fileDecryptor) Properties() *parquet.FileDecryptionProperties { return d.props } 93 func (d *fileDecryptor) Algorithm() parquet.Cipher { return d.alg } 94 func (d *fileDecryptor) GetFooterKey() string { 95 footerKey := d.props.FooterKey() 96 if footerKey == "" { 97 if d.footerKeyMetadata == "" { 98 panic("no footer key or key metadata") 99 } 100 if d.props.KeyRetriever == nil { 101 panic("no footer key or key retriever") 102 } 103 footerKey = d.props.KeyRetriever.GetKey([]byte(d.footerKeyMetadata)) 104 } 105 if footerKey == "" { 106 panic("invalid footer encryption key. Could not parse footer metadata") 107 } 108 return footerKey 109 } 110 111 func (d *fileDecryptor) GetFooterDecryptor() Decryptor { 112 aad := CreateFooterAad(d.fileAad) 113 return d.getFooterDecryptor(aad, true) 114 } 115 116 func (d *fileDecryptor) GetFooterDecryptorForColumnMeta(aad string) Decryptor { 117 return d.getFooterDecryptor(aad, true) 118 } 119 120 func (d *fileDecryptor) GetFooterDecryptorForColumnData(aad string) Decryptor { 121 return d.getFooterDecryptor(aad, false) 122 } 123 124 func (d *fileDecryptor) GetColumnMetaDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor { 125 return d.getColumnDecryptor(columnPath, columnKeyMetadata, aad, true) 126 } 127 128 func (d *fileDecryptor) GetColumnDataDecryptor(columnPath, columnKeyMetadata, aad string) Decryptor { 129 return d.getColumnDecryptor(columnPath, columnKeyMetadata, aad, false) 130 } 131 132 func (d *fileDecryptor) WipeOutDecryptionKeys() { 133 d.props.WipeOutDecryptionKeys() 134 } 135 136 func (d *fileDecryptor) getFooterDecryptor(aad string, metadata bool) Decryptor { 137 if metadata { 138 if d.footerMetadataDecryptor != nil { 139 return d.footerMetadataDecryptor 140 } 141 } else { 142 if d.footerDataDecryptor != nil { 143 return d.footerDataDecryptor 144 } 145 } 146 147 footerKey := d.GetFooterKey() 148 149 // Create both data and metadata decryptors to avoid redundant retrieval of key 150 // from the key_retriever. 151 aesMetaDecrypt := d.getMetaAesDecryptor() 152 aesDataDecrypt := d.getDataAesDecryptor() 153 154 d.footerMetadataDecryptor = &decryptor{ 155 decryptor: aesMetaDecrypt, 156 key: []byte(footerKey), 157 fileAad: []byte(d.fileAad), 158 aad: []byte(aad), 159 mem: d.mem, 160 } 161 d.footerDataDecryptor = &decryptor{ 162 decryptor: aesDataDecrypt, 163 key: []byte(footerKey), 164 fileAad: []byte(d.fileAad), 165 aad: []byte(aad), 166 mem: d.mem, 167 } 168 169 if metadata { 170 return d.footerMetadataDecryptor 171 } 172 return d.footerDataDecryptor 173 } 174 175 func (d *fileDecryptor) getColumnDecryptor(columnPath, columnMeta, aad string, metadata bool) Decryptor { 176 if metadata { 177 if res, ok := d.columnMetaDataMap[columnPath]; ok { 178 res.UpdateAad(aad) 179 return res 180 } 181 } else { 182 if res, ok := d.columnDataMap[columnPath]; ok { 183 res.UpdateAad(aad) 184 return res 185 } 186 } 187 188 columnKey := d.props.ColumnKey(columnPath) 189 // No explicit column key given via API. Retrieve via key metadata. 190 if columnKey == "" && columnMeta != "" && d.props.KeyRetriever != nil { 191 columnKey = d.props.KeyRetriever.GetKey([]byte(columnMeta)) 192 } 193 if columnKey == "" { 194 panic("hidden column exception, path=" + columnPath) 195 } 196 197 aesDataDecrypt := d.getDataAesDecryptor() 198 aesMetaDecrypt := d.getMetaAesDecryptor() 199 200 d.columnDataMap[columnPath] = &decryptor{ 201 decryptor: aesDataDecrypt, 202 key: []byte(columnKey), 203 fileAad: []byte(d.fileAad), 204 aad: []byte(aad), 205 mem: d.mem, 206 } 207 d.columnMetaDataMap[columnPath] = &decryptor{ 208 decryptor: aesMetaDecrypt, 209 key: []byte(columnKey), 210 fileAad: []byte(d.fileAad), 211 aad: []byte(aad), 212 mem: d.mem, 213 } 214 215 if metadata { 216 return d.columnMetaDataMap[columnPath] 217 } 218 return d.columnDataMap[columnPath] 219 } 220 221 func (d *fileDecryptor) getMetaAesDecryptor() *aesDecryptor { 222 if d.metaDecryptor == nil { 223 d.metaDecryptor = newAesDecryptor(d.alg, true) 224 } 225 return d.metaDecryptor 226 } 227 228 func (d *fileDecryptor) getDataAesDecryptor() *aesDecryptor { 229 if d.dataDecryptor == nil { 230 d.dataDecryptor = newAesDecryptor(d.alg, false) 231 } 232 return d.dataDecryptor 233 } 234 235 // Decryptor is the basic interface for any decryptor generated from a FileDecryptor 236 type Decryptor interface { 237 // returns the File Level AAD bytes 238 FileAad() string 239 // returns the current allocator that was used for any extra allocations of buffers 240 Allocator() memory.Allocator 241 // returns the CiphertextSizeDelta from the decryptor 242 CiphertextSizeDelta() int 243 // Decrypt just returns the decrypted plaintext from the src ciphertext 244 Decrypt(src []byte) []byte 245 // Decrypt just returns the decrypted plaintext from the src ciphertext 246 DecryptFrom(r io.Reader) []byte 247 // set the AAD bytes of the decryptor to the provided string 248 UpdateAad(string) 249 } 250 251 type decryptor struct { 252 decryptor *aesDecryptor 253 key []byte 254 fileAad []byte 255 aad []byte 256 mem memory.Allocator 257 } 258 259 func (d *decryptor) Allocator() memory.Allocator { return d.mem } 260 func (d *decryptor) FileAad() string { return string(d.fileAad) } 261 func (d *decryptor) UpdateAad(aad string) { d.aad = []byte(aad) } 262 func (d *decryptor) CiphertextSizeDelta() int { return d.decryptor.CiphertextSizeDelta() } 263 func (d *decryptor) Decrypt(src []byte) []byte { 264 return d.decryptor.Decrypt(src, d.key, d.aad) 265 } 266 func (d *decryptor) DecryptFrom(r io.Reader) []byte { 267 return d.decryptor.DecryptFrom(r, d.key, d.aad) 268 }