blob: 4dc1492a0b7677a6d8843107802399f7829a128c [file] [log] [blame]
// 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.
#include <iostream>
#include "arrow/util/utf8.h"
#include "parquet/encryption/file_key_unwrapper.h"
#include "parquet/encryption/key_metadata.h"
using ::arrow::util::SecureString;
namespace parquet::encryption {
FileKeyUnwrapper::FileKeyUnwrapper(
std::shared_ptr<KeyToolkit> key_toolkit,
const KmsConnectionConfig& kms_connection_config, double cache_lifetime_seconds,
const std::string& file_path,
const std::shared_ptr<::arrow::fs::FileSystem>& file_system)
: FileKeyUnwrapper(std::move(key_toolkit), /*key_toolkit=*/nullptr,
kms_connection_config, cache_lifetime_seconds,
/*key_material_store=*/nullptr, file_path, file_system) {}
FileKeyUnwrapper::FileKeyUnwrapper(
KeyToolkit* key_toolkit, const KmsConnectionConfig& kms_connection_config,
double cache_lifetime_seconds, const std::string& file_path,
const std::shared_ptr<::arrow::fs::FileSystem>& file_system)
: FileKeyUnwrapper(/*key_toolkit_owner=*/nullptr, key_toolkit, kms_connection_config,
cache_lifetime_seconds, /*key_material_store=*/nullptr, file_path,
file_system) {}
FileKeyUnwrapper::FileKeyUnwrapper(
KeyToolkit* key_toolkit, const KmsConnectionConfig& kms_connection_config,
double cache_lifetime_seconds,
std::shared_ptr<FileKeyMaterialStore> key_material_store)
: FileKeyUnwrapper(/*key_toolkit_owner=*/nullptr, key_toolkit, kms_connection_config,
cache_lifetime_seconds, std::move(key_material_store),
/*file_path=*/"",
/*file_system=*/nullptr) {}
FileKeyUnwrapper::FileKeyUnwrapper(
std::shared_ptr<KeyToolkit> key_toolkit_owner, KeyToolkit* key_toolkit,
const KmsConnectionConfig& kms_connection_config, double cache_lifetime_seconds,
std::shared_ptr<FileKeyMaterialStore> key_material_store,
const std::string& file_path,
const std::shared_ptr<::arrow::fs::FileSystem>& file_system)
: key_toolkit_owner_(std::move(key_toolkit_owner)),
key_toolkit_(key_toolkit ? key_toolkit : key_toolkit_owner_.get()),
kms_connection_config_(kms_connection_config),
cache_entry_lifetime_seconds_(cache_lifetime_seconds),
key_material_store_(std::move(key_material_store)),
file_path_(file_path),
file_system_(file_system) {
kek_per_kek_id_ = key_toolkit_->kek_read_cache_per_token().GetOrCreateInternalCache(
kms_connection_config.key_access_token(), cache_entry_lifetime_seconds_);
}
SecureString FileKeyUnwrapper::GetKey(const std::string& key_metadata_bytes) {
// key_metadata is expected to be in UTF8 encoding
::arrow::util::InitializeUTF8();
if (!::arrow::util::ValidateUTF8(
reinterpret_cast<const uint8_t*>(key_metadata_bytes.data()),
key_metadata_bytes.size())) {
throw ParquetException("key metadata should be in UTF8 encoding");
}
KeyMetadata key_metadata = KeyMetadata::Parse(key_metadata_bytes);
KeyMaterial key_material;
if (key_metadata.key_material_stored_internally()) {
key_material = key_metadata.key_material();
} else {
if (key_material_store_ == nullptr) {
key_material_store_ =
FileSystemKeyMaterialStore::Make(file_path_, file_system_, false);
}
// External key material storage: key metadata contains a reference
// to a key in the material store
std::string key_id_in_file = key_metadata.key_reference();
std::string key_material_string = key_material_store_->GetKeyMaterial(key_id_in_file);
if (key_material_string.empty()) {
throw ParquetException("Could not find key material with ID '" + key_id_in_file +
"' in external key material file");
}
key_material = KeyMaterial::Parse(key_material_string);
}
return GetDataEncryptionKey(key_material).data_key();
}
KeyWithMasterId FileKeyUnwrapper::GetDataEncryptionKey(const KeyMaterial& key_material) {
auto kms_client = GetKmsClientFromConfigOrKeyMaterial(key_material);
bool double_wrapping = key_material.is_double_wrapped();
const std::string& master_key_id = key_material.master_key_id();
const std::string& encoded_wrapped_dek = key_material.wrapped_dek();
SecureString data_key;
if (!double_wrapping) {
data_key = kms_client->UnwrapKey(encoded_wrapped_dek, master_key_id);
} else {
// Get Key Encryption Key
const std::string& encoded_kek_id = key_material.kek_id();
const std::string& encoded_wrapped_kek = key_material.wrapped_kek();
const SecureString kek_bytes = kek_per_kek_id_->GetOrInsert(
encoded_kek_id, [kms_client, encoded_wrapped_kek, master_key_id]() {
return kms_client->UnwrapKey(encoded_wrapped_kek, master_key_id);
});
// Decrypt the data key
std::string aad = ::arrow::util::base64_decode(encoded_kek_id);
data_key = internal::DecryptKeyLocally(encoded_wrapped_dek, kek_bytes, aad);
}
return KeyWithMasterId(std::move(data_key), master_key_id);
}
std::shared_ptr<KmsClient> FileKeyUnwrapper::GetKmsClientFromConfigOrKeyMaterial(
const KeyMaterial& key_material) {
std::string& kms_instance_id = kms_connection_config_.kms_instance_id;
if (kms_instance_id.empty()) {
kms_instance_id = key_material.kms_instance_id();
if (kms_instance_id.empty()) {
throw ParquetException(
"KMS instance ID is missing both in both kms connection configuration and file "
"key material");
}
}
std::string& kms_instance_url = kms_connection_config_.kms_instance_url;
if (kms_instance_url.empty()) {
kms_instance_url = key_material.kms_instance_url();
if (kms_instance_url.empty()) {
throw ParquetException(
"KMS instance ID is missing both in both kms connection configuration and file "
"key material");
}
}
return key_toolkit_->GetKmsClient(kms_connection_config_,
cache_entry_lifetime_seconds_);
}
} // namespace parquet::encryption