kmsp11/provider.cc (130 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/provider.h" #include "common/kms_client.h" #include "common/status_macros.h" #include "glog/logging.h" #include "kmsp11/cert_authority.h" #include "kmsp11/mechanism.h" #include "kmsp11/util/string_utils.h" #include "kmsp11/version.h" namespace cloud_kms::kmsp11 { namespace { static const char* kDefaultKmsEndpoint = "cloudkms.googleapis.com:443"; constexpr absl::Duration kDefaultRpcTimeout = absl::Seconds(30); absl::StatusOr<CK_INFO> NewCkInfo() { CK_INFO info = { {CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR}, // cryptokiVersion {0}, // manufacturerID (set with ' ' padding below) 0, // flags {0}, // libraryDescription (set with ' ' padding below) kLibraryVersion, // libraryVersion }; RETURN_IF_ERROR(CryptokiStrCopy("Google", info.manufacturerID)); RETURN_IF_ERROR(CryptokiStrCopy("Cryptoki Library for Cloud KMS", info.libraryDescription)); return info; } absl::StatusOr<std::unique_ptr<KmsClient>> NewKmsClient( const LibraryConfig& config) { KmsClient::Options options; options.endpoint_address = config.kms_endpoint().empty() ? kDefaultKmsEndpoint : config.kms_endpoint(); options.creds = config.use_insecure_grpc_channel_credentials() ? grpc::InsecureChannelCredentials() : grpc::GoogleDefaultCredentials(); if (!options.creds) { return NewError(absl::StatusCode::kInvalidArgument, "Invalid Application Default Credentials. See " "https://cloud.google.com/docs/authentication/" "external/about-adc", CKR_ARGUMENTS_BAD, SOURCE_LOCATION); } options.rpc_timeout = config.rpc_timeout_secs() == 0 ? kDefaultRpcTimeout : absl::Seconds(config.rpc_timeout_secs()); options.version_major = kLibraryVersion.major; options.version_minor = kLibraryVersion.minor; options.error_decorator = [](absl::Status& status) { SetErrorRv(status, CKR_DEVICE_ERROR); }; options.rpc_feature_flags = config.experimental_rpc_feature_flags(); options.user_project_override = config.user_project_override(); return std::make_unique<KmsClient>(options); } } // namespace absl::StatusOr<std::unique_ptr<Provider>> Provider::New(LibraryConfig config) { ASSIGN_OR_RETURN(CK_INFO info, NewCkInfo()); ASSIGN_OR_RETURN(std::unique_ptr<KmsClient> client, NewKmsClient(config)); std::vector<std::unique_ptr<Token>> tokens; tokens.reserve(config.tokens_size()); for (const TokenConfig& tokenConfig : config.tokens()) { ASSIGN_OR_RETURN(std::unique_ptr<Token> token, Token::New(tokens.size(), tokenConfig, client.get(), config.generate_certs(), config.allow_software_keys())); tokens.emplace_back(std::move(token)); } // using `new` to invoke a private constructor return std::unique_ptr<Provider>( new Provider(config, info, std::move(tokens), std::move(client), absl::Seconds(config.refresh_interval_secs()))); } absl::StatusOr<Token*> Provider::TokenAt(CK_SLOT_ID slot_id) { if (slot_id >= tokens_.size()) { return NewError(absl::StatusCode::kNotFound, absl::StrFormat("slot with ID %d does not exist", slot_id), CKR_SLOT_ID_INVALID, SOURCE_LOCATION); } return tokens_[slot_id].get(); } absl::StatusOr<CK_SESSION_HANDLE> Provider::OpenSession( CK_SLOT_ID slot_id, SessionType session_type) { ASSIGN_OR_RETURN(Token * token, TokenAt(slot_id)); return sessions_.Add(token, session_type, kms_client_.get()); } absl::StatusOr<std::shared_ptr<Session>> Provider::GetSession( CK_SESSION_HANDLE session_handle) { return sessions_.Get(session_handle); } absl::Status Provider::CloseSession(CK_SESSION_HANDLE session_handle) { return sessions_.Remove(session_handle); } absl::Status Provider::CloseAllSessions(CK_SLOT_ID slot_id) { RETURN_IF_ERROR(TokenAt(slot_id)); sessions_.RemoveIf( [&](const Session& s) { return s.token()->slot_id() == slot_id; }); return absl::OkStatus(); } Provider::Refresher::Refresher(Provider* provider, absl::Duration interval) : thread_( [](Provider* provider, const absl::Duration interval, const absl::Notification* shutdown) { while (!shutdown->WaitForNotificationWithTimeout(interval)) { for (const std::unique_ptr<Token>& token : provider->tokens_) { absl::Status refresh_result = token->RefreshState(*provider->kms_client_); if (!refresh_result.ok()) { LOG(ERROR) << "error refreshing state for key ring " << token->key_ring_name() << ": " << refresh_result; } } } }, provider, interval, &shutdown_) {} Provider::Refresher::~Refresher() { shutdown_.Notify(); thread_.join(); } absl::Span<const CK_MECHANISM_TYPE> Provider::Mechanisms() { return mechanism_types_; } absl::StatusOr<CK_MECHANISM_INFO> Provider::MechanismInfo( CK_MECHANISM_TYPE type) { const auto& entry = AllMechanisms().find(type); if (entry == AllMechanisms().end()) { return NewError(absl::StatusCode::kNotFound, absl::StrFormat("mechanism %#x not found", type), CKR_MECHANISM_INVALID, SOURCE_LOCATION); } return entry->second; } } // namespace cloud_kms::kmsp11