blob: 10411f488738ecb71f92e65acbc6793ddde10807 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/kcer/kcer_token_impl.h"
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/task/thread_pool.h"
#include "chromeos/ash/components/kcer/attributes.pb.h"
#include "chromeos/ash/components/kcer/chaps/high_level_chaps_client.h"
#include "chromeos/ash/components/kcer/chaps/session_chaps_client.h"
#include "chromeos/ash/components/kcer/helpers/key_helper.h"
#include "chromeos/ash/components/kcer/helpers/pkcs12_reader.h"
#include "chromeos/ash/components/kcer/helpers/pkcs12_validator.h"
#include "chromeos/ash/components/kcer/kcer_token_utils.h"
#include "chromeos/ash/components/kcer/kcer_utils.h"
#include "chromeos/constants/pkcs11_definitions.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/keypair.h"
#include "crypto/openssl_util.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_database.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/ec.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
#include "third_party/boringssl/src/include/openssl/x509.h"
#include "third_party/cros_system_api/dbus/chaps/dbus-constants.h"
using AttributeId = kcer::HighLevelChapsClient::AttributeId;
// Needed for bssl::UniquePtr<PKCS8_PRIV_KEY_INFO>.
BSSL_NAMESPACE_BEGIN
BORINGSSL_MAKE_DELETER(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free)
BSSL_NAMESPACE_END
namespace kcer::internal {
namespace {
std::string_view AsString(base::span<const uint8_t> bytes) {
return std::string_view(reinterpret_cast<const char*>(bytes.data()),
bytes.size());
}
const chaps::Attribute* GetAttribute(const chaps::AttributeList& attr_list,
AttributeId attribute_id) {
for (int i = 0; i < attr_list.attributes_size(); i++) {
if (attr_list.attributes(i).type() == static_cast<uint32_t>(attribute_id)) {
return &(attr_list.attributes(i));
}
}
return nullptr;
}
base::span<const uint8_t> GetAttributeValue(
const chaps::AttributeList& attr_list,
AttributeId attribute_id) {
const chaps::Attribute* attr = GetAttribute(attr_list, attribute_id);
if (!attr || !attr->has_value()) {
return {};
}
return base::as_byte_span(attr->value());
}
bool GetIsHardwareBacked(const chaps::Attribute* attr,
bool& is_hardware_backed) {
if (!attr || !attr->has_value()) {
return false;
}
if (attr->value().size() != sizeof(chromeos::PKCS11_CK_BBOOL)) {
return false;
}
chromeos::PKCS11_CK_BBOOL key_in_software =
*reinterpret_cast<const chromeos::PKCS11_CK_BBOOL*>(attr->value().data());
is_hardware_backed = (key_in_software == chromeos::PKCS11_CK_FALSE);
return true;
}
bool GetKeyType(const chaps::Attribute* attr, KeyType& key_type) {
if (!attr || !attr->has_value()) {
return false;
}
if (attr->value().size() != sizeof(chromeos::PKCS11_CK_KEY_TYPE)) {
return false;
}
chromeos::PKCS11_CK_KEY_TYPE pkcs_key_type =
*UNSAFE_TODO(reinterpret_cast<const chromeos::PKCS11_CK_KEY_TYPE*>(
attr->value().data()));
if (pkcs_key_type == chromeos::PKCS11_CKK_RSA) {
key_type = KeyType::kRsa;
return true;
} else if (pkcs_key_type == chromeos::PKCS11_CKK_EC) {
key_type = KeyType::kEcc;
return true;
}
return false;
}
bool GetOptionalString(const chaps::Attribute* attr,
std::optional<std::string>& result_string) {
if (!attr || !attr->has_value() || !attr->has_length()) {
return false;
}
if (static_cast<uint32_t>(attr->length()) ==
chromeos::PKCS11_CK_UNAVAILABLE_INFORMATION) {
// The attribute was not set for the object.
result_string.reset();
return true;
}
result_string = attr->value();
return true;
}
// Chaps wraps the EC point in a DER-encoded ASN.1 OctetString, which is
// required by PKCS#11 standard, but it needs to be removed before using it for
// boringssl.
bssl::UniquePtr<ASN1_OCTET_STRING> UnwrapEcPoint(
base::span<const uint8_t> ec_point) {
if (ec_point.empty()) {
return {};
}
const uint8_t* data = ec_point.data();
bssl::UniquePtr<ASN1_OCTET_STRING> result(
d2i_ASN1_OCTET_STRING(nullptr, &data, ec_point.size()));
return result;
}
// Calculates PKCS#11 id for the provided public key SPKI.
Pkcs11Id GetPkcs11IdFromSpki(const PublicKeySpki& public_key_spki) {
if (public_key_spki->empty()) {
LOG(ERROR) << "Empty public key provided";
return {};
}
std::optional<crypto::keypair::PublicKey> key =
crypto::keypair::PublicKey::FromSubjectPublicKeyInfo(*public_key_spki);
if (!key.has_value()) {
LOG(ERROR) << "Could not parse public key";
return {};
}
if (key->IsRsa()) {
// Backwards compatible with how NSS generated CKA_ID for RSA keys.
return MakePkcs11Id(key->GetRsaModulus());
}
if (key->IsEc()) {
// Backwards compatible with how NSS generated CKA_ID for EC keys.
return MakePkcs11Id(key->ToUncompressedForm());
}
return {};
}
// Returns true if the `key` already had PKCS#11 id or it was successfully set.
// Returns false if the `key` still doesn't have the id after the method
// finishes.
bool EnsurePkcs11IdIsSet(PrivateKeyHandle& key) {
if (!key.GetPkcs11IdInternal()->empty()) {
return true;
}
key.SetPkcs11IdInternal(GetPkcs11IdFromSpki(key.GetSpkiInternal()));
return !key.GetPkcs11IdInternal()->empty();
}
uint64_t SigningSchemeToPkcs11Mechanism(SigningScheme scheme) {
switch (scheme) {
case SigningScheme::kRsaPkcs1Sha1:
case SigningScheme::kRsaPkcs1Sha256:
case SigningScheme::kRsaPkcs1Sha384:
case SigningScheme::kRsaPkcs1Sha512:
return chromeos::PKCS11_CKM_RSA_PKCS;
case SigningScheme::kEcdsaSecp256r1Sha256:
case SigningScheme::kEcdsaSecp384r1Sha384:
case SigningScheme::kEcdsaSecp521r1Sha512:
return chromeos::PKCS11_CKM_ECDSA;
case SigningScheme::kRsaPssRsaeSha256:
case SigningScheme::kRsaPssRsaeSha384:
case SigningScheme::kRsaPssRsaeSha512:
return chromeos::PKCS11_CKM_RSA_PKCS_PSS;
}
}
void RunClosure(base::OnceClosure closure, uint32_t /*result_code*/) {
std::move(closure).Run();
}
// A helper method for error handling. When some method fails and should return
// the `error` through the `callback`, but also should clean up something first,
// this helper allows to bind the error to the callback and create a new
// callback for the clean up code.
template <typename T>
base::OnceCallback<void(uint32_t)> Bind(
base::OnceCallback<void(base::expected<T, Error>)> callback,
Error error) {
return base::BindOnce(&RunClosure, base::BindOnce(std::move(callback),
base::unexpected(error)));
}
// Creates a digest for `data_to_sign` with the correct prefix (if needed) for
// `kcer_signing_scheme`.
base::expected<DigestWithPrefix, Error> DigestOnWorkerThread(
SigningScheme kcer_signing_scheme,
DataToSign data_to_sign) {
// SigningScheme is defined in a way where this cast is meaningful.
uint16_t ssl_algorithm = static_cast<uint16_t>(kcer_signing_scheme);
const EVP_MD* digest_method =
SSL_get_signature_algorithm_digest(ssl_algorithm);
uint8_t digest_buffer[EVP_MAX_MD_SIZE];
uint8_t* digest = digest_buffer;
unsigned int digest_len = 0;
if (!digest_method ||
!EVP_Digest(data_to_sign->data(), data_to_sign->size(), digest,
&digest_len, digest_method, nullptr)) {
return base::unexpected(Error::kFailedToSignFailedToDigest);
}
bssl::UniquePtr<uint8_t> free_digest_info;
if ((SSL_get_signature_algorithm_key_type(ssl_algorithm) == EVP_PKEY_RSA) &&
!SSL_is_signature_algorithm_rsa_pss(ssl_algorithm)) {
// PKCS#11 Sign expects the caller to prepend the DigestInfo for PKCS #1.
int hash_nid =
EVP_MD_type(SSL_get_signature_algorithm_digest(ssl_algorithm));
int is_alloced = 0;
size_t digest_with_prefix_len = 0;
if (!RSA_add_pkcs1_prefix(&digest, &digest_with_prefix_len, &is_alloced,
hash_nid, digest, digest_len)) {
return base::unexpected(Error::kFailedToSignFailedToAddPrefix);
}
digest_len = digest_with_prefix_len;
if (is_alloced) {
free_digest_info.reset(digest);
}
}
return DigestWithPrefix(
std::vector<uint8_t>(digest, UNSAFE_TODO(digest + digest_len)));
}
std::vector<uint8_t> GetPssSignParams(SigningScheme kcer_signing_scheme) {
chromeos::PKCS11_CK_RSA_PKCS_PSS_PARAMS pss_params;
uint16_t ssl_algorithm = static_cast<uint16_t>(kcer_signing_scheme);
CHECK(SSL_is_signature_algorithm_rsa_pss(ssl_algorithm));
const EVP_MD* digest_method =
SSL_get_signature_algorithm_digest(ssl_algorithm);
switch (EVP_MD_type(digest_method)) {
case NID_sha256:
pss_params.hashAlg = CKM_SHA256;
pss_params.mgf = CKG_MGF1_SHA256;
break;
case NID_sha384:
pss_params.hashAlg = CKM_SHA384;
pss_params.mgf = CKG_MGF1_SHA384;
break;
case NID_sha512:
pss_params.hashAlg = CKM_SHA512;
pss_params.mgf = CKG_MGF1_SHA512;
break;
default:
return {};
}
// Use the hash length for the salt length.
pss_params.sLen = EVP_MD_size(digest_method);
const uint8_t* params_ptr = reinterpret_cast<const uint8_t*>(&pss_params);
return std::vector<uint8_t>(params_ptr,
UNSAFE_TODO(params_ptr + sizeof(pss_params)));
}
} // namespace
KcerTokenImpl::KcerTokenImpl(Token token, HighLevelChapsClient* chaps_client)
: token_(token),
pkcs_11_slot_id_(0),
chaps_client_(chaps_client),
kcer_utils_(token, chaps_client) {
CHECK(chaps_client_);
}
KcerTokenImpl::~KcerTokenImpl() {
net::CertDatabase::GetInstance()->RemoveObserver(this);
}
// Returns a weak pointer for the token. The pointer can be used to post tasks
// for the token.
base::WeakPtr<KcerToken> KcerTokenImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// Initializes the token with the provided NSS slot. If `nss_slot` is nullptr,
// the initialization is considered failed and the token will return an error
// for all queued and future requests.
void KcerTokenImpl::InitializeWithoutNss(
SessionChapsClient::SlotId pkcs11_slot_id) {
pkcs_11_slot_id_ = pkcs11_slot_id;
kcer_utils_.Initialize(pkcs_11_slot_id_);
net::CertDatabase::GetInstance()->AddObserver(this);
// This is supposed to be the first time the task queue is unblocked, no
// other tasks should be already running.
UnblockQueueProcessNextTask();
}
void KcerTokenImpl::OnClientCertStoreChanged() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
cache_state_ = CacheState::kOutdated;
// If task queue is not blocked, trigger cache update immediately.
if (!is_blocked_) {
UnblockQueueProcessNextTask();
}
}
//==============================================================================
KcerTokenImpl::GenerateRsaKeyTask::GenerateRsaKeyTask(
RsaModulusLength in_modulus_length_bits,
bool in_hardware_backed,
Kcer::GenerateKeyCallback in_callback)
: modulus_length_bits(in_modulus_length_bits),
hardware_backed(in_hardware_backed),
callback(std::move(in_callback)) {}
KcerTokenImpl::GenerateRsaKeyTask::GenerateRsaKeyTask(
GenerateRsaKeyTask&& other) = default;
KcerTokenImpl::GenerateRsaKeyTask::~GenerateRsaKeyTask() = default;
void KcerTokenImpl::GenerateRsaKey(RsaModulusLength modulus_length_bits,
bool hardware_backed,
Kcer::GenerateKeyCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::GenerateRsaKey, weak_factory_.GetWeakPtr(),
modulus_length_bits, hardware_backed, std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
GenerateRsaKeyImpl(GenerateRsaKeyTask(modulus_length_bits, hardware_backed,
std::move(unblocking_callback)));
}
// Generates a new key pair.
void KcerTokenImpl::GenerateRsaKeyImpl(GenerateRsaKeyTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
chromeos::PKCS11_CK_ULONG modulus_bits =
static_cast<uint32_t>(task.modulus_length_bits);
chromeos::PKCS11_CK_BYTE public_exponent[3] = {0x01, 0x00, 0x01}; // 65537
chaps::AttributeList public_key_attrs;
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_ENCRYPT,
MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY, MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_WRAP, MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_MODULUS_BITS,
MakeSpan(&modulus_bits));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
base::as_byte_span(public_exponent));
chaps::AttributeList private_key_attrs;
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN, MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_DECRYPT,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN, MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_UNWRAP,
MakeSpan(&kTrue));
if (!task.hardware_backed) {
AddAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue));
}
auto chaps_callback =
base::BindOnce(&KcerTokenImpl::DidGenerateRsaKey,
weak_factory_.GetWeakPtr(), std::move(task));
chaps_client_->GenerateKeyPair(
pkcs_11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS_KEY_PAIR_GEN,
/*mechanism_parameter=*/{}, std::move(public_key_attrs),
std::move(private_key_attrs), std::move(chaps_callback));
}
// Fetches the public key attributes of the generated key.
void KcerTokenImpl::DidGenerateRsaKey(GenerateRsaKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateRsaKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToGenerateKey));
}
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, public_key_id,
{AttributeId::kModulus, AttributeId::kPublicExponent},
base::BindOnce(&KcerTokenImpl::DidGetRsaPublicKey,
weak_factory_.GetWeakPtr(), std::move(task), public_key_id,
private_key_id));
}
// Computes PKCS#11 for the key and sets it.
void KcerTokenImpl::DidGetRsaPublicKey(
GenerateRsaKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
chaps::AttributeList public_key_attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateRsaKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), Error::kFailedToExportPublicKey));
}
base::span<const uint8_t> modulus =
GetAttributeValue(public_key_attributes, AttributeId::kModulus);
base::span<const uint8_t> public_exponent =
GetAttributeValue(public_key_attributes, AttributeId::kPublicExponent);
base::expected<PublicKey, Error> kcer_public_key =
MakeRsaPublicKey(token_, modulus, public_exponent);
if (!kcer_public_key.has_value()) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), kcer_public_key.error()));
}
chaps::AttributeList attr_list;
AddAttribute(attr_list, chromeos::PKCS11_CKA_ID,
kcer_public_key->GetPkcs11Id().value());
auto chaps_callback =
base::BindOnce(&KcerTokenImpl::DidAssignRsaKeyId,
weak_factory_.GetWeakPtr(), std::move(task), public_key_id,
private_key_id, std::move(kcer_public_key).value());
chaps_client_->SetAttributeValue(pkcs_11_slot_id_,
{public_key_id, private_key_id}, attr_list,
std::move(chaps_callback));
}
void KcerTokenImpl::DidAssignRsaKeyId(GenerateRsaKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
PublicKey kcer_public_key,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateRsaKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), Error::kFailedToWriteAttribute));
}
return std::move(task.callback).Run(std::move(kcer_public_key));
}
//==============================================================================
KcerTokenImpl::GenerateEcKeyTask::GenerateEcKeyTask(
EllipticCurve in_curve,
bool in_hardware_backed,
Kcer::GenerateKeyCallback in_callback)
: curve(in_curve),
hardware_backed(in_hardware_backed),
callback(std::move(in_callback)) {}
KcerTokenImpl::GenerateEcKeyTask::GenerateEcKeyTask(GenerateEcKeyTask&& other) =
default;
KcerTokenImpl::GenerateEcKeyTask::~GenerateEcKeyTask() = default;
void KcerTokenImpl::GenerateEcKey(EllipticCurve curve,
bool hardware_backed,
Kcer::GenerateKeyCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::GenerateEcKey, weak_factory_.GetWeakPtr(), curve,
hardware_backed, std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
GenerateEcKeyImpl(GenerateEcKeyTask(curve, hardware_backed,
std::move(unblocking_callback)));
}
// Generates an EC key pair.
void KcerTokenImpl::GenerateEcKeyImpl(GenerateEcKeyTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
if (task.curve != EllipticCurve::kP256) {
return std::move(task.callback).Run(base::unexpected(Error::kBadKeyParams));
}
bssl::ScopedCBB cbb;
uint8_t* ec_params_der = nullptr;
size_t ec_params_der_len = 0;
if (!CBB_init(cbb.get(), 0) ||
!EC_KEY_marshal_curve_name(cbb.get(), EC_group_p256()) ||
!CBB_finish(cbb.get(), &ec_params_der, &ec_params_der_len)) {
return std::move(task.callback).Run(base::unexpected(Error::kBadKeyParams));
}
bssl::UniquePtr<uint8_t> der_deleter(ec_params_der);
chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
chaps::AttributeList public_key_attrs;
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_ENCRYPT,
MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY, MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_WRAP, MakeSpan(&kTrue));
AddAttribute(public_key_attrs, chromeos::PKCS11_CKA_EC_PARAMS,
UNSAFE_TODO(base::span(ec_params_der, ec_params_der_len)));
chaps::AttributeList private_key_attrs;
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN, MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_DECRYPT,
MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN, MakeSpan(&kTrue));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_UNWRAP,
MakeSpan(&kTrue));
if (!task.hardware_backed) {
AddAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue));
}
auto chaps_callback =
base::BindOnce(&KcerTokenImpl::DidGenerateEcKey,
weak_factory_.GetWeakPtr(), std::move(task));
chaps_client_->GenerateKeyPair(
pkcs_11_slot_id_, chromeos::PKCS11_CKM_EC_KEY_PAIR_GEN,
/*mechanism_parameter=*/{}, std::move(public_key_attrs),
std::move(private_key_attrs), std::move(chaps_callback));
}
// Fetches the public key attributes of the generated key.
void KcerTokenImpl::DidGenerateEcKey(GenerateEcKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateEcKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToGenerateKey));
}
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, public_key_id, {AttributeId::kEcPoint},
base::BindOnce(&KcerTokenImpl::DidGetEcPublicKey,
weak_factory_.GetWeakPtr(), std::move(task), public_key_id,
private_key_id));
}
// Computes PKCS#11 for the key and sets it.
void KcerTokenImpl::DidGetEcPublicKey(
GenerateEcKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
chaps::AttributeList public_key_attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateEcKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), Error::kFailedToExportPublicKey));
}
base::span<const uint8_t> wrapped_ec_point =
GetAttributeValue(public_key_attributes, AttributeId::kEcPoint);
bssl::UniquePtr<ASN1_OCTET_STRING> ec_point_oct =
UnwrapEcPoint(wrapped_ec_point);
if (!ec_point_oct) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), Error::kFailedToReadAttribute));
}
const uint8_t* ec_point_data = ASN1_STRING_data(ec_point_oct.get());
size_t ec_point_data_len = ASN1_STRING_length(ec_point_oct.get());
base::span<const uint8_t> ec_point =
UNSAFE_TODO(base::span(ec_point_data, ec_point_data_len));
base::expected<PublicKey, Error> kcer_public_key =
MakeEcPublicKey(token_, ec_point);
if (!kcer_public_key.has_value()) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), kcer_public_key.error()));
}
chaps::AttributeList attr_list;
AddAttribute(attr_list, chromeos::PKCS11_CKA_ID,
kcer_public_key->GetPkcs11Id().value());
auto chaps_callback =
base::BindOnce(&KcerTokenImpl::DidAssignEcKeyId,
weak_factory_.GetWeakPtr(), std::move(task), public_key_id,
private_key_id, std::move(kcer_public_key).value());
chaps_client_->SetAttributeValue(pkcs_11_slot_id_,
{public_key_id, private_key_id}, attr_list,
std::move(chaps_callback));
}
void KcerTokenImpl::DidAssignEcKeyId(GenerateEcKeyTask task,
ObjectHandle public_key_id,
ObjectHandle private_key_id,
PublicKey kcer_public_key,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GenerateEcKeyImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, {public_key_id, private_key_id},
Bind(std::move(task.callback), Error::kFailedToWriteAttribute));
}
return std::move(task.callback).Run(std::move(kcer_public_key));
}
//==============================================================================
void KcerTokenImpl::ImportKey(Pkcs8PrivateKeyInfoDer pkcs8_private_key_info_der,
Kcer::ImportKeyCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::ImportKey, weak_factory_.GetWeakPtr(),
std::move(pkcs8_private_key_info_der), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
const uint8_t* buffer = pkcs8_private_key_info_der->data();
bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> p8(d2i_PKCS8_PRIV_KEY_INFO(
nullptr, &buffer, pkcs8_private_key_info_der->size()));
if (!p8) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToParseKey));
}
KeyData key_data;
key_data.key = bssl::UniquePtr<EVP_PKEY>(EVP_PKCS82PKEY(p8.get()));
if (!key_data.key) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToParseKey));
}
Pkcs12Reader pkcs12_reader;
Pkcs12ReaderStatusCode enrich_key_data_result =
pkcs12_reader.EnrichKeyData(key_data);
if (enrich_key_data_result != Pkcs12ReaderStatusCode::kSuccess) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
kcer_utils_.ImportKey(KcerTokenUtils::ImportKeyTask(
std::move(key_data), /*in_hardware_backed=*/false,
/*in_mark_as_migrated=*/false, std::move(unblocking_callback)));
}
//==============================================================================
KcerTokenImpl::ImportCertFromBytesTask::ImportCertFromBytesTask(
CertDer in_cert_der,
Kcer::StatusCallback in_callback)
: cert_der(std::move(in_cert_der)), callback(std::move(in_callback)) {}
KcerTokenImpl::ImportCertFromBytesTask::ImportCertFromBytesTask(
ImportCertFromBytesTask&& other) = default;
KcerTokenImpl::ImportCertFromBytesTask::~ImportCertFromBytesTask() = default;
void KcerTokenImpl::ImportCertFromBytes(CertDer cert_der,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::ImportCertFromBytes, weak_factory_.GetWeakPtr(),
std::move(cert_der), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
ImportCertFromBytesImpl(ImportCertFromBytesTask(
std::move(cert_der), std::move(unblocking_callback)));
}
void KcerTokenImpl::ImportCertFromBytesImpl(ImportCertFromBytesTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_VALUE, task.cert_der.value());
// Check whether the cert is already imported.
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::ImportCertFromBytesWithExistingCerts,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Checks whether the private key for the cert exists.
void KcerTokenImpl::ImportCertFromBytesWithExistingCerts(
ImportCertFromBytesTask task,
std::vector<ObjectHandle> existing_certs,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ImportCertFromBytesImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
if (!existing_certs.empty()) {
// The cert already exists, no need to import, return success.
return std::move(task.callback).Run({});
}
std::string_view spki_string_piece;
if (!net::asn1::ExtractSPKIFromDERCert(AsString(task.cert_der.value()),
&spki_string_piece)) {
return std::move(task.callback)
.Run(base::unexpected(Error::kInvalidCertificate));
}
PublicKeySpki spki(std::vector<uint8_t>(
spki_string_piece.data(),
UNSAFE_TODO(spki_string_piece.data() + spki_string_piece.size())));
Pkcs11Id key_id = GetPkcs11IdFromSpki(spki);
if (key_id->empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kInvalidCertificate));
}
Pkcs11Id key_id_copy = key_id;
kcer_utils_.FindPrivateKey(
std::move(key_id),
base::BindOnce(&KcerTokenImpl::ImportCertFromBytesWithKeyHandle,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(key_id_copy)));
}
// Parses and imports the cert into Chaps.
void KcerTokenImpl::ImportCertFromBytesWithKeyHandle(
ImportCertFromBytesTask task,
Pkcs11Id pkcs11_id,
std::vector<ObjectHandle> key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ImportCertFromBytesImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
if (key_handles.empty()) {
return std::move(task.callback).Run(base::unexpected(Error::kKeyNotFound));
}
Pkcs12Reader reader;
bssl::UniquePtr<X509> cert;
Pkcs12ReaderStatusCode status = reader.GetCertFromDerData(
task.cert_der.value().data(), task.cert_der.value().size(), cert);
if (status != Pkcs12ReaderStatusCode::kSuccess) {
return std::move(task.callback)
.Run(base::unexpected(Error::kInvalidCertificate));
}
std::vector<scoped_refptr<const Cert>> existing_certs =
cert_cache_.GetAllCerts();
std::vector<std::string_view> existing_nicknames;
existing_nicknames.reserve(existing_certs.size());
for (const auto& existing_cert : existing_certs) {
existing_nicknames.push_back(
std::string_view(existing_cert->GetNickname()));
}
std::string label;
Pkcs12ReaderStatusCode get_nickname_result =
GetNickname(std::move(existing_certs), std::move(existing_nicknames),
cert.get(), reader, label);
if (get_nickname_result != Pkcs12ReaderStatusCode::kSuccess) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToMakeCertNickname));
}
CertDer cert_der = task.cert_der;
auto import_callback =
base::BindOnce(&KcerTokenImpl::DidImportCertFromBytes,
weak_factory_.GetWeakPtr(), std::move(task));
kcer_utils_.ImportCert(std::move(cert), std::move(pkcs11_id),
std::move(label), std::move(cert_der),
/*is_hardware_backed=*/true,
/*mark_as_migrated=*/false,
std::move(import_callback));
}
void KcerTokenImpl::DidImportCertFromBytes(ImportCertFromBytesTask task,
std::optional<Error> kcer_error,
ObjectHandle cert_handle,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (kcer_error.has_value()) {
return std::move(task.callback).Run(base::unexpected(kcer_error.value()));
}
if (SessionChapsClient::IsSessionError(result_code)) {
return ImportCertFromBytesImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToImportCertificate));
}
return std::move(task.callback).Run({});
}
//==============================================================================
void KcerTokenImpl::ImportPkcs12Cert(Pkcs12Blob pkcs12_blob,
std::string password,
bool hardware_backed,
bool mark_as_migrated,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::ImportPkcs12Cert, weak_factory_.GetWeakPtr(),
std::move(pkcs12_blob), std::move(password), hardware_backed,
mark_as_migrated, std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
Pkcs12Reader pkcs12_reader;
KeyData key_data;
bssl::UniquePtr<STACK_OF(X509)> certs;
Pkcs12ReaderStatusCode get_key_and_cert_status =
pkcs12_reader.GetPkcs12KeyAndCerts(pkcs12_blob.value(), password,
key_data.key, certs);
if (get_key_and_cert_status != Pkcs12ReaderStatusCode::kSuccess) {
return std::move(unblocking_callback)
.Run(base::unexpected(
ConvertPkcs12ParsingError(get_key_and_cert_status)));
}
Pkcs12ReaderStatusCode enrich_key_data_result =
pkcs12_reader.EnrichKeyData(key_data);
if ((enrich_key_data_result != Pkcs12ReaderStatusCode::kSuccess) ||
key_data.cka_id_value.empty()) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetKeyId));
}
std::vector<CertData> certs_data;
Pkcs12ReaderStatusCode prepare_certs_status = ValidateAndPrepareCertData(
cert_cache_, pkcs12_reader, std::move(certs), key_data, certs_data);
if (prepare_certs_status == Pkcs12ReaderStatusCode::kAlreadyExists) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kAlreadyExists));
}
if ((prepare_certs_status != Pkcs12ReaderStatusCode::kSuccess) ||
certs_data.empty()) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kInvalidPkcs12));
}
auto import_callback = base::BindOnce(&KcerTokenImpl::DidImportPkcs12Cert,
weak_factory_.GetWeakPtr(),
std::move(unblocking_callback));
kcer_utils_.ImportPkcs12(std::move(key_data), std::move(certs_data),
hardware_backed, mark_as_migrated,
std::move(import_callback));
}
void KcerTokenImpl::DidImportPkcs12Cert(
Kcer::StatusCallback callback,
bool did_modify,
base::expected<void, Error> import_result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (did_modify) {
return NotifyCertsChanged(
base::BindOnce(std::move(callback), std::move(import_result)));
}
return std::move(callback).Run(std::move(import_result));
}
//==============================================================================
void KcerTokenImpl::ExportPkcs12Cert(scoped_refptr<const Cert> cert,
Kcer::ExportPkcs12Callback callback) {
// TODO(244409232): Implement.
}
//==============================================================================
KcerTokenImpl::RemoveKeyAndCertsTask::RemoveKeyAndCertsTask(
PrivateKeyHandle in_key,
Kcer::StatusCallback in_callback)
: key(std::move(in_key)), callback(std::move(in_callback)) {}
KcerTokenImpl::RemoveKeyAndCertsTask::RemoveKeyAndCertsTask(
RemoveKeyAndCertsTask&& other) = default;
KcerTokenImpl::RemoveKeyAndCertsTask::~RemoveKeyAndCertsTask() = default;
void KcerTokenImpl::RemoveKeyAndCerts(PrivateKeyHandle key,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::RemoveKeyAndCerts, weak_factory_.GetWeakPtr(),
std::move(key), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
RemoveKeyAndCertsImpl(
RemoveKeyAndCertsTask(std::move(key), std::move(unblocking_callback)));
}
// Finds all objects related to the `task.key` by PKCS#11 id.
void KcerTokenImpl::RemoveKeyAndCertsImpl(RemoveKeyAndCertsTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_ID,
task.key.GetPkcs11IdInternal().value());
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::RemoveKeyAndCertsWithObjectHandles,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Destroys all found objects.
void KcerTokenImpl::RemoveKeyAndCertsWithObjectHandles(
RemoveKeyAndCertsTask task,
std::vector<ObjectHandle> handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return RemoveKeyAndCertsImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
if (handles.empty()) {
return std::move(task.callback).Run(base::unexpected(Error::kKeyNotFound));
}
chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, std::move(handles),
base::BindOnce(&KcerTokenImpl::DidRemoveKeyAndCerts,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Checks the result and notifies that some certs were changed.
void KcerTokenImpl::DidRemoveKeyAndCerts(RemoveKeyAndCertsTask task,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::expected<void, Error> result;
if (SessionChapsClient::IsSessionError(result_code)) {
return RemoveKeyAndCertsImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
result = base::unexpected(Error::kFailedToRemoveObjects);
}
// Even if `DestroyObjectsWithRetries` fails, it might have removed at
// least some objects, so notify about possible changes.
NotifyCertsChanged(
base::BindOnce(std::move(task.callback), std::move(result)));
}
//==============================================================================
KcerTokenImpl::RemoveCertTask::RemoveCertTask(scoped_refptr<const Cert> in_cert,
Kcer::StatusCallback in_callback)
: cert(std::move(in_cert)), callback(std::move(in_callback)) {}
KcerTokenImpl::RemoveCertTask::RemoveCertTask(RemoveCertTask&& other) = default;
KcerTokenImpl::RemoveCertTask::~RemoveCertTask() = default;
void KcerTokenImpl::RemoveCert(scoped_refptr<const Cert> cert,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(
base::BindOnce(&KcerTokenImpl::RemoveCert, weak_factory_.GetWeakPtr(),
std::move(cert), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
RemoveCertImpl(
RemoveCertTask(std::move(cert), std::move(unblocking_callback)));
}
// Searches for objects in Chaps containing the provided cert.
void KcerTokenImpl::RemoveCertImpl(RemoveCertTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
if (!task.cert || !task.cert->GetX509Cert()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToRemoveCertificate));
}
const CRYPTO_BUFFER* buffer = task.cert->GetX509Cert()->cert_buffer();
base::span<const uint8_t> cert_der = UNSAFE_TODO(
base::span(CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer)));
CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE;
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_CLASS, MakeSpan(&cert_class));
AddAttribute(attributes, chromeos::PKCS11_CKA_VALUE, cert_der);
// Find all objects for the certificate. There should be at most one, but the
// code can handle multiple.
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::RemoveCertWithHandles,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Deletes all the found objects.
void KcerTokenImpl::RemoveCertWithHandles(RemoveCertTask task,
std::vector<ObjectHandle> handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return RemoveCertImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
if (handles.empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToRemoveCertificate));
}
chaps_client_->DestroyObjectsWithRetries(
pkcs_11_slot_id_, std::move(handles),
base::BindOnce(&KcerTokenImpl::DidRemoveCert, weak_factory_.GetWeakPtr(),
std::move(task)));
}
void KcerTokenImpl::DidRemoveCert(RemoveCertTask task, uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::expected<void, Error> result;
if (SessionChapsClient::IsSessionError(result_code)) {
return RemoveCertImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
result = base::unexpected(Error::kFailedToRemoveObjects);
}
// Even if `DestroyObjectsWithRetries` fails, it might have removed the cert,
// so notify about possible changes.
NotifyCertsChanged(
base::BindOnce(std::move(task.callback), std::move(result)));
}
//==============================================================================
KcerTokenImpl::ListKeysTask::ListKeysTask(TokenListKeysCallback in_callback)
: callback(std::move(in_callback)) {}
KcerTokenImpl::ListKeysTask::ListKeysTask(ListKeysTask&& other) = default;
KcerTokenImpl::ListKeysTask::~ListKeysTask() = default;
void KcerTokenImpl::ListKeys(TokenListKeysCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(&KcerTokenImpl::ListKeys,
weak_factory_.GetWeakPtr(),
std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
ListKeysImpl(ListKeysTask(std::move(unblocking_callback)));
}
// Starts by finding RSA key objects.
void KcerTokenImpl::ListKeysImpl(ListKeysTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
// For RSA keys the required attributes are stored in the private key objects.
chromeos::PKCS11_CK_OBJECT_CLASS obj_class = chromeos::PKCS11_CKO_PRIVATE_KEY;
chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_RSA;
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_CLASS, MakeSpan(&obj_class));
AddAttribute(attributes, chromeos::PKCS11_CKA_KEY_TYPE, MakeSpan(&key_type));
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::ListKeysWithRsaHandles,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Starts iterating over the RSA keys.
void KcerTokenImpl::ListKeysWithRsaHandles(ListKeysTask task,
std::vector<ObjectHandle> handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ListKeysImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
ListKeysGetOneRsaKey(std::move(task), std::move(handles),
std::vector<PublicKey>());
}
// This is called repeatedly until `handles` is empty.
void KcerTokenImpl::ListKeysGetOneRsaKey(ListKeysTask task,
std::vector<ObjectHandle> handles,
std::vector<PublicKey> result_keys) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (handles.empty()) {
// All RSA keys are handled, now search for EC keys.
return ListKeysFindEcKeys(std::move(task), std::move(result_keys));
}
ObjectHandle current_handle = handles.back();
handles.pop_back();
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, current_handle,
{AttributeId::kPkcs11Id, AttributeId::kModulus,
AttributeId::kPublicExponent},
base::BindOnce(&KcerTokenImpl::ListKeysDidGetOneRsaKey,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(handles), std::move(result_keys)));
}
// Receives attributes for a single RSA key and creates kcer::PublicKey from
// them.
void KcerTokenImpl::ListKeysDidGetOneRsaKey(ListKeysTask task,
std::vector<ObjectHandle> handles,
std::vector<PublicKey> result_keys,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ListKeysImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
// Try to get as many keys as possible even if some of them fail.
return ListKeysGetOneRsaKey(std::move(task), std::move(handles),
std::move(result_keys));
}
base::span<const uint8_t> pkcs11_id =
GetAttributeValue(attributes, AttributeId::kPkcs11Id);
base::span<const uint8_t> modulus =
GetAttributeValue(attributes, AttributeId::kModulus);
base::span<const uint8_t> public_exponent =
GetAttributeValue(attributes, AttributeId::kPublicExponent);
if (pkcs11_id.empty() || modulus.empty() || public_exponent.empty()) {
LOG(WARNING) << "Invalid RSA key was fetched from Chaps, skipping it.";
return ListKeysGetOneRsaKey(std::move(task), std::move(handles),
std::move(result_keys));
}
PublicKeySpki spki = MakeRsaSpki(modulus, public_exponent);
if (spki->empty()) {
LOG(WARNING) << "Invalid RSA key was fetched from Chaps, skipping it.";
return ListKeysGetOneRsaKey(std::move(task), std::move(handles),
std::move(result_keys));
}
std::vector<uint8_t> id(pkcs11_id.begin(), pkcs11_id.end());
result_keys.emplace_back(token_, Pkcs11Id(std::move(id)), std::move(spki));
return ListKeysGetOneRsaKey(std::move(task), std::move(handles),
std::move(result_keys));
}
// Finds EC key objects.
void KcerTokenImpl::ListKeysFindEcKeys(ListKeysTask task,
std::vector<PublicKey> result_keys) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// For EC keys the required attributes are stored in the public key objects.
chromeos::PKCS11_CK_OBJECT_CLASS obj_class = chromeos::PKCS11_CKO_PUBLIC_KEY;
chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_EC;
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_CLASS, MakeSpan(&obj_class));
AddAttribute(attributes, chromeos::PKCS11_CKA_KEY_TYPE, MakeSpan(&key_type));
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::ListKeysWithEcHandles,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(result_keys)));
}
// Starts iterating over the EC keys.
void KcerTokenImpl::ListKeysWithEcHandles(ListKeysTask task,
std::vector<PublicKey> result_keys,
std::vector<ObjectHandle> handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ListKeysImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
// This is called repeatedly until `handles` is empty.
void KcerTokenImpl::ListKeysGetOneEcKey(ListKeysTask task,
std::vector<ObjectHandle> handles,
std::vector<PublicKey> result_keys) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (handles.empty()) {
// All RSA and EC keys are handled, return the final result.
return std::move(task.callback).Run(std::move(result_keys));
}
ObjectHandle current_handle = handles.back();
handles.pop_back();
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, current_handle,
{AttributeId::kPkcs11Id, AttributeId::kEcPoint},
base::BindOnce(&KcerTokenImpl::ListKeysDidGetOneEcKey,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(handles), std::move(result_keys)));
}
// Receives attributes for a single EC key and creates kcer::PublicKey from
// them.
void KcerTokenImpl::ListKeysDidGetOneEcKey(ListKeysTask task,
std::vector<ObjectHandle> handles,
std::vector<PublicKey> result_keys,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return ListKeysImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
// Try to get as many keys as possible even if some of them fail.
return ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
base::span<const uint8_t> pkcs11_id =
GetAttributeValue(attributes, AttributeId::kPkcs11Id);
base::span<const uint8_t> wrapped_ec_point =
GetAttributeValue(attributes, AttributeId::kEcPoint);
if (pkcs11_id.empty() || wrapped_ec_point.empty()) {
LOG(WARNING) << "Invalid EC key was fetched from Chaps, skipping it.";
return ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
bssl::UniquePtr<ASN1_OCTET_STRING> ec_point_oct =
UnwrapEcPoint(wrapped_ec_point);
if (!ec_point_oct) {
LOG(WARNING) << "Invalid EC key was fetched from Chaps, skipping it.";
return ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
const uint8_t* ec_point_data = ASN1_STRING_data(ec_point_oct.get());
size_t ec_point_data_len = ASN1_STRING_length(ec_point_oct.get());
base::span<const uint8_t> ec_point =
UNSAFE_TODO(base::span(ec_point_data, ec_point_data_len));
PublicKeySpki spki = MakeEcSpki(ec_point);
if (spki->empty()) {
LOG(WARNING) << "Invalid EC key was fetched from Chaps, skipping it.";
return ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
std::vector<uint8_t> id(pkcs11_id.begin(), pkcs11_id.end());
PublicKey public_key(token_, Pkcs11Id(std::move(id)), std::move(spki));
chromeos::PKCS11_CK_OBJECT_CLASS obj_class = chromeos::PKCS11_CKO_PRIVATE_KEY;
chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_EC;
chaps::AttributeList private_key_attributes;
AddAttribute(private_key_attributes, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&obj_class));
AddAttribute(private_key_attributes, chromeos::PKCS11_CKA_KEY_TYPE,
MakeSpan(&key_type));
AddAttribute(private_key_attributes, chromeos::PKCS11_CKA_ID,
public_key.GetPkcs11Id().value());
// Check that the private key for public key exists in Chaps. RSA keys don't
// need this check because key attributes can be read from the RSA private key
// objects.
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(private_key_attributes),
base::BindOnce(&KcerTokenImpl::ListKeysDidFindEcPrivateKey,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(handles), std::move(result_keys),
std::move(public_key)));
}
void KcerTokenImpl::ListKeysDidFindEcPrivateKey(
ListKeysTask task,
std::vector<ObjectHandle> handles,
std::vector<PublicKey> result_keys,
PublicKey current_public_key,
std::vector<ObjectHandle> private_key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!private_key_handles.empty()) {
result_keys.push_back(std::move(current_public_key));
}
return ListKeysGetOneEcKey(std::move(task), std::move(handles),
std::move(result_keys));
}
//==============================================================================
void KcerTokenImpl::ListCerts(TokenListCertsCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(&KcerTokenImpl::ListCerts,
weak_factory_.GetWeakPtr(),
std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
// Return current certs from the cache. It's expected to always be up-to-date
// at this point. UnblockQueueProcessNextTask() will update the cache first
// when necessary.
return std::move(unblocking_callback).Run(cert_cache_.GetAllCerts());
}
//==============================================================================
KcerTokenImpl::DoesPrivateKeyExistTask::DoesPrivateKeyExistTask(
PrivateKeyHandle in_key,
Kcer::DoesKeyExistCallback in_callback)
: key(std::move(in_key)), callback(std::move(in_callback)) {}
KcerTokenImpl::DoesPrivateKeyExistTask::DoesPrivateKeyExistTask(
DoesPrivateKeyExistTask&& other) = default;
KcerTokenImpl::DoesPrivateKeyExistTask::~DoesPrivateKeyExistTask() = default;
void KcerTokenImpl::DoesPrivateKeyExist(PrivateKeyHandle key,
Kcer::DoesKeyExistCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::DoesPrivateKeyExist, weak_factory_.GetWeakPtr(),
std::move(key), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
DoesPrivateKeyExistImpl(
DoesPrivateKeyExistTask(std::move(key), std::move(unblocking_callback)));
}
// Searches for the Chaps handle for `task.key`.
void KcerTokenImpl::DoesPrivateKeyExistImpl(DoesPrivateKeyExistTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
chaps::AttributeList private_key_attrs;
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class));
AddAttribute(private_key_attrs, chromeos::PKCS11_CKA_ID,
task.key.GetPkcs11IdInternal().value());
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(private_key_attrs),
base::BindOnce(&KcerTokenImpl::DidDoesPrivateKeyExist,
weak_factory_.GetWeakPtr(), std::move(task)));
}
void KcerTokenImpl::DidDoesPrivateKeyExist(
DoesPrivateKeyExistTask task,
std::vector<ObjectHandle> object_list,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return DoesPrivateKeyExistImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
return std::move(task.callback).Run(!object_list.empty());
}
//==============================================================================
KcerTokenImpl::SignTask::SignTask(PrivateKeyHandle in_key,
SigningScheme in_signing_scheme,
DataToSign in_data,
Kcer::SignCallback in_callback)
: key(std::move(in_key)),
signing_scheme(in_signing_scheme),
data(std::move(in_data)),
callback(std::move(in_callback)) {}
KcerTokenImpl::SignTask::SignTask(SignTask&& other) = default;
KcerTokenImpl::SignTask::~SignTask() = default;
void KcerTokenImpl::Sign(PrivateKeyHandle key,
SigningScheme signing_scheme,
DataToSign data,
Kcer::SignCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::Sign, weak_factory_.GetWeakPtr(), std::move(key),
signing_scheme, std::move(data), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
SignImpl(SignTask(std::move(key), signing_scheme, std::move(data),
std::move(unblocking_callback)));
}
// Finds the key.
void KcerTokenImpl::SignImpl(SignTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
Pkcs11Id key_id = task.key.GetPkcs11IdInternal();
kcer_utils_.FindPrivateKey(
std::move(key_id),
base::BindOnce(&KcerTokenImpl::SignWithKeyHandle,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Digests the data.
void KcerTokenImpl::SignWithKeyHandle(SignTask task,
std::vector<ObjectHandle> key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SignImpl(std::move(task));
}
if ((result_code != chromeos::PKCS11_CKR_OK) || key_handles.empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
DCHECK_EQ(key_handles.size(), 1u);
DataToSign data = task.data;
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&DigestOnWorkerThread, task.signing_scheme,
std::move(data)),
base::BindOnce(&KcerTokenImpl::SignWithKeyHandleAndDigest,
weak_factory_.GetWeakPtr(), std::move(task),
key_handles.front()));
}
// Signs the data.
void KcerTokenImpl::SignWithKeyHandleAndDigest(
SignTask task,
ObjectHandle key_handle,
base::expected<DigestWithPrefix, Error> digest) {
if (!digest.has_value()) {
return std::move(task.callback).Run(base::unexpected(digest.error()));
}
uint64_t mechanism = SigningSchemeToPkcs11Mechanism(task.signing_scheme);
std::vector<uint8_t> mechanism_params;
if (mechanism == chromeos::PKCS11_CKM_RSA_PKCS_PSS) {
mechanism_params = GetPssSignParams(task.signing_scheme);
}
auto chaps_callback = base::BindOnce(
&KcerTokenImpl::DidSign, weak_factory_.GetWeakPtr(), std::move(task));
chaps_client_->Sign(pkcs_11_slot_id_, mechanism, mechanism_params, key_handle,
std::move(digest).value().value(),
std::move(chaps_callback));
}
// Re-encodes the signature if needed.
void KcerTokenImpl::DidSign(SignTask task,
std::vector<uint8_t> signature,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SignImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback).Run(base::unexpected(Error::kFailedToSign));
}
// ECDSA signatures have to be reencoded.
uint64_t mechanism = SigningSchemeToPkcs11Mechanism(task.signing_scheme);
if (mechanism == chromeos::PKCS11_CKM_ECDSA) {
base::expected<std::vector<uint8_t>, Error> reencoded_signature =
ReencodeEcSignatureAsAsn1(std::move(signature));
if (!reencoded_signature.has_value()) {
return std::move(task.callback)
.Run(base::unexpected(reencoded_signature.error()));
}
signature = std::move(reencoded_signature).value();
}
return std::move(task.callback).Run(Signature(signature));
}
//==============================================================================
KcerTokenImpl::SignRsaPkcs1RawTask::SignRsaPkcs1RawTask(
PrivateKeyHandle in_key,
DigestWithPrefix in_digest_with_prefix,
Kcer::SignCallback in_callback)
: key(std::move(in_key)),
digest_with_prefix(std::move(in_digest_with_prefix)),
callback(std::move(in_callback)) {}
KcerTokenImpl::SignRsaPkcs1RawTask::SignRsaPkcs1RawTask(
SignRsaPkcs1RawTask&& other) = default;
KcerTokenImpl::SignRsaPkcs1RawTask::~SignRsaPkcs1RawTask() = default;
void KcerTokenImpl::SignRsaPkcs1Raw(PrivateKeyHandle key,
DigestWithPrefix digest_with_prefix,
Kcer::SignCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::SignRsaPkcs1Raw, weak_factory_.GetWeakPtr(),
std::move(key), std::move(digest_with_prefix), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
SignRsaPkcs1RawImpl(SignRsaPkcs1RawTask(std::move(key),
std::move(digest_with_prefix),
std::move(unblocking_callback)));
}
// Finds the key.
void KcerTokenImpl::SignRsaPkcs1RawImpl(SignRsaPkcs1RawTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
Pkcs11Id key_id = task.key.GetPkcs11IdInternal();
kcer_utils_.FindPrivateKey(
std::move(key_id),
base::BindOnce(&KcerTokenImpl::SignRsaPkcs1RawWithKeyHandle,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Sings the data.
void KcerTokenImpl::SignRsaPkcs1RawWithKeyHandle(
SignRsaPkcs1RawTask task,
std::vector<ObjectHandle> key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SignRsaPkcs1RawImpl(std::move(task));
}
if ((result_code != chromeos::PKCS11_CKR_OK) || key_handles.empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToSearchForObjects));
}
DCHECK_EQ(key_handles.size(), 1u);
ObjectHandle key_handle = key_handles.front();
uint64_t mechanism =
SigningSchemeToPkcs11Mechanism(SigningScheme::kRsaPkcs1Sha256);
std::vector<uint8_t> digest = task.digest_with_prefix.value();
auto chaps_callback =
base::BindOnce(&KcerTokenImpl::DidSignRsaPkcs1Raw,
weak_factory_.GetWeakPtr(), std::move(task));
chaps_client_->Sign(pkcs_11_slot_id_, mechanism,
/*mechanism_parameter=*/std::vector<uint8_t>(),
key_handle, std::move(digest), std::move(chaps_callback));
}
void KcerTokenImpl::DidSignRsaPkcs1Raw(SignRsaPkcs1RawTask task,
std::vector<uint8_t> signature,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SignRsaPkcs1RawImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback).Run(base::unexpected(Error::kFailedToSign));
}
return std::move(task.callback).Run(Signature(signature));
}
//==============================================================================
void KcerTokenImpl::GetTokenInfo(Kcer::GetTokenInfoCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(&KcerTokenImpl::GetTokenInfo,
weak_factory_.GetWeakPtr(),
std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
TokenInfo result;
result.pkcs11_id = pkcs_11_slot_id_.value();
result.module_name = "Chaps";
switch (token_) {
case Token::kUser:
result.token_name = "User Token";
break;
case Token::kDevice:
result.token_name = "Device Token";
break;
}
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(unblocking_callback), std::move(result)));
}
//==============================================================================
void KcerTokenImpl::GetKeyAttributes(
PrivateKeyHandle key,
std::vector<HighLevelChapsClient::AttributeId> attribute_ids,
GetKeyAttributesCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
kcer_utils_.FindPrivateKey(
key.GetPkcs11IdInternal(),
base::BindOnce(&KcerTokenImpl::GetKeyAttributesWithKeyHandle,
weak_factory_.GetWeakPtr(), std::move(attribute_ids),
std::move(callback)));
}
void KcerTokenImpl::GetKeyAttributesWithKeyHandle(
std::vector<HighLevelChapsClient::AttributeId> attribute_ids,
GetKeyAttributesCallback callback,
std::vector<ObjectHandle> private_key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return std::move(callback).Run(/*kcer_error=*/std::nullopt,
chaps::AttributeList(), result_code);
}
if ((result_code != chromeos::PKCS11_CKR_OK) || private_key_handles.empty()) {
return std::move(callback).Run(Error::kKeyNotFound, chaps::AttributeList(),
result_code);
}
if (private_key_handles.size() != 1) {
// This shouldn't happen.
return std::move(callback).Run(Error::kUnexpectedFindResult,
chaps::AttributeList(), result_code);
}
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, private_key_handles.front(), std::move(attribute_ids),
base::BindOnce(std::move(callback), /*kcer_error=*/std::nullopt));
}
//==============================================================================
KcerTokenImpl::GetKeyInfoTask::GetKeyInfoTask(
PrivateKeyHandle in_key,
Kcer::GetKeyInfoCallback in_callback)
: key(std::move(in_key)), callback(std::move(in_callback)) {}
KcerTokenImpl::GetKeyInfoTask::GetKeyInfoTask(GetKeyInfoTask&& other) = default;
KcerTokenImpl::GetKeyInfoTask::~GetKeyInfoTask() = default;
void KcerTokenImpl::GetKeyInfo(PrivateKeyHandle key,
Kcer::GetKeyInfoCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(
base::BindOnce(&KcerTokenImpl::GetKeyInfo, weak_factory_.GetWeakPtr(),
std::move(key), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
GetKeyInfoImpl(
GetKeyInfoTask(std::move(key), std::move(unblocking_callback)));
}
// If PSS support for the token is not known yet - query it, all keys implicitly
// inherit it. Otherwise proceed to retrieving actual key info.
void KcerTokenImpl::GetKeyInfoImpl(GetKeyInfoTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
if (!token_supports_pss_.has_value()) {
return chaps_client_->GetMechanismList(
pkcs_11_slot_id_,
base::BindOnce(&KcerTokenImpl::GetKeyInfoWithMechanismList,
weak_factory_.GetWeakPtr(), std::move(task)));
}
GetKeyInfoGetAttributes(std::move(task));
}
// Receives and caches PSS support for the token.
void KcerTokenImpl::GetKeyInfoWithMechanismList(
GetKeyInfoTask task,
const std::vector<uint64_t>& mechanism_list,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return GetKeyInfoImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToRetrieveMechanismList));
}
for (uint64_t mechanism : mechanism_list) {
if (mechanism == chromeos::PKCS11_CKM_RSA_PKCS_PSS) {
token_supports_pss_ = true;
break;
}
}
if (!token_supports_pss_.has_value()) {
token_supports_pss_ = false;
}
GetKeyInfoGetAttributes(std::move(task));
}
void KcerTokenImpl::GetKeyInfoGetAttributes(GetKeyInfoTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
PrivateKeyHandle key = task.key;
GetKeyAttributes(
std::move(key),
{AttributeId::kKeyInSoftware, AttributeId::kKeyType, AttributeId::kLabel},
base::BindOnce(&KcerTokenImpl::GetKeyInfoWithAttributes,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Parses the attributes into the result struct and returns it.
void KcerTokenImpl::GetKeyInfoWithAttributes(GetKeyInfoTask task,
std::optional<Error> kcer_error,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (kcer_error.has_value()) {
return std::move(task.callback).Run(base::unexpected(kcer_error.value()));
}
if (SessionChapsClient::IsSessionError(result_code)) {
return GetKeyInfoImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToReadAttribute));
}
KeyInfo key_info;
bool is_ok = true;
is_ok = is_ok && GetIsHardwareBacked(
GetAttribute(attributes, AttributeId::kKeyInSoftware),
key_info.is_hardware_backed);
is_ok = is_ok && GetKeyType(GetAttribute(attributes, AttributeId::kKeyType),
key_info.key_type);
is_ok =
is_ok && GetOptionalString(GetAttribute(attributes, AttributeId::kLabel),
key_info.nickname);
if (!is_ok) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
CHECK(token_supports_pss_.has_value());
key_info.supported_signing_schemes = GetSupportedSigningSchemes(
token_supports_pss_.value(), key_info.key_type);
return std::move(task.callback).Run(std::move(key_info));
}
//==============================================================================
KcerTokenImpl::GetKeyPermissionsTask::GetKeyPermissionsTask(
PrivateKeyHandle in_key,
Kcer::GetKeyPermissionsCallback in_callback)
: key(std::move(in_key)), callback(std::move(in_callback)) {}
KcerTokenImpl::GetKeyPermissionsTask::GetKeyPermissionsTask(
GetKeyPermissionsTask&& other) = default;
KcerTokenImpl::GetKeyPermissionsTask::~GetKeyPermissionsTask() = default;
void KcerTokenImpl::GetKeyPermissions(
PrivateKeyHandle key,
Kcer::GetKeyPermissionsCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::GetKeyPermissions, weak_factory_.GetWeakPtr(),
std::move(key), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
GetKeyPermissionsImpl(
GetKeyPermissionsTask(std::move(key), std::move(unblocking_callback)));
}
void KcerTokenImpl::GetKeyPermissionsImpl(GetKeyPermissionsTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
PrivateKeyHandle key = task.key;
GetKeyAttributes(
std::move(key), {AttributeId::kKeyPermissions},
base::BindOnce(&KcerTokenImpl::GetKeyPermissionsWithAttributes,
weak_factory_.GetWeakPtr(), std::move(task)));
}
void KcerTokenImpl::GetKeyPermissionsWithAttributes(
GetKeyPermissionsTask task,
std::optional<Error> kcer_error,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (kcer_error.has_value()) {
return std::move(task.callback).Run(base::unexpected(kcer_error.value()));
}
if (SessionChapsClient::IsSessionError(result_code)) {
return GetKeyPermissionsImpl(std::move(task));
}
if (result_code == chromeos::PKCS11_CKR_ATTRIBUTE_TYPE_INVALID) {
// Key permissions were never set on this key.
return std::move(task.callback).Run(std::nullopt);
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToReadAttribute));
}
if (attributes.attributes_size() != 1) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
const chaps::Attribute& attr = attributes.attributes(0);
if ((attr.type() != static_cast<uint32_t>(AttributeId::kKeyPermissions)) ||
!attr.has_value() || attr.value().empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
chaps::KeyPermissions key_permissions;
if (!key_permissions.ParseFromArray(attr.value().data(),
attr.value().size())) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
return std::move(task.callback).Run(std::move(key_permissions));
}
//==============================================================================
KcerTokenImpl::GetCertProvisioningIdTask::GetCertProvisioningIdTask(
PrivateKeyHandle in_key,
Kcer::GetCertProvisioningProfileIdCallback in_callback)
: key(std::move(in_key)), callback(std::move(in_callback)) {}
KcerTokenImpl::GetCertProvisioningIdTask::GetCertProvisioningIdTask(
GetCertProvisioningIdTask&& other) = default;
KcerTokenImpl::GetCertProvisioningIdTask::~GetCertProvisioningIdTask() =
default;
void KcerTokenImpl::GetCertProvisioningProfileId(
PrivateKeyHandle key,
Kcer::GetCertProvisioningProfileIdCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::GetCertProvisioningProfileId,
weak_factory_.GetWeakPtr(), std::move(key), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(unblocking_callback)
.Run(base::unexpected(Error::kFailedToGetPkcs11Id));
}
GetCertProvisioningIdImpl(GetCertProvisioningIdTask(
std::move(key), std::move(unblocking_callback)));
}
void KcerTokenImpl::GetCertProvisioningIdImpl(GetCertProvisioningIdTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
PrivateKeyHandle key = task.key;
GetKeyAttributes(
std::move(key), {AttributeId::kCertProvisioningId},
base::BindOnce(&KcerTokenImpl::GetCertProvisioningIdWithAttributes,
weak_factory_.GetWeakPtr(), std::move(task)));
}
void KcerTokenImpl::GetCertProvisioningIdWithAttributes(
GetCertProvisioningIdTask task,
std::optional<Error> kcer_error,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (kcer_error.has_value()) {
return std::move(task.callback).Run(base::unexpected(kcer_error.value()));
}
if (SessionChapsClient::IsSessionError(result_code)) {
return GetCertProvisioningIdImpl(std::move(task));
}
if (result_code == chromeos::PKCS11_CKR_ATTRIBUTE_TYPE_INVALID) {
// Cert provisioning profile id was never set on this key.
return std::move(task.callback).Run(std::nullopt);
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToReadAttribute));
}
if (attributes.attributes_size() != 1) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
const chaps::Attribute& attr = attributes.attributes(0);
if ((attr.type() !=
static_cast<uint32_t>(AttributeId::kCertProvisioningId)) ||
!attr.has_value() || attr.value().empty()) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToDecodeKeyAttributes));
}
return std::move(task.callback).Run(attr.value());
}
//==============================================================================
KcerTokenImpl::SetKeyAttributeTask::SetKeyAttributeTask(
PrivateKeyHandle in_key,
HighLevelChapsClient::AttributeId in_attribute_id,
std::vector<uint8_t> in_attribute_value,
Kcer::StatusCallback in_callback)
: key(std::move(in_key)),
attribute_id(in_attribute_id),
attribute_value(std::move(in_attribute_value)),
callback(std::move(in_callback)) {}
KcerTokenImpl::SetKeyAttributeTask::SetKeyAttributeTask(
SetKeyAttributeTask&& other) = default;
KcerTokenImpl::SetKeyAttributeTask::~SetKeyAttributeTask() = default;
void KcerTokenImpl::SetKeyAttribute(
PrivateKeyHandle key,
HighLevelChapsClient::AttributeId attribute_id,
std::vector<uint8_t> attribute_value,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!EnsurePkcs11IdIsSet(key)) {
return std::move(callback).Run(
base::unexpected(Error::kFailedToGetPkcs11Id));
}
SetKeyAttributeImpl(SetKeyAttributeTask(std::move(key), attribute_id,
std::move(attribute_value),
std::move(callback)));
}
// Finds the private key that will store the attribute.
void KcerTokenImpl::SetKeyAttributeImpl(SetKeyAttributeTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return std::move(task.callback)
.Run(base::unexpected(Error::kPkcs11SessionFailure));
}
chromeos::PKCS11_CK_OBJECT_CLASS obj_class = chromeos::PKCS11_CKO_PRIVATE_KEY;
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_CLASS, MakeSpan(&obj_class));
AddAttribute(attributes, chromeos::PKCS11_CKA_ID,
task.key.GetPkcs11IdInternal().value());
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::SetKeyAttributeWithHandle,
weak_factory_.GetWeakPtr(), std::move(task)));
}
// Set attribute on the key.
void KcerTokenImpl::SetKeyAttributeWithHandle(
SetKeyAttributeTask task,
std::vector<ObjectHandle> private_key_handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SetKeyAttributeImpl(std::move(task));
}
if ((result_code != chromeos::PKCS11_CKR_OK) || private_key_handles.empty()) {
return std::move(task.callback).Run(base::unexpected(Error::kKeyNotFound));
}
if (private_key_handles.size() != 1) {
// This shouldn't happen.
return std::move(task.callback)
.Run(base::unexpected(Error::kUnexpectedFindResult));
}
chaps::AttributeList attributes;
AddAttribute(attributes, static_cast<uint32_t>(task.attribute_id),
task.attribute_value);
chaps_client_->SetAttributeValue(
pkcs_11_slot_id_, private_key_handles.front(), attributes,
base::BindOnce(&KcerTokenImpl::SetKeyAttributeDidSetAttribute,
weak_factory_.GetWeakPtr(), std::move(task)));
}
void KcerTokenImpl::SetKeyAttributeDidSetAttribute(SetKeyAttributeTask task,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (SessionChapsClient::IsSessionError(result_code)) {
return SetKeyAttributeImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return std::move(task.callback)
.Run(base::unexpected(Error::kFailedToWriteAttribute));
}
return std::move(task.callback).Run({});
}
//==============================================================================
void KcerTokenImpl::SetKeyNickname(PrivateKeyHandle key,
std::string nickname,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::SetKeyNickname, weak_factory_.GetWeakPtr(),
std::move(key), std::move(nickname), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
return SetKeyAttribute(std::move(key),
HighLevelChapsClient::AttributeId::kLabel,
std::vector<uint8_t>(nickname.begin(), nickname.end()),
std::move(unblocking_callback));
}
//==============================================================================
void KcerTokenImpl::SetKeyPermissions(PrivateKeyHandle key,
chaps::KeyPermissions key_permissions,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(
&KcerTokenImpl::SetKeyPermissions, weak_factory_.GetWeakPtr(),
std::move(key), std::move(key_permissions), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
std::vector<uint8_t> serialized_permissions;
serialized_permissions.resize(key_permissions.ByteSizeLong());
key_permissions.SerializeToArray(serialized_permissions.data(),
serialized_permissions.size());
return SetKeyAttribute(
std::move(key), HighLevelChapsClient::AttributeId::kKeyPermissions,
serialized_permissions, std::move(unblocking_callback));
}
//==============================================================================
void KcerTokenImpl::SetCertProvisioningProfileId(
PrivateKeyHandle key,
std::string profile_id,
Kcer::StatusCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(
base::BindOnce(&KcerTokenImpl::SetCertProvisioningProfileId,
weak_factory_.GetWeakPtr(), std::move(key),
std::move(profile_id), std::move(callback)));
}
// Block task queue, attach unblocking task to the callback.
auto unblocking_callback = BlockQueueGetUnblocker(std::move(callback));
return SetKeyAttribute(
std::move(key), HighLevelChapsClient::AttributeId::kCertProvisioningId,
std::vector<uint8_t>(profile_id.begin(), profile_id.end()),
std::move(unblocking_callback));
}
//==============================================================================
KcerTokenImpl::UpdateCacheTask::UpdateCacheTask(base::OnceClosure in_callback)
: callback(std::move(in_callback)) {}
KcerTokenImpl::UpdateCacheTask::UpdateCacheTask(UpdateCacheTask&& other) =
default;
KcerTokenImpl::UpdateCacheTask::~UpdateCacheTask() = default;
void KcerTokenImpl::UpdateCache() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_blocked_) {
return task_queue_.push_back(base::BindOnce(&KcerTokenImpl::UpdateCache,
weak_factory_.GetWeakPtr()));
}
// Block task queue, attach unblocking task to the DoNothing closure.
auto unblocking_callback =
BlockQueueGetUnblocker(base::OnceClosure(base::DoNothing()));
UpdateCacheImpl(UpdateCacheTask(std::move(unblocking_callback)));
}
// Finds all certificate objects in Chaps.
void KcerTokenImpl::UpdateCacheImpl(UpdateCacheTask task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
task.attemps_left--;
if (task.attemps_left < 0) {
return UpdateCacheWithCerts(std::move(task),
base::unexpected(Error::kPkcs11SessionFailure));
}
cache_state_ = CacheState::kUpdating;
chromeos::PKCS11_CK_OBJECT_CLASS cert_class =
chromeos::PKCS11_CKO_CERTIFICATE;
chaps::AttributeList attributes;
AddAttribute(attributes, chromeos::PKCS11_CKA_CLASS, MakeSpan(&cert_class));
chaps_client_->FindObjects(
pkcs_11_slot_id_, std::move(attributes),
base::BindOnce(&KcerTokenImpl::UpdateCacheWithCertHandles,
weak_factory_.GetWeakPtr(), std::move(task)));
}
void KcerTokenImpl::UpdateCacheWithCertHandles(
UpdateCacheTask task,
std::vector<ObjectHandle> handles,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (cache_state_ == CacheState::kOutdated) {
// If the status switched from kUpdating, then a new update happened since
// the cache started to update, `handles` might already be outdated.
// Skip re-building the cache and try again by unblocking the queue and
// returning to UnblockQueueProcessNextTask().
return std::move(task.callback).Run();
}
if (SessionChapsClient::IsSessionError(result_code)) {
return UpdateCacheImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
return UpdateCacheWithCerts(
std::move(task), base::unexpected(Error::kFailedToSearchForObjects));
}
UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::vector<scoped_refptr<const Cert>>());
}
// This is called repeatedly until `handles` is empty.
void KcerTokenImpl::UpdateCacheGetOneCert(
UpdateCacheTask task,
std::vector<ObjectHandle> handles,
std::vector<scoped_refptr<const Cert>> certs) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (handles.empty()) {
return UpdateCacheWithCerts(std::move(task), std::move(certs));
}
ObjectHandle current_handle = handles.back();
handles.pop_back();
chaps_client_->GetAttributeValue(
pkcs_11_slot_id_, current_handle,
{AttributeId::kPkcs11Id, AttributeId::kLabel, AttributeId::kValue},
base::BindOnce(&KcerTokenImpl::UpdateCacheDidGetOneCert,
weak_factory_.GetWeakPtr(), std::move(task),
std::move(handles), std::move(certs)));
}
// Parses attributes of a single cert and adds the new object to `certs`.
void KcerTokenImpl::UpdateCacheDidGetOneCert(
UpdateCacheTask task,
std::vector<ObjectHandle> handles,
std::vector<scoped_refptr<const Cert>> certs,
chaps::AttributeList attributes,
uint32_t result_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (cache_state_ == CacheState::kOutdated) {
// If the status switched from kUpdating, then new update happened since
// the cache started to update, `certs` might already be outdated.
// Skip re-building the cache and try again by unblocking the queue and
// returning to UnblockQueueProcessNextTask().
return std::move(task.callback).Run();
}
if (SessionChapsClient::IsSessionError(result_code)) {
return UpdateCacheImpl(std::move(task));
}
if (result_code != chromeos::PKCS11_CKR_OK) {
LOG(WARNING) << "Failed to get attributes for a cert, skipping it";
// Try to get as many certs as possible even if some of them fail.
return UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::move(certs));
}
base::span<const uint8_t> pkcs11_id =
GetAttributeValue(attributes, AttributeId::kPkcs11Id);
base::span<const uint8_t> nickname =
GetAttributeValue(attributes, AttributeId::kLabel);
base::span<const uint8_t> cert_der =
GetAttributeValue(attributes, AttributeId::kValue);
if (pkcs11_id.empty() || cert_der.empty()) {
LOG(WARNING) << "Invalid cert was fetched from Chaps, skipping it";
return UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::move(certs));
}
scoped_refptr<const Cert> existing_cert = cert_cache_.FindCert(cert_der);
if (existing_cert) {
certs.push_back(std::move(existing_cert));
return UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::move(certs));
}
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(cert_der);
if (!x509_cert) {
LOG(WARNING) << "Failed to parse a cert from Chaps, skipping it";
return UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::move(certs));
}
std::vector<uint8_t> id(pkcs11_id.begin(), pkcs11_id.end());
certs.push_back(base::MakeRefCounted<Cert>(
token_, Pkcs11Id(std::move(id)),
std::string(nickname.begin(), nickname.end()), std::move(x509_cert)));
return UpdateCacheGetOneCert(std::move(task), std::move(handles),
std::move(certs));
}
void KcerTokenImpl::UpdateCacheWithCerts(
UpdateCacheTask task,
base::expected<std::vector<scoped_refptr<const Cert>>, Error> certs) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (certs.has_value()) {
cert_cache_ = CertCache(certs.value());
} else {
LOG(ERROR) << "Failed to update cert cache, error: "
<< static_cast<uint32_t>(certs.error());
}
// Even if the update failed, mark it as complete to avoid an infinite loop in
// case of persistent errors.
cache_state_ = CacheState::kUpToDate;
return std::move(task.callback).Run();
}
//==============================================================================
void KcerTokenImpl::NotifyCertsChanged(base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
// The Notify... above will post a task to invalidate the cache. Calling the
// original callback for a request will automatically trigger updating cache
// and executing the next request. Post a task with the original callback
// (instead of calling it synchronously), so the cache update and the next
// request happen after the notification.
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback));
}
template <typename... Args>
void RunUnblockerAndCallback(base::ScopedClosureRunner unblocker,
base::OnceCallback<void(Args...)> callback,
Args... args) {
unblocker.RunAndReset();
std::move(callback).Run(args...);
}
template <typename... Args>
base::OnceCallback<void(Args...)> KcerTokenImpl::BlockQueueGetUnblocker(
base::OnceCallback<void(Args...)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK(!is_blocked_);
is_blocked_ = true;
// `unblocker` is executed either manually or on destruction.
base::ScopedClosureRunner unblocker(base::BindOnce(
&KcerTokenImpl::UnblockQueueProcessNextTask, weak_factory_.GetWeakPtr()));
return base::BindOnce(&RunUnblockerAndCallback<Args...>, std::move(unblocker),
std::move(callback));
}
void KcerTokenImpl::UnblockQueueProcessNextTask() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
is_blocked_ = false;
if (cache_state_ == CacheState::kOutdated) {
return UpdateCache();
}
if (task_queue_.empty()) {
return;
}
base::OnceClosure next_task = std::move(task_queue_.front());
task_queue_.pop_front();
std::move(next_task).Run();
}
} // namespace kcer::internal