kmsp11/token.cc (119 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/token.h"
#include <string_view>
#include "absl/container/flat_hash_set.h"
#include "absl/status/statusor.h"
#include "common/kms_client.h"
#include "common/status_macros.h"
#include "kmsp11/object_loader.h"
#include "kmsp11/object_store_state.pb.h"
#include "kmsp11/util/errors.h"
#include "kmsp11/util/string_utils.h"
namespace cloud_kms::kmsp11 {
namespace {
absl::StatusOr<CK_SLOT_INFO> NewSlotInfo() {
CK_SLOT_INFO info = {
{0}, // slotDescription (set with ' ' padding below)
{0}, // manufacturerID (set with ' ' padding below)
CKF_TOKEN_PRESENT, // flags
{0, 0}, // hardwareVersion
{0, 0}, // firmwareVersion
};
RETURN_IF_ERROR(
CryptokiStrCopy("A virtual slot mapped to a key ring in Google Cloud KMS",
info.slotDescription));
RETURN_IF_ERROR(CryptokiStrCopy("Google", info.manufacturerID));
return info;
}
absl::StatusOr<CK_TOKEN_INFO> NewTokenInfo(std::string_view token_label) {
CK_TOKEN_INFO info = {
{0}, // label (set with ' ' padding below)
{0}, // manufacturerID (set with ' ' padding below)
{0}, // model (set with ' ' padding below)
{0}, // serialNumber (set below)
CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED | CKF_SO_PIN_LOCKED |
CKF_RNG, // flags
CK_EFFECTIVELY_INFINITE, // ulMaxSessionCount
CK_UNAVAILABLE_INFORMATION, // ulSessionCount
CK_EFFECTIVELY_INFINITE, // ulMaxRwSessionCount
CK_UNAVAILABLE_INFORMATION, // ulRwSessionCount
0, // ulMaxPinLen
0, // ulMinPinLen
CK_UNAVAILABLE_INFORMATION, // ulTotalPublicMemory
CK_UNAVAILABLE_INFORMATION, // ulFreePublicMemory
CK_UNAVAILABLE_INFORMATION, // ulTotalPrivateMemory
CK_UNAVAILABLE_INFORMATION, // ulFreePrivateMemory
{0, 0}, // hardwareVersion
{0, 0}, // firmwareVersion
{0} // utcTime (set below)
};
RETURN_IF_ERROR(CryptokiStrCopy(token_label, info.label));
RETURN_IF_ERROR(CryptokiStrCopy("Google", info.manufacturerID));
RETURN_IF_ERROR(CryptokiStrCopy("Cloud KMS Token", info.model));
RETURN_IF_ERROR(CryptokiStrCopy("", info.serialNumber, '0'));
RETURN_IF_ERROR(CryptokiStrCopy("", info.utcTime, '0'));
return info;
}
} // namespace
absl::StatusOr<std::unique_ptr<Token>> Token::New(CK_SLOT_ID slot_id,
TokenConfig token_config,
KmsClient* kms_client,
bool generate_certs,
bool allow_software_keys) {
ASSIGN_OR_RETURN(CK_SLOT_INFO slot_info, NewSlotInfo());
ASSIGN_OR_RETURN(CK_TOKEN_INFO token_info,
NewTokenInfo(token_config.label()));
ASSIGN_OR_RETURN(
std::unique_ptr<ObjectLoader> loader,
ObjectLoader::New(token_config.key_ring(),
token_config.certs(), generate_certs, allow_software_keys));
ASSIGN_OR_RETURN(ObjectStoreState state, loader->BuildState(*kms_client));
ASSIGN_OR_RETURN(std::unique_ptr<ObjectStore> store, ObjectStore::New(state));
// using `new` to invoke a private constructor
return std::unique_ptr<Token>(new Token(slot_id, slot_info, token_info,
std::move(loader), std::move(store)));
}
bool Token::is_logged_in() const {
absl::ReaderMutexLock l(&login_mutex_);
return is_logged_in_;
}
absl::Status Token::Login(CK_USER_TYPE user) {
switch (user) {
case CKU_USER:
break;
case CKU_SO:
return NewError(absl::StatusCode::kPermissionDenied,
"login as CKU_SO is not permitted", CKR_PIN_LOCKED,
SOURCE_LOCATION);
case CKU_CONTEXT_SPECIFIC:
// See description of CKA_ALWAYS_AUTHENTICATE at
// http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html#_Toc322855286
return NewError(absl::StatusCode::kPermissionDenied,
"CKA_ALWAYS_AUTHENTICATE is not true on this token",
CKR_OPERATION_NOT_INITIALIZED, SOURCE_LOCATION);
default:
return NewInvalidArgumentError(
absl::StrFormat("unknown user type: %#x", user),
CKR_USER_TYPE_INVALID, SOURCE_LOCATION);
}
absl::WriterMutexLock l(&login_mutex_);
if (is_logged_in_) {
return FailedPreconditionError("user is already logged in",
CKR_USER_ALREADY_LOGGED_IN, SOURCE_LOCATION);
}
is_logged_in_ = true;
return absl::OkStatus();
}
absl::Status Token::Logout() {
absl::WriterMutexLock l(&login_mutex_);
if (!is_logged_in_) {
return FailedPreconditionError("user is not logged in",
CKR_USER_NOT_LOGGED_IN, SOURCE_LOCATION);
}
is_logged_in_ = false;
return absl::OkStatus();
}
absl::Status Token::RefreshState(const KmsClient& client) {
ASSIGN_OR_RETURN(ObjectStoreState state, object_loader_->BuildState(client));
ASSIGN_OR_RETURN(std::unique_ptr<ObjectStore> store, ObjectStore::New(state));
absl::WriterMutexLock lock(&objects_mutex_);
objects_.swap(store);
return absl::OkStatus();
}
} // namespace cloud_kms::kmsp11