kmscng/object.cc (114 lines of code) (raw):

// Copyright 2023 Google LLC // // Licensed 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 "kmscng/object.h" #include <cwchar> #include "absl/strings/str_format.h" #include "common/kms_client.h" #include "common/kms_v1.h" #include "common/status_macros.h" #include "kmscng/algorithm_details.h" #include "kmscng/cng_headers.h" #include "kmscng/provider.h" #include "kmscng/util/errors.h" #include "kmscng/util/status_utils.h" #include "kmscng/util/string_utils.h" #include "kmscng/version.h" #include "kmsp11/util/crypto_utils.h" namespace cloud_kms::kmscng { namespace { // TODO(b/270419822): drop these once crypto_utils has been migrated to common. using cloud_kms::kmsp11::MarshalX509PublicKeyDer; using cloud_kms::kmsp11::ParseX509PublicKeyPem; absl::StatusOr<kms_v1::PublicKey> GetPublicKey(const KmsClient& client, std::string key_name) { kms_v1::GetPublicKeyRequest pub_req; pub_req.set_name(key_name); auto pub_resp = client.GetPublicKey(pub_req); if (!pub_resp.ok()) { // Populate status with more descriptive SECURITY_STATUS if not found. absl::Status resp_status = pub_resp.status(); if (pub_resp.status().code() == absl::StatusCode::kNotFound) { SetErrorSs(resp_status, NTE_BAD_KEYSET); } return resp_status; } if (pub_resp->protection_level() != kms_v1::ProtectionLevel::HSM) { return NewError( absl::StatusCode::kFailedPrecondition, "the key is not loadable due to unsupported protection level", NTE_NOT_SUPPORTED, SOURCE_LOCATION); } return *pub_resp; } absl::flat_hash_map<std::wstring, std::string> BuildInfo( NCRYPT_PROV_HANDLE prov_handle, std::string key_name, AlgorithmDetails details) { return { {NCRYPT_ALGORITHM_GROUP_PROPERTY, WideToBytes(details.algorithm_group)}, {NCRYPT_ALGORITHM_PROPERTY, WideToBytes(details.algorithm_property)}, {NCRYPT_KEY_USAGE_PROPERTY, Uint32ToBytes(details.key_usage)}, {NCRYPT_NAME_PROPERTY, key_name}, {NCRYPT_PROVIDER_HANDLE_PROPERTY, std::string(reinterpret_cast<char*>(&prov_handle), sizeof(NCRYPT_PROV_HANDLE))}, }; } } // namespace absl::StatusOr<Object*> ValidateKeyHandle(NCRYPT_PROV_HANDLE prov_handle, NCRYPT_KEY_HANDLE key_handle) { if (prov_handle == 0) { return NewInvalidArgumentError("The provider handle cannot be null", NTE_INVALID_HANDLE, SOURCE_LOCATION); } if (key_handle == 0) { return NewInvalidArgumentError("The key handle cannot be null", NTE_INVALID_HANDLE, SOURCE_LOCATION); } Object* object = reinterpret_cast<Object*>(key_handle); absl::StatusOr<std::string_view> stored_prov_handle = object->GetProperty(NCRYPT_PROVIDER_HANDLE_PROPERTY); // If FreeKey() is called after FreeProvider(), silently ignore the error and // return nullptr, which is handled appropriately by FreeKey(). // TODO(b/288298206): figure out why this can happen during the SignTool flow. if (!stored_prov_handle.status().ok() && stored_prov_handle.status().code() == absl::StatusCode::kNotFound) { return nullptr; } if (*stored_prov_handle != ProvHandleToBytes(prov_handle)) { return NewInvalidArgumentError( "The key handle does not match the provider handle", NTE_INVALID_HANDLE, SOURCE_LOCATION); } return object; } Object::Object(std::string kms_key_name, std::unique_ptr<KmsClient> client, kms_v1::CryptoKeyVersion::CryptoKeyVersionAlgorithm algorithm, bssl::UniquePtr<EVP_PKEY> public_key, absl::flat_hash_map<std::wstring, std::string> info) : kms_key_name_(kms_key_name), kms_client_(std::move(client)), algorithm_(algorithm), public_key_(std::move(public_key)), key_info_(info) {} absl::StatusOr<Object*> Object::New(NCRYPT_PROV_HANDLE prov_handle, std::string key_name) { ASSIGN_OR_RETURN(std::unique_ptr<KmsClient> client, NewKmsClient(prov_handle)); ASSIGN_OR_RETURN(auto public_key, GetPublicKey(*client, key_name)); absl::StatusOr<bssl::UniquePtr<EVP_PKEY>> pub = ParseX509PublicKeyPem(public_key.pem()); if (!pub.ok()) { absl::Status result = pub.status(); SetErrorSs(result, NTE_INTERNAL_ERROR); return result; } ASSIGN_OR_RETURN(AlgorithmDetails alg_details, GetDetails(public_key.algorithm())); auto info = BuildInfo(prov_handle, key_name, alg_details); // using `new` to invoke a private constructor return new Object(key_name, std::move(client), public_key.algorithm(), *std::move(pub), info); } absl::StatusOr<std::string_view> Object::GetProperty(std::wstring_view name) { auto it = key_info_.find(name); if (it == key_info_.end()) { return NewError(absl::StatusCode::kNotFound, absl::StrFormat("unsupported property specified: %s", WideToString(name.data())), NTE_NOT_SUPPORTED, SOURCE_LOCATION); } return it->second; } } // namespace cloud_kms::kmscng