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,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17
18 #include "parquet/internal_file_decryptor.h"
19 #include "parquet/encryption.h"
20 #include "parquet/encryption_internal.h"
21
22 namespace parquet {
23
24 // Decryptor
Decryptor(encryption::AesDecryptor * aes_decryptor,const std::string & key,const std::string & file_aad,const std::string & aad,::arrow::MemoryPool * pool)25 Decryptor::Decryptor(encryption::AesDecryptor* aes_decryptor, const std::string& key,
26 const std::string& file_aad, const std::string& aad,
27 ::arrow::MemoryPool* pool)
28 : aes_decryptor_(aes_decryptor),
29 key_(key),
30 file_aad_(file_aad),
31 aad_(aad),
32 pool_(pool) {}
33
CiphertextSizeDelta()34 int Decryptor::CiphertextSizeDelta() { return aes_decryptor_->CiphertextSizeDelta(); }
35
Decrypt(const uint8_t * ciphertext,int ciphertext_len,uint8_t * plaintext)36 int Decryptor::Decrypt(const uint8_t* ciphertext, int ciphertext_len,
37 uint8_t* plaintext) {
38 return aes_decryptor_->Decrypt(ciphertext, ciphertext_len, str2bytes(key_),
39 static_cast<int>(key_.size()), str2bytes(aad_),
40 static_cast<int>(aad_.size()), plaintext);
41 }
42
43 // InternalFileDecryptor
InternalFileDecryptor(FileDecryptionProperties * properties,const std::string & file_aad,ParquetCipher::type algorithm,const std::string & footer_key_metadata,::arrow::MemoryPool * pool)44 InternalFileDecryptor::InternalFileDecryptor(FileDecryptionProperties* properties,
45 const std::string& file_aad,
46 ParquetCipher::type algorithm,
47 const std::string& footer_key_metadata,
48 ::arrow::MemoryPool* pool)
49 : properties_(properties),
50 file_aad_(file_aad),
51 algorithm_(algorithm),
52 footer_key_metadata_(footer_key_metadata),
53 pool_(pool) {
54 if (properties_->is_utilized()) {
55 throw ParquetException(
56 "Re-using decryption properties with explicit keys for another file");
57 }
58 properties_->set_utilized();
59 }
60
WipeOutDecryptionKeys()61 void InternalFileDecryptor::WipeOutDecryptionKeys() {
62 properties_->WipeOutDecryptionKeys();
63 for (auto const& i : all_decryptors_) {
64 i->WipeOut();
65 }
66 }
67
GetFooterKey()68 std::string InternalFileDecryptor::GetFooterKey() {
69 std::string footer_key = properties_->footer_key();
70 // ignore footer key metadata if footer key is explicitly set via API
71 if (footer_key.empty()) {
72 if (footer_key_metadata_.empty())
73 throw ParquetException("No footer key or key metadata");
74 if (properties_->key_retriever() == nullptr)
75 throw ParquetException("No footer key or key retriever");
76 try {
77 footer_key = properties_->key_retriever()->GetKey(footer_key_metadata_);
78 } catch (KeyAccessDeniedException& e) {
79 std::stringstream ss;
80 ss << "Footer key: access denied " << e.what() << "\n";
81 throw ParquetException(ss.str());
82 }
83 }
84 if (footer_key.empty()) {
85 throw ParquetException(
86 "Footer key unavailable. Could not verify "
87 "plaintext footer metadata");
88 }
89 return footer_key;
90 }
91
GetFooterDecryptor()92 std::shared_ptr<Decryptor> InternalFileDecryptor::GetFooterDecryptor() {
93 std::string aad = encryption::CreateFooterAad(file_aad_);
94 return GetFooterDecryptor(aad, true);
95 }
96
GetFooterDecryptorForColumnMeta(const std::string & aad)97 std::shared_ptr<Decryptor> InternalFileDecryptor::GetFooterDecryptorForColumnMeta(
98 const std::string& aad) {
99 return GetFooterDecryptor(aad, true);
100 }
101
GetFooterDecryptorForColumnData(const std::string & aad)102 std::shared_ptr<Decryptor> InternalFileDecryptor::GetFooterDecryptorForColumnData(
103 const std::string& aad) {
104 return GetFooterDecryptor(aad, false);
105 }
106
GetFooterDecryptor(const std::string & aad,bool metadata)107 std::shared_ptr<Decryptor> InternalFileDecryptor::GetFooterDecryptor(
108 const std::string& aad, bool metadata) {
109 if (metadata) {
110 if (footer_metadata_decryptor_ != nullptr) return footer_metadata_decryptor_;
111 } else {
112 if (footer_data_decryptor_ != nullptr) return footer_data_decryptor_;
113 }
114
115 std::string footer_key = properties_->footer_key();
116 if (footer_key.empty()) {
117 if (footer_key_metadata_.empty())
118 throw ParquetException("No footer key or key metadata");
119 if (properties_->key_retriever() == nullptr)
120 throw ParquetException("No footer key or key retriever");
121 try {
122 footer_key = properties_->key_retriever()->GetKey(footer_key_metadata_);
123 } catch (KeyAccessDeniedException& e) {
124 std::stringstream ss;
125 ss << "Footer key: access denied " << e.what() << "\n";
126 throw ParquetException(ss.str());
127 }
128 }
129 if (footer_key.empty()) {
130 throw ParquetException(
131 "Invalid footer encryption key. "
132 "Could not parse footer metadata");
133 }
134
135 // Create both data and metadata decryptors to avoid redundant retrieval of key
136 // from the key_retriever.
137 auto aes_metadata_decryptor = GetMetaAesDecryptor(footer_key.size());
138 auto aes_data_decryptor = GetDataAesDecryptor(footer_key.size());
139
140 footer_metadata_decryptor_ = std::make_shared<Decryptor>(
141 aes_metadata_decryptor, footer_key, file_aad_, aad, pool_);
142 footer_data_decryptor_ =
143 std::make_shared<Decryptor>(aes_data_decryptor, footer_key, file_aad_, aad, pool_);
144
145 if (metadata) return footer_metadata_decryptor_;
146 return footer_data_decryptor_;
147 }
148
GetColumnMetaDecryptor(const std::string & column_path,const std::string & column_key_metadata,const std::string & aad)149 std::shared_ptr<Decryptor> InternalFileDecryptor::GetColumnMetaDecryptor(
150 const std::string& column_path, const std::string& column_key_metadata,
151 const std::string& aad) {
152 return GetColumnDecryptor(column_path, column_key_metadata, aad, true);
153 }
154
GetColumnDataDecryptor(const std::string & column_path,const std::string & column_key_metadata,const std::string & aad)155 std::shared_ptr<Decryptor> InternalFileDecryptor::GetColumnDataDecryptor(
156 const std::string& column_path, const std::string& column_key_metadata,
157 const std::string& aad) {
158 return GetColumnDecryptor(column_path, column_key_metadata, aad, false);
159 }
160
GetColumnDecryptor(const std::string & column_path,const std::string & column_key_metadata,const std::string & aad,bool metadata)161 std::shared_ptr<Decryptor> InternalFileDecryptor::GetColumnDecryptor(
162 const std::string& column_path, const std::string& column_key_metadata,
163 const std::string& aad, bool metadata) {
164 std::string column_key;
165 // first look if we already got the decryptor from before
166 if (metadata) {
167 if (column_metadata_map_.find(column_path) != column_metadata_map_.end()) {
168 auto res(column_metadata_map_.at(column_path));
169 res->UpdateAad(aad);
170 return res;
171 }
172 } else {
173 if (column_data_map_.find(column_path) != column_data_map_.end()) {
174 auto res(column_data_map_.at(column_path));
175 res->UpdateAad(aad);
176 return res;
177 }
178 }
179
180 column_key = properties_->column_key(column_path);
181 // No explicit column key given via API. Retrieve via key metadata.
182 if (column_key.empty() && !column_key_metadata.empty() &&
183 properties_->key_retriever() != nullptr) {
184 try {
185 column_key = properties_->key_retriever()->GetKey(column_key_metadata);
186 } catch (KeyAccessDeniedException& e) {
187 std::stringstream ss;
188 ss << "HiddenColumnException, path=" + column_path + " " << e.what() << "\n";
189 throw HiddenColumnException(ss.str());
190 }
191 }
192 if (column_key.empty()) {
193 throw HiddenColumnException("HiddenColumnException, path=" + column_path);
194 }
195
196 // Create both data and metadata decryptors to avoid redundant retrieval of key
197 // using the key_retriever.
198 auto aes_metadata_decryptor = GetMetaAesDecryptor(column_key.size());
199 auto aes_data_decryptor = GetDataAesDecryptor(column_key.size());
200
201 column_metadata_map_[column_path] = std::make_shared<Decryptor>(
202 aes_metadata_decryptor, column_key, file_aad_, aad, pool_);
203 column_data_map_[column_path] =
204 std::make_shared<Decryptor>(aes_data_decryptor, column_key, file_aad_, aad, pool_);
205
206 if (metadata) return column_metadata_map_[column_path];
207 return column_data_map_[column_path];
208 }
209
MapKeyLenToDecryptorArrayIndex(int key_len)210 int InternalFileDecryptor::MapKeyLenToDecryptorArrayIndex(int key_len) {
211 if (key_len == 16)
212 return 0;
213 else if (key_len == 24)
214 return 1;
215 else if (key_len == 32)
216 return 2;
217 throw ParquetException("decryption key must be 16, 24 or 32 bytes in length");
218 }
219
GetMetaAesDecryptor(size_t key_size)220 encryption::AesDecryptor* InternalFileDecryptor::GetMetaAesDecryptor(size_t key_size) {
221 int key_len = static_cast<int>(key_size);
222 int index = MapKeyLenToDecryptorArrayIndex(key_len);
223 if (meta_decryptor_[index] == nullptr) {
224 meta_decryptor_[index].reset(
225 encryption::AesDecryptor::Make(algorithm_, key_len, true, &all_decryptors_));
226 }
227 return meta_decryptor_[index].get();
228 }
229
GetDataAesDecryptor(size_t key_size)230 encryption::AesDecryptor* InternalFileDecryptor::GetDataAesDecryptor(size_t key_size) {
231 int key_len = static_cast<int>(key_size);
232 int index = MapKeyLenToDecryptorArrayIndex(key_len);
233 if (data_decryptor_[index] == nullptr) {
234 data_decryptor_[index].reset(
235 encryption::AesDecryptor::Make(algorithm_, key_len, false, &all_decryptors_));
236 }
237 return data_decryptor_[index].get();
238 }
239
240 } // namespace parquet
241