kmsp11/object_store.cc (145 lines of code) (raw):

// Copyright 2021 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 "kmsp11/object_store.h" #include "common/status_macros.h" #include "kmsp11/util/crypto_utils.h" #include "kmsp11/util/errors.h" namespace cloud_kms::kmsp11 { namespace { // Cryptoki defines CK_OBJECT_HANDLE (and CK_ULONG) as simply `unsigned long`, // whose size is platform-dependent. Ensure that proto's uint64 is large enough // to hold such a value on any platform we're compiling on. static_assert( sizeof(CK_OBJECT_HANDLE) <= sizeof(google::protobuf::uint64), "object handles must fit in proto uint64 for proto compatibility"); using ObjectStoreEntry = ObjectStoreMap::value_type; absl::StatusOr<std::vector<ObjectStoreEntry>> ParseStoreEntries( const ObjectStoreState& state) { std::vector<ObjectStoreEntry> entries; for (const Key& item : state.keys()) { if (item.secret_key_handle() == 0 && item.private_key_handle() == 0) { return absl::InvalidArgumentError( "both secret_key_handle and private_key_handle are unset, cannot " "determine if key is symmetric or asymmetric"); } if (item.secret_key_handle() != 0) { ASSIGN_OR_RETURN(Object key, Object::NewSecretKey(item.crypto_key_version())); entries.emplace_back(item.secret_key_handle(), std::make_shared<Object>(std::move(key))); continue; } ASSIGN_OR_RETURN(bssl::UniquePtr<EVP_PKEY> public_key, ParseX509PublicKeyDer(item.public_key_der())); ASSIGN_OR_RETURN( KeyPair keypair, Object::NewKeyPair(item.crypto_key_version(), public_key.get())); if (item.public_key_handle() == 0) { return absl::InvalidArgumentError("public_key_handle is unset"); } entries.emplace_back( item.public_key_handle(), std::make_shared<Object>(std::move(keypair.public_key))); if (item.private_key_handle() == 0) { return absl::InvalidArgumentError("private_key_handle is unset"); } entries.emplace_back( item.private_key_handle(), std::make_shared<Object>(std::move(keypair.private_key))); if (item.has_certificate()) { if (item.certificate().handle() == 0) { return absl::InvalidArgumentError("certificate_handle is unset"); } ASSIGN_OR_RETURN( bssl::UniquePtr<X509> x509, ParseX509CertificateDer(item.certificate().x509_der())); ASSIGN_OR_RETURN( Object cert, Object::NewCertificate(item.crypto_key_version(), x509.get())); entries.emplace_back(item.certificate().handle(), std::make_shared<Object>(std::move(cert))); } } return entries; } // A comparison function for ObjectStore entries that sorts by KMS key name, // followed by object class. bool EntryCompare(const ObjectStoreEntry& e1, const ObjectStoreEntry& e2) { int name_cmp = e1.second->kms_key_name().compare(e2.second->kms_key_name()); if (name_cmp == 0) { return e1.second->object_class() < e2.second->object_class(); } return name_cmp < 0; } } // namespace absl::StatusOr<std::unique_ptr<ObjectStore>> ObjectStore::New( const ObjectStoreState& state) { absl::StatusOr<std::vector<ObjectStoreEntry>> entries = ParseStoreEntries(state); if (!entries.ok()) { return NewInvalidArgumentError( absl::StrCat("failure building ObjectStore: ", entries.status().message()), CKR_DEVICE_ERROR, SOURCE_LOCATION); } std::unique_ptr<ObjectStore> store = absl::WrapUnique( new ObjectStore(ObjectStoreMap(entries->begin(), entries->end()))); if (store->entries_.size() != entries->size()) { return NewInvalidArgumentError( absl::StrFormat("duplicate handle detected: " "store.entries_.size()=%d; entries.size()=%d", store->entries_.size(), entries->size()), CKR_DEVICE_ERROR, SOURCE_LOCATION); } return std::move(store); } absl::StatusOr<std::shared_ptr<Object>> ObjectStore::GetObject( CK_OBJECT_HANDLE handle) const { ObjectStoreMap::const_iterator it = entries_.find(handle); if (it == entries_.end()) { return HandleNotFoundError(handle, CKR_OBJECT_HANDLE_INVALID, SOURCE_LOCATION); } return it->second; } absl::StatusOr<std::shared_ptr<Object>> ObjectStore::GetKey( CK_OBJECT_HANDLE handle) const { ObjectStoreMap::const_iterator it = entries_.find(handle); if (it == entries_.end()) { return HandleNotFoundError(handle, CKR_KEY_HANDLE_INVALID, SOURCE_LOCATION); } switch (it->second->object_class()) { case CKO_PRIVATE_KEY: case CKO_PUBLIC_KEY: case CKO_SECRET_KEY: return it->second; default: return HandleNotFoundError(handle, CKR_KEY_HANDLE_INVALID, SOURCE_LOCATION); } } std::vector<CK_OBJECT_HANDLE> ObjectStore::Find( std::function<bool(const Object&)> predicate) const { std::vector<std::reference_wrapper<const ObjectStoreEntry>> matches; for (const ObjectStoreEntry& entry : entries_) { if (predicate(*entry.second)) { matches.push_back(entry); } } std::sort(matches.begin(), matches.end(), &EntryCompare); std::vector<CK_OBJECT_HANDLE> handles(matches.size()); for (size_t i = 0; i < matches.size(); i++) { handles[i] = matches[i].get().first; } return handles; } absl::StatusOr<CK_OBJECT_HANDLE> ObjectStore::FindSingle( std::function<bool(const Object&)> predicate) const { std::optional<CK_OBJECT_HANDLE> match; for (const auto& [handle, object] : entries_) { if (predicate(*object)) { if (match.has_value()) { return absl::FailedPreconditionError("multiple matches found"); } match = handle; } } if (!match.has_value()) { return absl::NotFoundError("no match found"); } return *match; } } // namespace cloud_kms::kmsp11