// 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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chromeos/components/kcer/kcer_token_impl.h"
#include <string_view>
#include "base/base64.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/test_future.h"
#include "chromeos/components/kcer/chaps/mock_high_level_chaps_client.h"
#include "chromeos/components/kcer/kcer_nss/test_utils.h"
#include "content/public/test/browser_task_environment.h"
#include "net/cert/cert_database.h"
#include "net/cert/x509_util.h"
#include "net/test/cert_builder.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/chaps/dbus-constants.h"
using base::test::RunOnceCallback;
using base::test::RunOnceCallbackRepeatedly;
using testing::_;
using testing::DoAll;
using testing::Invoke;
using ObjectHandle = kcer::SessionChapsClient::ObjectHandle;
using AttributeId = kcer::HighLevelChapsClient::AttributeId;
using testing::UnorderedElementsAre;
namespace kcer::internal {
namespace {
constexpr int kDefaultAttempts = KcerTokenImpl::kDefaultAttempts;
// Base64 encoded RSA modulus for client_1.key.
constexpr char kFakeRsaModulusBase64[] =
"2vg6u00xUsNQMUZGn7awjPLbj22B+"
"zU8S3R4pdBdsuBvj765ajdKgeN7UWiiT0BGIRvkZ6aV3jpOvwG4DhVwUAj6FSVjo8cDF93mt60"
"izOQvcVD/UbJ3/35J4A+huqTqNp2Ip34KSDUA4Rgf6/ASbOvUTYb4HWzeGlewc/"
"FhmKihdw1YVfg4ucyDjzQvc6GUD3suHbjEtOjhERuzqfHxcLN0KAsxMsFXQG3v0fpuaHoPO+"
"by1b5jjcA4udpqr+"
"K4G35fmj4csj86Ed4XSDxItdwEAeYZsiwHEw7b3Im923jS8OfOjV3GIEGXXGy5g5I1UQyuLp4h"
"OEGkVBnkSQ9vsQ==";
constexpr char kFakeRsaExponentBase64[] = "AQAB";
// The correct id for `kFakeRsaModulusBase64`.
constexpr char kFakeRsaPkcs11IdBase64[] = "PIlO/U4s0iZrIYu7pjwbGzTrWlA=";
// The correct SPKI for `kFakeRsaModulusBase64` and `kFakeRsaExponentBase64`
// pair.
constexpr char kFakeRsaSpkiBase64[] =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2vg6u00xUsNQMUZGn7awjPLbj22B+"
"zU8S3R4pdBdsuBvj765ajdKgeN7UWiiT0BGIRvkZ6aV3jpOvwG4DhVwUAj6FSVjo8cDF93mt60"
"izOQvcVD/UbJ3/35J4A+huqTqNp2Ip34KSDUA4Rgf6/ASbOvUTYb4HWzeGlewc/"
"FhmKihdw1YVfg4ucyDjzQvc6GUD3suHbjEtOjhERuzqfHxcLN0KAsxMsFXQG3v0fpuaHoPO+"
"by1b5jjcA4udpqr+"
"K4G35fmj4csj86Ed4XSDxItdwEAeYZsiwHEw7b3Im923jS8OfOjV3GIEGXXGy5g5I1UQyuLp4h"
"OEGkVBnkSQ9vsQIDAQAB";
// DER-encoded ASN.1 octet string containing the public value of an EC key (the
// EC point).
constexpr char kFakeEcPublicValueOctetStringDerBase64[] =
"BEEE862evgBzpaK6fQ+IuGRLhVtiHE9eg0Qq+auOnkj/"
"PQsakZK+HwzGhwq9BkcLYwa3J7pY4wVkbR0wKO7u8ql7kw==";
// The correct id for `kFakeEcPublicValueBase64`.
constexpr char kFakeEcPkcs11IdBase64[] = "gE0RjontXMV0CGICnWode163LoU=";
// The correct SPKI for `kFakeEcPublicValueBase64` and `kFakeEcPkcs11IdBase64`
// pair.
constexpr char kFakeEcSpkiBase64[] =
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE862evgBzpaK6fQ+IuGRLhVtiHE9eg0Qq+"
"auOnkj/PQsakZK+HwzGhwq9BkcLYwa3J7pY4wVkbR0wKO7u8ql7kw==";
// Generated by chrome/test/data/policy/test_certs/create_test_certs.sh
constexpr char kFakeCertificate[] =
"MIIDAzCCAeugAwIBAgIJAKqcrTp/"
"a0aRMA0GCSqGSIb3DQEBCwUAMCIxIDAeBgNVBAMMF0lzc3VlclN1YmplY3RDb21tb25OYW1lMB"
"4XDTcwMDEwMjAwNDQyN1oXDTcwMDIwMTAwNDQyN1owHDEaMBgGA1UEAwwRU3ViamVjdENvbW1v"
"bk5hbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC57XcC/"
"hnVfyqzjoc785VENgZ+DfcCJOYjBFLVkBtKX+sbAyef7X42Jpfb6riB8bCH/"
"nG9+AGgUySkTHRryQS8STJx00+IolhwDE+l7QoaznID5USkwxhiPOFD+"
"1bZUF6b7UKISrws7SLSmxEjPuiMc2bDJWmuUMCrhm4mpoG1F43m1iyxtZx+"
"3eXC1NJ3HvDGSTSlBcRlKdO+glcMf0YAQmFN+py6PXLGq1HbOrh2shnz72N+"
"34H7jMoulRbA49kkyh0wRbW41s+"
"yJPG0oOAJzc5jKYBoRM7uDfzoBPbFYclpHbNHxhYbzhjvFVAar+"
"zhoX5lTwKmG0UhALcsThQ1AgMBAAGjQjBAMB0GA1UdDgQWBBTUNlxLd+brIllUgxM5mFQ/"
"Zdxl7jAfBgNVHSMEGDAWgBS0pYptJl5osorqt880cBIKOMvfRTANBgkqhkiG9w0BAQsFAAOCAQ"
"EAQn0F/"
"Q7QAowOlNwY74cHJRNs4rktcCcV3VODNwSNaXuZtELK2XI9wFweX6szR4EpFbnQnOS+"
"SE52meaJb5TwqPWvd6ILkbr9XRHBFze296vU81IUrcm0IYu1R4wrguFnGuMLI9fjMOeYOCOXbr"
"JpYm8QMr3dNnE/QIrz+qZ3PxBI7e/gLTezowfR40/"
"yfGniCIBjZacKNosXaf2M5ZS7iqqdAQiTaHKp0eTONCBI7OGRKPq7VF+"
"1qtPl5xE7ffmxf1TbmsDp0UyPy1zlcZUrWHwVjcuNil09wwZfSG1lqLOX6zXdWZKkIJzRlBNXw"
"6GzDsbwCyiKzpEBk3edl+aNfQ==";
const std::vector<uint8_t>& GetRsaModulus() {
static std::vector<uint8_t> value =
base::Base64Decode(kFakeRsaModulusBase64).value();
return value;
}
const std::vector<uint8_t>& GetRsaPublicExponent() {
static std::vector<uint8_t> value =
base::Base64Decode(kFakeRsaExponentBase64).value();
return value;
}
const Pkcs11Id& GetRsaPkcs11Id() {
static Pkcs11Id value(base::Base64Decode(kFakeRsaPkcs11IdBase64).value());
return value;
}
const PublicKeySpki& GetRsaSpki() {
static PublicKeySpki value(base::Base64Decode(kFakeRsaSpkiBase64).value());
return value;
}
const std::vector<uint8_t>& GetEcPublicValue() {
static std::vector<uint8_t> value =
base::Base64Decode(kFakeEcPublicValueOctetStringDerBase64).value();
return value;
}
const Pkcs11Id& GetEcPkcs11Id() {
static Pkcs11Id value(base::Base64Decode(kFakeEcPkcs11IdBase64).value());
return value;
}
const PublicKeySpki& GetEcSpki() {
static PublicKeySpki value(base::Base64Decode(kFakeEcSpkiBase64).value());
return value;
}
const CertDer& GetCertDer() {
static CertDer value(base::Base64Decode(kFakeCertificate).value());
return value;
}
bool SpanEqual(base::span<const uint8_t> s1, base::span<const uint8_t> s2) {
return base::ranges::equal(s1, s2);
}
[[nodiscard]] bool FindAttribute(chaps::AttributeList attrs,
uint32_t attribute_type,
base::span<const uint8_t> value) {
for (int i = 0; i < attrs.attributes_size(); ++i) {
const chaps::Attribute& cur_attr = attrs.attributes(i);
if (cur_attr.type() != attribute_type) {
continue;
}
// There shouldn't be two attributes with the same type and different
// values, if this one is not the one, return false;
if (!cur_attr.has_length() || !cur_attr.has_value()) {
return false;
}
return SpanEqual(base::as_byte_span(cur_attr.value()), value);
}
return false;
}
// `T` must be a simple type, i.e. no internal pointers, etc.
// `value` must outlive the returned span.
template <typename T>
base::span<const uint8_t> MakeSpan(T* value) {
return base::as_bytes(base::span<T>(value, /*count=*/1u));
}
void AddAttribute(chaps::AttributeList& attr_list,
chromeos::PKCS11_CK_ATTRIBUTE_TYPE type,
base::span<const uint8_t> data) {
chaps::Attribute* new_attr = attr_list.add_attributes();
new_attr->set_type(type);
new_attr->set_value(std::string(data.begin(), data.end()));
new_attr->set_length(data.size());
}
chaps::AttributeList GetRsaKeyAttrs(base::span<const uint8_t> pkcs11_id,
base::span<const uint8_t> modulus,
base::span<const uint8_t> exponent) {
chaps::AttributeList rsa_attrs;
AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_ID, pkcs11_id);
AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_MODULUS, modulus);
AddAttribute(rsa_attrs, chromeos::PKCS11_CKA_PUBLIC_EXPONENT, exponent);
return rsa_attrs;
}
chaps::AttributeList GetEcKeyAttrs(base::span<const uint8_t> pkcs11_id,
base::span<const uint8_t> ec_point) {
chaps::AttributeList ec_attrs;
AddAttribute(ec_attrs, chromeos::PKCS11_CKA_ID, pkcs11_id);
AddAttribute(ec_attrs, chromeos::PKCS11_CKA_EC_POINT, ec_point);
return ec_attrs;
}
chaps::AttributeList GetFakeRsaPublicKeyAttrs() {
chaps::AttributeList result;
AddAttribute(result, chromeos::PKCS11_CKA_MODULUS, GetRsaModulus());
AddAttribute(result, chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
GetRsaPublicExponent());
return result;
}
chaps::AttributeList GetFakeEcPublicKeyAttrs() {
chaps::AttributeList result;
AddAttribute(result, chromeos::PKCS11_CKA_EC_POINT, GetEcPublicValue());
return result;
}
chaps::AttributeList GetFakeKeyInfoAttrs(
bool is_in_software,
chromeos::PKCS11_CK_KEY_TYPE pkcs_key_type,
const std::string& label) {
chaps::AttributeList result;
AddAttribute(result, chaps::kKeyInSoftwareAttribute,
MakeSpan(&is_in_software));
AddAttribute(result, chromeos::PKCS11_CKA_KEY_TYPE, MakeSpan(&pkcs_key_type));
AddAttribute(result, chromeos::PKCS11_CKA_LABEL, base::as_byte_span(label));
return result;
}
chaps::AttributeList GetFakeCertAttrs(const Pkcs11Id& pkcs11_id,
const std::string& label,
const CertDer& cert_der) {
chaps::AttributeList result;
AddAttribute(result, chromeos::PKCS11_CKA_ID, pkcs11_id.value());
AddAttribute(result, chromeos::PKCS11_CKA_LABEL, base::as_byte_span(label));
AddAttribute(result, chromeos::PKCS11_CKA_VALUE, cert_der.value());
return result;
}
std::vector<uint8_t> StrToBytes(const std::string& str) {
return std::vector<uint8_t>(str.begin(), str.end());
}
class ScopedNotificationsObserver : public net::CertDatabase::Observer {
public:
ScopedNotificationsObserver() {
net::CertDatabase::GetInstance()->AddObserver(this);
}
~ScopedNotificationsObserver() override {
net::CertDatabase::GetInstance()->RemoveObserver(this);
}
void OnClientCertStoreChanged() override { ++counter_; }
size_t counter_ = 0;
};
class KcerTokenImplTest : public testing::Test {
public:
KcerTokenImplTest() : token_(Token::kUser, &chaps_client_) {
ON_CALL(chaps_client_, FindObjects)
.WillByDefault(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
}
void TearDown() override {
// Check the notifications about cert changes. If a test doesn't configure
// anything, then by default it is expected to not emit any notifications.
EXPECT_EQ(notifications_observer_.counter_, expected_notifications_count_);
}
protected:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::MainThreadType::UI};
ScopedNotificationsObserver notifications_observer_;
size_t expected_notifications_count_ = 0;
SessionChapsClient::SlotId pkcs11_slot_id_{1};
MockHighLevelChapsClient chaps_client_;
KcerTokenImpl token_;
};
// Test that GenerateRsaKey can successfully generate a key pair.
TEST_F(KcerTokenImplTest, GenerateRsaKeySuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> mechanism_attrs;
chaps::AttributeList public_key_attrs;
EXPECT_CALL(chaps_client_,
GenerateKeyPair(pkcs11_slot_id_,
chromeos::PKCS11_CKM_RSA_PKCS_KEY_PAIR_GEN,
mechanism_attrs, _, _, _))
.WillOnce(DoAll(MoveArg<3>(&public_key_attrs),
RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle, result_code)));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, result_pub_key_handle, _, _))
.WillOnce(RunOnceCallback<3>(GetFakeRsaPublicKeyAttrs(), result_code));
chaps::AttributeList pkcs11_id_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handles, _, _))
.WillOnce(
DoAll(MoveArg<2>(&pkcs11_id_attrs), RunOnceCallback<3>(result_code)));
RsaModulusLength modulus_length_enum = RsaModulusLength::k2048;
chromeos::PKCS11_CK_ULONG modulus_length_bits =
static_cast<uint32_t>(modulus_length_enum);
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(modulus_length_enum,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_MODULUS_BITS,
MakeSpan(&modulus_length_bits)));
EXPECT_TRUE(FindAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get()->GetToken(), Token::kUser);
EXPECT_TRUE(
SpanEqual(waiter.Get()->GetPkcs11Id().value(), GetRsaPkcs11Id().value()));
EXPECT_TRUE(SpanEqual(waiter.Get()->GetSpki().value(), GetRsaSpki().value()));
}
// Test that GenerateRsaKey correctly sets attributes for a software backed key
// pair.
TEST_F(KcerTokenImplTest, GenerateRsaKeySoftwareBacked) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList private_key_attrs;
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(DoAll(MoveArg<4>(&private_key_attrs),
RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
chromeos::PKCS11_CKR_GENERAL_ERROR)));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/false, waiter.GetCallback());
chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
// The rest is not important for this test.
EXPECT_FALSE(waiter.Get().has_value());
}
// Test that GenerateRsaKey correctly fails when the generation of a key pair
// fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToGenerate) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToGenerateKey);
}
// Test that GenerateRsaKey retries several times when generation of a key pair
// fails with a session error.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetryGenerateOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(
ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GenerateRsaKey correctly fails when the reading of public key
// attributes fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToReadPublicKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToExportPublicKey);
}
// Test that GenerateRsaKey retries several times when reading of public key
// attributes fails with a session error. GenerateRsaKey has to retry all the
// previous methods.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetryReadAttrsOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GenerateRsaKey correctly fails when the writing of the id on the
// public and private keys fails.
TEST_F(KcerTokenImplTest, GenerateRsaKeyFailToSetId) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.WillOnce(RunOnceCallback<3>(GetFakeRsaPublicKeyAttrs(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
.WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}
// Test that GenerateRsaKey retries several times when the writing of the id on
// the public and private keys fails with a session error. GenerateRsaKey has to
// retry all the previous methods.
TEST_F(KcerTokenImplTest, GenerateRsaKeyRetrySetIdOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(GetFakeRsaPublicKeyAttrs(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GenerateEcKey can successfully generate a key pair.
TEST_F(KcerTokenImplTest, GenerateEcKeySuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> mechanism_attrs;
chaps::AttributeList public_key_attrs;
EXPECT_CALL(
chaps_client_,
GenerateKeyPair(pkcs11_slot_id_, chromeos::PKCS11_CKM_EC_KEY_PAIR_GEN,
mechanism_attrs, _, _, _))
.WillOnce(DoAll(MoveArg<3>(&public_key_attrs),
RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle, result_code)));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, result_pub_key_handle, _, _))
.WillOnce(RunOnceCallback<3>(GetFakeEcPublicKeyAttrs(), result_code));
chaps::AttributeList pkcs11_id_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handles, _, _))
.WillOnce(
DoAll(MoveArg<2>(&pkcs11_id_attrs), RunOnceCallback<3>(result_code)));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_TRUE(FindAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
EXPECT_TRUE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get()->GetToken(), Token::kUser);
EXPECT_TRUE(
SpanEqual(waiter.Get()->GetPkcs11Id().value(), GetEcPkcs11Id().value()));
EXPECT_TRUE(SpanEqual(waiter.Get()->GetSpki().value(), GetEcSpki().value()));
}
// Test that GenerateEcKey correctly sets attributes for a software backed key
// pair.
TEST_F(KcerTokenImplTest, GenerateEcKeySoftwareBacked) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList private_key_attrs;
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(DoAll(MoveArg<4>(&private_key_attrs),
RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
chromeos::PKCS11_CKR_GENERAL_ERROR)));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/false, waiter.GetCallback());
chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
// The rest is not important for this test.
EXPECT_FALSE(waiter.Get().has_value());
}
// Test that GenerateEcKey correctly fails when the generation of a key pair
// fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToGenerate) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(ObjectHandle(), ObjectHandle(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToGenerateKey);
}
// Test that GenerateEcKey retries several times when generation of a key pair
// fails with a session error.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetryGenerateOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(
ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GenerateEcKey correctly fails when the reading of public key
// attributes fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToReadPublicKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToExportPublicKey);
}
// Test that GenerateEcKey retries several times when reading of public key
// attributes fails with a session error. GenerateEcKey has to retry all the
// previous methods.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetryReadAttrsOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GenerateEcKey correctly fails when the writing of the id on the
// public and private keys fails.
TEST_F(KcerTokenImplTest, GenerateEcKeyFailToSetId) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillOnce(RunOnceCallback<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.WillOnce(RunOnceCallback<3>(GetFakeEcPublicKeyAttrs(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
.WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, key_handles, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}
// Test that GenerateEcKey retries several times when the writing of the id on
// the public and private keys fails with a session error. GenerateEcKey has to
// retry all the previous methods.
TEST_F(KcerTokenImplTest, GenerateEcKeyRetrySetIdOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle result_pub_key_handle{10};
ObjectHandle result_priv_key_handle{20};
std::vector<ObjectHandle> key_handles(
{result_pub_key_handle, result_priv_key_handle});
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(result_pub_key_handle,
result_priv_key_handle,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(GetFakeEcPublicKeyAttrs(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handles, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<PublicKey, Error>> waiter;
token_.GenerateEcKey(EllipticCurve::kP256,
/*hardware_backed=*/true, waiter.GetCallback());
EXPECT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey correctly fails when it's given an invalid key.
TEST_F(KcerTokenImplTest, ImportKeyBadKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
Pkcs8PrivateKeyInfoDer invalid_key({1, 2, 3, 4, 5});
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(invalid_key, import_key_waiter.GetCallback());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToParseKey);
}
// Test that ImportKey can successfully import an RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// ImportKey() will check whether the key already exists.
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK)));
chaps::AttributeList private_key_attrs;
chaps::AttributeList public_key_attrs;
EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&private_key_attrs),
RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
.WillOnce(
DoAll(MoveArg<1>(&public_key_attrs),
RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_TRUE(import_key_waiter.Get().has_value());
const PublicKey& kcer_public_key = import_key_waiter.Get().value();
// Check that ImportKey() was looking for the correct existing key.
const chromeos::PKCS11_CK_OBJECT_CLASS kPrivKeyClass =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
// Check the private key attributes.
constexpr chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_RSA;
// At the moment of writing these key components were printed from the code
// under test, i.e. not guaranteed to be correct. The code was also tested
// against the real Chaps and was able to correctly sign using the imported
// key client_1.key, so most likely they are correct. Long term this is a
// regression test.
std::vector<uint8_t> private_exponent =
base::Base64Decode(
"BMvoYMcg2WGDuESZZ5u6nn0eZUlT4329H6ECQzg/KTEvOGydhqUF6eD4B/"
"vnsZ6POrVFSaZK76EtgukbJUcqcee0b1yljrDyvCXaoojgHjFcaa90HE/"
"Gvvm++AcXoZfwX826cILQtQK2OCK4EPTDY+U+6LYtaVruZZDTVxgr7V+v4v1EDKEjQc+"
"Ttupwo6aXSeiTKuqNsXuodoDvcv/"
"uJgzMDCxi14TZTjaWOz7Xw2JZ+NLbTrsiqTyzmyJousV6/+4sfTYt8/"
"tz0gMt3Qaddvs+"
"BpTTrYIKTpsGYwPkKDqUdEkC87OQ6a2mXB1lpA7FMpZiiyJ1HpIXHkd+eLoNkQ==")
.value();
std::vector<uint8_t> prime_1 =
base::Base64Decode(
"5ULWXU5R7tGCzNlpTkRWD/M8sf/"
"ZOq12VVWSzQXGWsxFJHIFyezEvKV4nol6OlCRH9DzQgmNtaKDjD6uiyNDTRf8g270/"
"UYu3kTWUWaLFyA2gde0vd74GeFJK4uySb9wcfldlsJdz9v4NPNLIasfGWbINWkKqSqL/"
"arGr8lwLhk=")
.value();
std::vector<uint8_t> prime_2 =
base::Base64Decode(
"9IIdj/q7dI0PUpyfBExsxQF5oWcreJsvrwbhV/"
"LRbwcsTIlCMc7Jh8QgOrXf+iglj8KKN8YXu6//"
"jRxBXupPTUdyoKxzqxxXAqjgB1mD7jwimhtMnGxBd7Y87g22VCcx+"
"FgYEAxLD5cA2BKZEdexV+rCDGgDzEb5TnGUGap10Vk=")
.value();
std::vector<uint8_t> exponent_1 =
base::Base64Decode(
"urdPnNhPlGAf1jRvNmYjbYQdd562zbo+eMtj7wR4ArUAzujqXAUwSa++Z+fxmxLIzw+/"
"PpZHSpnb51mZkAodIumZJ3Yzox8Ixs9reQo515DNs7v5IPY6O+GmVQfGIZf/"
"vWNpXIJaIxK0uHM5SmdywZ5bClzNaO8U6niurrYxXek=")
.value();
std::vector<uint8_t> exponent_2 =
base::Base64Decode(
"gGh9AgpZvCIAtBAQ6v7/"
"+I6HxB4clGBbsH3ahoe9OaP4vdEv9Fx3Nlfn3S17DTNcVp2CXTwpZqZNfVwjcKd5Mkqd"
"hohKzsg5YeoyjWmTgeAPBAPmPhgUYbxRT2vgH13ePmB1cqgiG3PgO5m4zcgLGPLvKfjO"
"Vc/ISkwXzUraSTE=")
.value();
std::vector<uint8_t> coefficient =
base::Base64Decode(
"fpgE21erI8NeCrK9GSNm3TUjZlifaIgcGEfINYTmPRCOHMXaQukogBI7W45Oxq6sCE+"
"7JRNH7wRe7Y5I/"
"dGhonlu7BRvzIvZvxDTvlYJKn55d1TpmYRh1+cpf7rcj1Aha9MWxERnJsVHi0+"
"CxAkar8vyM0jEPEb0PQ/UjZnGt90=")
.value();
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
MakeSpan(&key_type)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXTRACTABLE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_UNWRAP,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_DECRYPT,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(
private_key_attrs, chromeos::PKCS11_CKA_SIGN_RECOVER, MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_MODULUS,
GetRsaModulus()));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(private_key_attrs,
chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
GetRsaPublicExponent()));
EXPECT_TRUE(FindAttribute(private_key_attrs,
chromeos::PKCS11_CKA_PRIVATE_EXPONENT,
private_exponent));
EXPECT_TRUE(
FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIME_1, prime_1));
EXPECT_TRUE(
FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIME_2, prime_2));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXPONENT_1,
exponent_1));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXPONENT_2,
exponent_2));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_COEFFICIENT,
coefficient));
// Check the public key attributes.
chromeos::PKCS11_CK_OBJECT_CLASS pub_key_class =
chromeos::PKCS11_CKO_PUBLIC_KEY;
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&pub_key_class)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
MakeSpan(&key_type)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_TOKEN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_WRAP,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ENCRYPT,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_MODULUS,
GetRsaModulus()));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(public_key_attrs,
chromeos::PKCS11_CKA_PUBLIC_EXPONENT,
GetRsaPublicExponent()));
EXPECT_TRUE(FindAttribute(public_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
// Check returned kcer::PublicKey.
EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(kcer_public_key.GetSpki(), GetRsaSpki());
EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}
// Test that ImportKey skips the import and succeeds when it's given an already
// imported RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaAlreadyExists) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_TRUE(import_key_waiter.Get().has_value());
const PublicKey& kcer_public_key = import_key_waiter.Get().value();
EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(kcer_public_key.GetSpki(), GetRsaSpki());
EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}
// Test that ImportKey correctly fails when Chaps fails to search for the
// existing RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToSearchExistingKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ImportKey retries several times when Chaps fails to search for the
// existing RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToSearchExistingKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey correctly fails when Chaps fails to create the private
// RSA key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToCreatePrivKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}
// Test that ImportKey retries several times when Chaps fails to create the
// private RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToCreatePrivKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey correctly fails when Chaps fails to create the public RSA
// key.
TEST_F(KcerTokenImplTest, ImportKeyRsaFailToCreatePubKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
ObjectHandle priv_key_handle(10);
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(RunOnceCallback<2>(priv_key_handle, chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, DestroyObjectsWithRetries(
pkcs11_slot_id_,
std::vector<ObjectHandle>{priv_key_handle}, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}
// Test that ImportKey retries several times when Chaps fails to create the
// public RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyRsaRetryToCreatePubKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
// Alternates between replying with OK and SESSION_CLOSED to handle
// alternating calls for private and public keys.
auto fake_create_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_pub_key = true;
bool is_pub_key =
std::exchange(next_is_pub_key, /*new_value=*/!next_is_pub_key);
if (is_pub_key) {
return std::move(callback).Run(ObjectHandle(10), chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(ObjectHandle(0),
chromeos::PKCS11_CKR_SESSION_CLOSED);
}
};
EXPECT_CALL(chaps_client_, CreateObject)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_create_objects));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey can successfully import an EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK)));
chaps::AttributeList private_key_attrs;
chaps::AttributeList public_key_attrs;
EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&private_key_attrs),
RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
.WillOnce(
DoAll(MoveArg<1>(&public_key_attrs),
RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_TRUE(import_key_waiter.Get().has_value());
const PublicKey& kcer_public_key = import_key_waiter.Get().value();
const chromeos::PKCS11_CK_OBJECT_CLASS kPrivKeyClass =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
// Check the private key attributes.
constexpr chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
chromeos::PKCS11_CK_KEY_TYPE key_type = chromeos::PKCS11_CKK_EC;
// At the moment of writing these key components were printed from the code
// under test, i.e. not guaranteed to be correct. The code was also tested
// against the real Chaps and was able to correctly sign using the imported
// key key_usage_p256.key, so most likely they are correct. Long term this is
// a regression test.
std::vector<uint8_t> priv_key_bytes =
base::Base64Decode("eor3507Mmwvc9idKsuXWTudE0GDlVmEc64H7acez5xQ=")
.value();
std::vector<uint8_t> ec_params_der =
base::Base64Decode("BggqhkjOPQMBBw==").value();
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
MakeSpan(&key_type)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_TOKEN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SENSITIVE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EXTRACTABLE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_SIGN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(
private_key_attrs, chromeos::PKCS11_CKA_SIGN_RECOVER, MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_DERIVE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_VALUE,
priv_key_bytes));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EC_POINT,
GetEcPublicValue()));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_PRIVATE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(private_key_attrs, chromeos::PKCS11_CKA_EC_PARAMS,
ec_params_der));
// Check the public key attributes.
chromeos::PKCS11_CK_OBJECT_CLASS pub_key_class =
chromeos::PKCS11_CKO_PUBLIC_KEY;
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&pub_key_class)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_KEY_TYPE,
MakeSpan(&key_type)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_TOKEN,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_VERIFY,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_DERIVE,
MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_EC_PARAMS,
ec_params_der));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_EC_POINT,
GetEcPublicValue()));
EXPECT_TRUE(FindAttribute(public_key_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(public_key_attrs, chaps::kForceSoftwareAttribute,
MakeSpan(&kTrue)));
// Check returned kcer::PublicKey.
EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetEcPkcs11Id());
EXPECT_EQ(kcer_public_key.GetSpki(), GetEcSpki());
EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}
// Test that ImportKey skips the import and succeeds when it's given an already
// imported EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcAlreadyExists) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_TRUE(import_key_waiter.Get().has_value());
const PublicKey& kcer_public_key = import_key_waiter.Get().value();
EXPECT_EQ(kcer_public_key.GetPkcs11Id(), GetEcPkcs11Id());
EXPECT_EQ(kcer_public_key.GetSpki(), GetEcSpki());
EXPECT_EQ(kcer_public_key.GetToken(), Token::kUser);
}
// Test that ImportKey correctly fails when Chaps fails to search for the
// existing EC key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToSearchExistingKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ImportKey retries several times when Chaps fails to search for the
// existing EC key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToSearchExistingKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey correctly fails when Chaps fails to create the private EC
// key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToCreatePrivKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}
// Test that ImportKey retries several times when Chaps fails to create the
// private RSA key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToCreatePrivKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportKey correctly fails when Chaps fails to create the public EC
// key.
TEST_F(KcerTokenImplTest, ImportKeyEcFailToCreatePubKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
ObjectHandle priv_key_handle(10);
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(RunOnceCallback<2>(priv_key_handle, chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, DestroyObjectsWithRetries(
pkcs11_slot_id_,
std::vector<ObjectHandle>{priv_key_handle}, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kFailedToImportKey);
}
// Test that ImportKey retries several times when Chaps fails to create the
// public EC key with a session error.
TEST_F(KcerTokenImplTest, ImportKeyEcRetryToCreatePubKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
// Alternates between replying with OK and SESSION_CLOSED to handle
// alternating calls for private and public keys.
auto fake_create_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_pub_key = true;
bool is_pub_key =
std::exchange(next_is_pub_key, /*new_value=*/!next_is_pub_key);
if (is_pub_key) {
return std::move(callback).Run(ObjectHandle(10), chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(ObjectHandle(0),
chromeos::PKCS11_CKR_SESSION_CLOSED);
}
};
EXPECT_CALL(chaps_client_, CreateObject)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_create_objects));
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportCertFromBytes can successfully import a cert.
TEST_F(KcerTokenImplTest, ImportCertFromBytesSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
// Return empty list of existing certs.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK))
// Simulate that a key for the cert exists.
.WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&cert_attrs),
RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
EXPECT_TRUE(waiter.Get().has_value());
chromeos::PKCS11_CK_OBJECT_CLASS cert_class =
chromeos::PKCS11_CKO_CERTIFICATE;
chromeos::PKCS11_CK_CERTIFICATE_TYPE cert_type = chromeos::PKCS11_CKC_X_509;
chromeos::PKCS11_CK_BBOOL kTrue = chromeos::PKCS11_CK_TRUE;
// The label comes from the client_1.pem file, see the generating script
// //net/data/ssl/scripts/generate-client-certificates.sh for details.
const std::string kExpectedLabel = "Client Cert A";
// Contains "CN=B CA".
std::vector<uint8_t> issuer_name_der =
base::Base64Decode("MA8xDTALBgNVBAMMBEIgQ0E=").value();
// Contains "CN=Client Cert A".
std::vector<uint8_t> subject_name_der =
base::Base64Decode("MBgxFjAUBgNVBAMMDUNsaWVudCBDZXJ0IEE=").value();
// Contains 4096.
std::vector<uint8_t> serial_number_der =
base::Base64Decode("AgIQAA==").value();
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&cert_class)));
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_CERTIFICATE_TYPE,
MakeSpan(&cert_type)));
EXPECT_TRUE(
FindAttribute(cert_attrs, chromeos::PKCS11_CKA_TOKEN, MakeSpan(&kTrue)));
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_LABEL,
base::as_byte_span(kExpectedLabel)));
EXPECT_TRUE(
FindAttribute(cert_attrs, chromeos::PKCS11_CKA_VALUE, cert.value()));
EXPECT_TRUE(
FindAttribute(cert_attrs, chromeos::PKCS11_CKA_ISSUER, issuer_name_der));
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_SUBJECT,
subject_name_der));
EXPECT_TRUE(FindAttribute(cert_attrs, chromeos::PKCS11_CKA_SERIAL_NUMBER,
serial_number_der));
}
// Test that ImportCertFromBytes correctly fails when the key for the cert is
// not present.
TEST_F(KcerTokenImplTest, ImportCertFromBytesKeyNotFound) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
// Return empty list of existing certs.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK))
// Return empty list of found keys.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kKeyNotFound);
}
// Test that ImportCertFromBytes returns success without importing a cert when
// it already exists.
TEST_F(KcerTokenImplTest, ImportCertFromBytesAlreadyExists) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
// Return some handles to simulate that an existing cert was found.
std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
EXPECT_TRUE(waiter.Get().has_value());
}
// Test that ImportCertFromBytes correctly fails when the cert is invalid.
TEST_F(KcerTokenImplTest, ImportCertFromBytesBadCert) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
// Corrupt the data.
cert.value().pop_back();
std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
// Return empty list of existing certs.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject).Times(0);
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kInvalidCertificate);
}
// Test that ImportCertFromBytes correctly fails when it fails to fetch search
// for the existing cert.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToSearchForCert) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ImportCertFromBytes correctly fails when it fails to fetch search
// for the existing key.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToSearchForKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
// Return some handles to simulate that an existing cert was found.
std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
// Return empty list of existing certs.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK))
// Return an error when searching for the key.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ImportCertFromBytes correctly fails when it fails to create a cert
// object.
TEST_F(KcerTokenImplTest, ImportCertFromBytesFailToCreate) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
std::vector<ObjectHandle> result_handles{ObjectHandle(10)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
// Return empty list of existing certs.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK))
// Simulate that a key for the cert exists.
.WillOnce(RunOnceCallback<2>(result_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToImportCertificate);
}
// Test that ImportCertFromBytes retries several times when Chaps fails to
// search for the cert with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToSearchForCert) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportCertFromBytes retries several times when Chaps fails to
// search for the key with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToSearchForKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
// Alternates between replying with OK and SESSION_CLOSED to handle
// alternating calls for existing certs and keys.
auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_cert = true;
bool is_cert = std::exchange(next_is_cert, /*new_value=*/!next_is_cert);
if (is_cert) {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_SESSION_CLOSED);
}
};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_find_objects));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportCertFromBytes retries several times when Chaps fails to
// create a new cert with a session error.
TEST_F(KcerTokenImplTest, ImportCertFromBytesRetryToCreateCert) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
// Alternates between replying with empty and non-empty handles to process
// alternating calls for existing certs and keys.
auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_cert = true;
bool is_cert = std::exchange(next_is_cert, /*new_value=*/!next_is_cert);
if (is_cert) {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK);
}
};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_find_objects));
EXPECT_CALL(chaps_client_, CreateObject)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
ObjectHandle(0), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.ImportCertFromBytes(CertDer(cert.value()), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ImportPkcs12Cert can successfully import a PKCS#12 file. Most of
// the implementation is shared with KcerTokenImplNss, is covered by the tests
// for it and is not covered again. TODO(miersh): After KcerTokenImplNss is
// removed, those tests should be moved here.
TEST_F(KcerTokenImplTest, ImportPkcs12CertSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(2)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
Pkcs12Blob pkcs12_data(ReadTestFile("client.p12"));
std::string password("12345");
chaps::AttributeList private_key_attrs;
chaps::AttributeList public_key_attrs;
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&private_key_attrs),
RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK)))
.WillOnce(
DoAll(MoveArg<1>(&public_key_attrs),
RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK)))
.WillOnce(
DoAll(MoveArg<1>(&cert_attrs),
RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK)));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
token_.ImportPkcs12Cert(pkcs12_data, password, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
expected_notifications_count_ = 1;
}
// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// PKCS#11 id.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsByIdSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
chaps::AttributeList pkcs11_id_attrs;
AddAttribute(pkcs11_id_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value());
// These ids represent all the objects that are related to `public_key` and
// should be deleted.
std::vector<ObjectHandle> result_object_list{
ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)))
// Kcer will try to update its cache after a cert removal, prepare a
// reply.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(waiter.Get().has_value());
expected_notifications_count_ = 1;
}
// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// SPKI for RSA keys.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiRsaSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// These ids represent all the objects that should be deleted.
std::vector<ObjectHandle> result_object_list{
ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)))
// Kcer will try to update its cache after a cert removal, prepare a
// reply.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(GetRsaSpki()),
waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(waiter.Get().has_value());
expected_notifications_count_ = 1;
}
// Test that RemoveKeyAndCerts can successfully remove a key pair and certs by
// SPKI for EC keys.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiEcSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// These ids represent all the objects that should be deleted.
std::vector<ObjectHandle> result_object_list{
ObjectHandle(10), ObjectHandle(20), ObjectHandle(30)};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)))
// Kcer will try to update its cache after a cert removal, prepare a
// reply.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(GetEcSpki()), waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
EXPECT_TRUE(waiter.Get().has_value());
expected_notifications_count_ = 1;
}
// Test that RemoveKeyAndCerts correctly fails when it cannot recover PKCS#11
// from the provided SPKI.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsBySpkiFail) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKeySpki bad_spki({1, 2, 3, 4, 5}); // Not a valid SPKI.
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(bad_spki), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToGetPkcs11Id);
}
// Test that RemoveKeyAndCerts correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsFailToSearch) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that RemoveKeyAndCerts retries several times when the search for objects
// fails with a session error.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsRetrySearchOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that RemoveKeyAndCerts correctly fails when the removal of objects
// fails.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsFailToDestroy) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(result_object_list, chromeos::PKCS11_CKR_OK))
// Kcer will try to update its cache after a cert removal, prepare a reply
// (even though an error occurred, chaps still might have removed
// something).
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToRemoveObjects);
expected_notifications_count_ = 1;
}
// Test that RemoveKeyAndCerts retries several times when the removal of objects
// fails with a session error. RemoveKeyAndCerts has to retry all the previous
// methods.
TEST_F(KcerTokenImplTest, RemoveKeyAndCertsRetryDestroyOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key), waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that RemoveCert can successfully remove a cert.
TEST_F(KcerTokenImplTest, RemoveCertSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(GetCertDer().value());
scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);
// Contains the ids of found certs that should be deleted.
std::vector<ObjectHandle> cert_handles{ObjectHandle(10)};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(cert_handles, result_code)))
// Kcer will try to update its cache after a cert removal, prepare a
// reply.
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(), result_code));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, cert_handles, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveCert(cert, waiter.GetCallback());
EXPECT_TRUE(waiter.Get().has_value());
CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&cert_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_VALUE,
GetCertDer().value()));
expected_notifications_count_ = 1;
}
// Test that RemoveCert correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, RemoveCertFailToSearch) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(GetCertDer().value());
scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveCert(cert, waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that RemoveCert retries several times when the search for objects
// fails with a session error.
TEST_F(KcerTokenImplTest, RemoveCertRetrySearchOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(GetCertDer().value());
scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveCert(cert, waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that RemoveCert correctly fails when the removal of objects
// fails.
TEST_F(KcerTokenImplTest, RemoveCertFailToDestroy) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(GetCertDer().value());
scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);
std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(result_object_list, chromeos::PKCS11_CKR_OK))
// Kcer will try to update its cache after a cert removal, prepare a reply
// (even though an error occurred, chaps still might have removed
// something).
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.WillOnce(RunOnceCallback<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveCert(cert, waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToRemoveObjects);
expected_notifications_count_ = 1;
}
// Test that RemoveCert retries several times when the removal of objects
// fails with a session error. RemoveCert has to retry all the previous
// methods.
TEST_F(KcerTokenImplTest, RemoveCertRetryDestroyOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytes(GetCertDer().value());
scoped_refptr<const Cert> cert = base::MakeRefCounted<Cert>(
Token::kUser, GetRsaPkcs11Id(), /*nickname=*/"", x509_cert);
std::vector<ObjectHandle> result_object_list{ObjectHandle(1)};
uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
DestroyObjectsWithRetries(pkcs11_slot_id_, result_object_list, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_code));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.RemoveCert(cert, waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ListKeys can successfully list keys when there are no keys.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithNoKeys) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<ObjectHandle> result_object_list{};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(2)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get().value().empty());
}
// Test that ListKeys can successfully list keys when there is one RSA key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithOneRsaKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle rsa_handle{1};
std::vector<ObjectHandle> rsa_handles{rsa_handle};
std::vector<ObjectHandle> ec_handles{};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(2)
.WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK));
chaps::AttributeList rsa_attrs = GetRsaKeyAttrs(
GetRsaPkcs11Id().value(), GetRsaModulus(), GetRsaPublicExponent());
EXPECT_CALL(
chaps_client_,
GetAttributeValue(pkcs11_slot_id_, rsa_handle,
std::vector<AttributeId>{AttributeId::kPkcs11Id,
AttributeId::kModulus,
AttributeId::kPublicExponent},
_))
.WillOnce(RunOnceCallback<3>(rsa_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 1u);
PublicKey pub_key = waiter.Get().value().front();
EXPECT_EQ(pub_key.GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(pub_key.GetSpki(), GetRsaSpki());
EXPECT_EQ(pub_key.GetToken(), Token::kUser);
}
// Test that ListKeys can successfully list keys when there is one EC key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithOneEcKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle ec_handle{1};
std::vector<ObjectHandle> rsa_handles{};
std::vector<ObjectHandle> ec_handles{ec_handle};
std::vector<ObjectHandle> priv_key_handles{ObjectHandle(2)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(3)
.WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK))
// For each EC handle `token_` will check that the private key exists.
.WillOnce(RunOnceCallback<2>(priv_key_handles, chromeos::PKCS11_CKR_OK));
chaps::AttributeList ec_attrs =
GetEcKeyAttrs(GetEcPkcs11Id().value(), GetEcPublicValue());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, ec_handle,
std::vector<AttributeId>{AttributeId::kPkcs11Id,
AttributeId::kEcPoint},
_))
.WillOnce(RunOnceCallback<3>(ec_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 1u);
PublicKey pub_key = waiter.Get().value().front();
EXPECT_EQ(pub_key.GetPkcs11Id(), GetEcPkcs11Id());
EXPECT_EQ(pub_key.GetSpki(), GetEcSpki());
EXPECT_EQ(pub_key.GetToken(), Token::kUser);
}
// Test that ListKeys can successfully list keys when there is one RSA and one
// EC key.
TEST_F(KcerTokenImplTest, ListKeysSuccessWithTwoKeys) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle rsa_handle{1};
ObjectHandle ec_handle{2};
std::vector<ObjectHandle> rsa_handles{rsa_handle};
std::vector<ObjectHandle> ec_handles{ec_handle};
std::vector<ObjectHandle> priv_key_handles{ObjectHandle(3)};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(3)
.WillOnce(RunOnceCallback<2>(rsa_handles, chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ec_handles, chromeos::PKCS11_CKR_OK))
// For each EC handle `token_` will check that the private key exists.
.WillOnce(RunOnceCallback<2>(priv_key_handles, chromeos::PKCS11_CKR_OK));
chaps::AttributeList rsa_attrs = GetRsaKeyAttrs(
GetRsaPkcs11Id().value(), GetRsaModulus(), GetRsaPublicExponent());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, rsa_handle, _, _))
.WillOnce(RunOnceCallback<3>(rsa_attrs, chromeos::PKCS11_CKR_OK));
chaps::AttributeList ec_attrs =
GetEcKeyAttrs(GetEcPkcs11Id().value(), GetEcPublicValue());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, ec_handle, _, _))
.WillOnce(RunOnceCallback<3>(ec_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 2u);
// The order is not guaranteed, but in practice should be stable.
PublicKey rsa_pub_key = waiter.Get().value().front();
PublicKey ec_pub_key = waiter.Get().value().back();
EXPECT_EQ(rsa_pub_key.GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(rsa_pub_key.GetSpki(), GetRsaSpki());
EXPECT_EQ(rsa_pub_key.GetToken(), Token::kUser);
EXPECT_EQ(ec_pub_key.GetPkcs11Id(), GetEcPkcs11Id());
EXPECT_EQ(ec_pub_key.GetSpki(), GetEcSpki());
EXPECT_EQ(ec_pub_key.GetToken(), Token::kUser);
}
// Test that ListKeys correctly skips invalid keys.
TEST_F(KcerTokenImplTest, ListKeysBadKeysAreSkipped) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// Same handles will be returned for RSA and EC keys, that's not realistic,
// but good enough for the test.
std::vector<ObjectHandle> result_object_list{ObjectHandle{1},
ObjectHandle{2}};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(2)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
chromeos::PKCS11_CKR_OK));
chaps::AttributeList bad_rsa_attrs = GetRsaKeyAttrs(
GetRsaPkcs11Id().value(),
std::vector<uint8_t>(GetRsaModulus().begin(), GetRsaModulus().end() - 4),
GetRsaPublicExponent());
chaps::AttributeList bad_ec_attrs =
GetEcKeyAttrs(GetEcPkcs11Id().value(),
std::vector<uint8_t>(GetEcPublicValue().begin(),
GetEcPublicValue().end() - 1));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(4)
.WillOnce(
RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<3>(bad_rsa_attrs, chromeos::PKCS11_CKR_OK))
.WillOnce(
RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<3>(bad_ec_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get().value().empty());
}
// Test that ListKeys correctly fails when Chaps fails to find RSA keys.
TEST_F(KcerTokenImplTest, ListKeysFailedToListRsaKeys) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<ObjectHandle> handles{};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(
RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ListKeys correctly fails when Chaps fails to find EC keys.
TEST_F(KcerTokenImplTest, ListKeysFailedToListEcKeys) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<ObjectHandle> handles{};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(2)
.WillOnce(RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_OK))
.WillOnce(
RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that ListKeys correctly retries when Chaps fails to find RSA keys with a
// session error.
TEST_F(KcerTokenImplTest, ListKeysRetryFindRsaOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<ObjectHandle> handles{};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
handles, chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ListKeys correctly retries when Chaps fails to retrieve attributes
// for RSA keys with a session error.
TEST_F(KcerTokenImplTest, ListKeysRetryGetRsaOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<ObjectHandle> handles{ObjectHandle(1)};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ListKeys correctly retries when Chaps fails to find EC keys with a
// session error.
TEST_F(KcerTokenImplTest, ListKeysRetryFindEcOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// Alternates between replying with OK and SESSION_CLOSED to handle
// alternating calls for RSA and EC keys.
auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_rsa = true;
bool is_rsa = std::exchange(next_is_rsa, /*new_value=*/!next_is_rsa);
if (is_rsa) {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_SESSION_CLOSED);
}
};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_find_objects));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ListKeys correctly retries when Chaps fails to retrieve attributes
// for EC keys with a session error.
TEST_F(KcerTokenImplTest, ListKeysRetryGetEcOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// Alternates between returning no handles and one handle to process
// alternating calls for RSA and EC keys.
auto fake_find_objects = [](auto slot_id, auto attrs, auto callback) {
static bool next_is_rsa = true;
bool is_rsa = std::exchange(next_is_rsa, /*new_value=*/!next_is_rsa);
if (is_rsa) {
return std::move(callback).Run(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK);
} else {
return std::move(callback).Run(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK);
}
};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(2 * kDefaultAttempts)
.WillRepeatedly(Invoke(fake_find_objects));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>> waiter;
token_.ListKeys(waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that ListCerts can successfully list certs when there are no certs.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithNoCerts) {
std::vector<ObjectHandle> result_object_list{};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(1)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(result_object_list,
chromeos::PKCS11_CKR_OK));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get().value().empty());
}
// Test that ListCerts can successfully list certs when there is one cert.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithOneCert) {
ObjectHandle cert_handle{1};
std::vector<ObjectHandle> cert_handles{cert_handle};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));
std::string nickname = "cert_nickname";
chaps::AttributeList cert_attrs =
GetFakeCertAttrs(GetRsaPkcs11Id(), nickname, GetCertDer());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle,
std::vector<AttributeId>{AttributeId::kPkcs11Id,
AttributeId::kLabel,
AttributeId::kValue},
_))
.WillOnce(RunOnceCallback<3>(cert_attrs, chromeos::PKCS11_CKR_OK));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 1u);
scoped_refptr<const Cert> cert = waiter.Get().value().front();
EXPECT_EQ(cert->GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(cert->GetNickname(), nickname);
EXPECT_EQ(cert->GetToken(), Token::kUser);
EXPECT_TRUE(SpanEqual(
net::x509_util::CryptoBufferAsSpan(cert->GetX509Cert()->cert_buffer()),
GetCertDer().value()));
}
// Test that KcerTokenImpl correct updates / doesn't update the cert cache for
// ListCerts().
TEST_F(KcerTokenImplTest, ListCertsCacheUpdate) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
// No certs initially, the cache will be up-to-date and empty.
{
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 0u);
}
// Check that the cache doesn't update itself unnecessary.
EXPECT_CALL(chaps_client_, FindObjects).Times(0);
// Still no certs, the cache is still empty.
{
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 0u);
}
// Prepare fake chaps client to return a cert.
ObjectHandle cert_handle{1};
std::vector<ObjectHandle> cert_handles{cert_handle};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));
std::string nickname = "cert_nickname";
chaps::AttributeList cert_attrs =
GetFakeCertAttrs(GetRsaPkcs11Id(), nickname, GetCertDer());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle,
std::vector<AttributeId>{AttributeId::kPkcs11Id,
AttributeId::kLabel,
AttributeId::kValue},
_))
.WillOnce(RunOnceCallback<3>(cert_attrs, chromeos::PKCS11_CKR_OK));
// Simulate a notification that certs changed.
net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
expected_notifications_count_++;
// CertDatabase sends the notifications asynchronously, so give it a chance to
// do that.
task_environment_.RunUntilIdle();
// Now the cache should update itself again and return one cert.
{
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 1u);
}
}
// Test that ListCerts can successfully list two certs.
TEST_F(KcerTokenImplTest, ListCertsSuccessWithTwoCerts) {
ObjectHandle cert_handle_1{1};
ObjectHandle cert_handle_2{2};
std::vector<ObjectHandle> cert_handles{cert_handle_1, cert_handle_2};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));
std::string nickname_1 = "cert_nickname_1";
chaps::AttributeList cert_attrs_1 =
GetFakeCertAttrs(GetRsaPkcs11Id(), nickname_1, GetCertDer());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle_1, _, _))
.WillOnce(RunOnceCallback<3>(cert_attrs_1, chromeos::PKCS11_CKR_OK));
// The second cert has to be different for Kcer to return them as two separate
// certs.
std::unique_ptr<net::CertBuilder> cert_issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(cert_issuer.get(), GetRsaSpki().value());
std::string nickname_2 = "cert_nickname_2";
CertDer cert_der_2(StrToBytes(cert_builder->GetDER()));
chaps::AttributeList cert_attrs_2 =
GetFakeCertAttrs(GetEcPkcs11Id(), nickname_2, cert_der_2);
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle_2, _, _))
.WillOnce(RunOnceCallback<3>(cert_attrs_2, chromeos::PKCS11_CKR_OK));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
ASSERT_EQ(waiter.Get().value().size(), 2u);
scoped_refptr<const Cert> cert_1 = waiter.Get().value().back();
scoped_refptr<const Cert> cert_2 = waiter.Get().value().front();
if (cert_1->GetPkcs11Id() != GetRsaPkcs11Id()) {
// The order is not guaranteed.
std::swap(cert_1, cert_2);
}
EXPECT_EQ(cert_1->GetPkcs11Id(), GetRsaPkcs11Id());
EXPECT_EQ(cert_1->GetNickname(), nickname_1);
EXPECT_EQ(cert_1->GetToken(), Token::kUser);
EXPECT_TRUE(SpanEqual(
net::x509_util::CryptoBufferAsSpan(cert_1->GetX509Cert()->cert_buffer()),
GetCertDer().value()));
EXPECT_EQ(cert_2->GetPkcs11Id(), GetEcPkcs11Id());
EXPECT_EQ(cert_2->GetNickname(), nickname_2);
EXPECT_EQ(cert_2->GetToken(), Token::kUser);
EXPECT_TRUE(cert_2->GetX509Cert()->EqualsIncludingChain(
cert_builder->GetX509Certificate().get()));
}
// Test that ListCerts correctly skips invalid certs.
TEST_F(KcerTokenImplTest, ListCertsBadCertsAreSkipped) {
ObjectHandle cert_handle_1{1};
ObjectHandle cert_handle_2{2};
std::vector<ObjectHandle> cert_handles{cert_handle_1, cert_handle_2};
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(RunOnceCallback<2>(cert_handles, chromeos::PKCS11_CKR_OK));
// Invalid because pkcs11_id is empty.
chaps::AttributeList cert_attrs_1 =
GetFakeCertAttrs(Pkcs11Id(), /*label=*/"", GetCertDer());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle_1, _, _))
.WillOnce(RunOnceCallback<3>(cert_attrs_1, chromeos::PKCS11_CKR_OK));
// Invalid because cert_der is empty.
chaps::AttributeList cert_attrs_2 =
GetFakeCertAttrs(GetEcPkcs11Id(), /*label=*/"", CertDer());
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, cert_handle_2, _, _))
.WillOnce(RunOnceCallback<3>(cert_attrs_2, chromeos::PKCS11_CKR_OK));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().value().size(), 0u);
}
// Test that ListCerts correctly fails when Chaps fails to find cert handles.
TEST_F(KcerTokenImplTest, ListCertsFailedToListObjects) {
std::vector<ObjectHandle> handles{};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(
RunOnceCallback<2>(handles, chromeos::PKCS11_CKR_GENERAL_ERROR));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get()->empty());
}
// Test that ListCerts correctly retries when Chaps fails to find cert handles
// with a session error.
TEST_F(KcerTokenImplTest, ListCertsRetryFindOjectsOnSessionError) {
std::vector<ObjectHandle> handles{};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
handles, chromeos::PKCS11_CKR_SESSION_CLOSED));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get()->empty());
}
// Test that ListCerts correctly retries when Chaps fails to retrieve attributes
// for a cert with a session error.
TEST_F(KcerTokenImplTest, ListCertsRetryGetCertOnSessionError) {
std::vector<ObjectHandle> handles{ObjectHandle(1)};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, GetAttributeValue)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
// Initialize late, so cache update can interact with the EXPECTs.
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
waiter;
token_.ListCerts(waiter.GetCallback());
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get()->empty());
}
// Test that DoesPrivateKeyExist can successfully check whether a private key
// exists when it exists.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistKeyExistsSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{ObjectHandle(10)};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList attrs;
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(DoAll(MoveArg<1>(&attrs),
RunOnceCallback<2>(result_object_list, result_code)));
base::test::TestFuture<base::expected<bool, Error>> waiter;
token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
waiter.GetCallback());
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(
FindAttribute(attrs, chromeos::PKCS11_CKA_ID, GetRsaPkcs11Id().value()));
ASSERT_TRUE(waiter.Get().has_value());
EXPECT_TRUE(waiter.Get().value());
}
// Test that DoesPrivateKeyExist can successfully check whether a private key
// exists when it doesn't exist.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistKeyDoesNotExistsSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_OK;
chaps::AttributeList attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&attrs),
RunOnceCallback<2>(result_object_list, result_code)));
base::test::TestFuture<base::expected<bool, Error>> waiter;
token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
waiter.GetCallback());
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(
FindAttribute(attrs, chromeos::PKCS11_CKA_ID, GetRsaPkcs11Id().value()));
ASSERT_TRUE(waiter.Get<0>().has_value());
EXPECT_FALSE(waiter.Get<0>().value());
}
// Test that DoesPrivateKeyExist correctly fails when the search for objects
// fails.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistFailToSearch) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_GENERAL_ERROR;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(1)
.WillOnce(RunOnceCallback<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<bool, Error>> waiter;
token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
waiter.GetCallback());
EXPECT_FALSE(waiter.Get<0>().has_value());
EXPECT_EQ(waiter.Get<0>().error(), Error::kFailedToSearchForObjects);
}
// Test that DoesPrivateKeyExist retries several times when the search for
// objects fails with a session error.
TEST_F(KcerTokenImplTest, DoesPrivateKeyExistRetryOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
std::vector<ObjectHandle> result_object_list{};
uint32_t result_code = chromeos::PKCS11_CKR_SESSION_CLOSED;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(result_object_list, result_code));
base::test::TestFuture<base::expected<bool, Error>> waiter;
token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
waiter.GetCallback());
EXPECT_FALSE(waiter.Get<0>().has_value());
EXPECT_EQ(waiter.Get<0>().error(), Error::kPkcs11SessionFailure);
}
// Test that Sign can successfully create a RsaSha1 signature.
TEST_F(KcerTokenImplTest, SignRsaSha1Success) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Digest for the same data and algorithm is always the same and was recorded
// from a working device.
std::vector<uint8_t> expected_digest =
base::Base64Decode("MCEwCQYFKw4DAhoFAAQUxTkeMIryW0LVk01qIBo06JjSVcY=")
.value();
ObjectHandle expected_key_handle(10);
std::vector<ObjectHandle> result_object_list({expected_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
Signature result_signature(result_signature_bytes);
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)));
EXPECT_CALL(
chaps_client_,
Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
std::vector<uint8_t>(), expected_key_handle, expected_digest, _))
.WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().value(), result_signature);
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
}
// Test that Sign can successfully create a RsaSha256 signature.
TEST_F(KcerTokenImplTest, SignRsaSha256Success) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha256;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Digest for the same data and algorithm is always the same and was recorded
// from a working device.
std::vector<uint8_t> expected_digest =
base::Base64Decode(
"MDEwDQYJYIZIAWUDBAIBBQAEIMhI4QE/"
"nwSp1j+kPOf9SvA1FSx8ZppKQEtnEHzuXy5O")
.value();
ObjectHandle expected_key_handle(10);
std::vector<ObjectHandle> result_object_list({expected_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
Signature result_signature(result_signature_bytes);
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)));
EXPECT_CALL(
chaps_client_,
Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
std::vector<uint8_t>(), expected_key_handle, expected_digest, _))
.WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().value(), result_signature);
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
}
// Test that Sign can successfully create a RsaPssSha256 signature.
TEST_F(KcerTokenImplTest, SignRsaPssSha256Success) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPssRsaeSha256;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Digest for the same data and algorithm is always the same and was recorded
// from a working device.
std::vector<uint8_t> expected_digest =
base::Base64Decode("yEjhAT+fBKnWP6Q85/1K8DUVLHxmmkpAS2cQfO5fLk4=")
.value();
// Mechanism parameters are always the same for a given algorithm.
std::vector<uint8_t> expected_mechanism_param =
base::Base64Decode("UAIAAAAAAAACAAAAAAAAACAAAAAAAAAA").value();
ObjectHandle expected_key_handle(10);
std::vector<ObjectHandle> result_object_list({expected_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
Signature result_signature(result_signature_bytes);
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)));
EXPECT_CALL(
chaps_client_,
Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS_PSS,
expected_mechanism_param, expected_key_handle, expected_digest, _))
.WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().value(), result_signature);
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
}
// Test that Sign can successfully create a EcSha256 signature.
TEST_F(KcerTokenImplTest, SignEcSha256) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetEcPkcs11Id(), GetEcSpki());
SigningScheme signing_scheme = SigningScheme::kEcdsaSecp256r1Sha256;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Digest for the same data and algorithm is always the same and was recorded
// from a working device.
std::vector<uint8_t> expected_digest =
base::Base64Decode("yEjhAT+fBKnWP6Q85/1K8DUVLHxmmkpAS2cQfO5fLk4=")
.value();
ObjectHandle expected_key_handle(10);
std::vector<ObjectHandle> result_object_list({expected_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
// Signature is different for each key, this one was recorded from a working
// device.
std::vector<uint8_t> result_chaps_signature =
base::Base64Decode(
"aNhCYZ1TL7eSxbrA6t/+XBAllGfi0zom4Ybo++iwW81Yob2LDKX6OOUX2h661/"
"INbTVYGDO5kNDqLBc1BUxgkA==")
.value();
// `result_chaps_signature` needs to be reencoded by Kcer into a different
// format, this is the expected result.
Signature result_signature(
base::Base64Decode("MEQCIGjYQmGdUy+3ksW6wOrf/"
"lwQJZRn4tM6JuGG6PvosFvNAiBYob2LDKX6OOUX2h661/"
"INbTVYGDO5kNDqLBc1BUxgkA==")
.value());
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)));
EXPECT_CALL(chaps_client_, Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_ECDSA,
std::vector<uint8_t>(), expected_key_handle,
expected_digest, _))
.WillOnce(DoAll(RunOnceCallback<5>(result_chaps_signature, result_code)));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().value(), result_signature);
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetEcPkcs11Id().value()));
}
// Test that Sign correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, SignFailToSearch) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that Sign retries several times when the search for the key fails with a
// session error.
TEST_F(KcerTokenImplTest, SignRetrySearchOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that Sign correctly fails when Chaps fails to sign.
TEST_F(KcerTokenImplTest, SignFailToSign) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(
RunOnceCallback<2>(std::vector<ObjectHandle>({ObjectHandle(10)}),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, Sign)
.WillOnce(RunOnceCallback<5>(std::vector<uint8_t>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSign);
}
// Test that Sign retries several times when Chaps fails to sign with a session
// error.
TEST_F(KcerTokenImplTest, SignRetrySignOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>({ObjectHandle(10)}),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, Sign)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(
std::vector<uint8_t>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that SignRsaPkcs1Raw can successfully create a signature.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
ObjectHandle expected_key_handle(10);
std::vector<ObjectHandle> result_object_list({expected_key_handle});
uint32_t result_code = chromeos::PKCS11_CKR_OK;
std::vector<uint8_t> result_signature_bytes({11, 12, 13, 14, 15});
Signature result_signature(result_signature_bytes);
chaps::AttributeList find_objects_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(DoAll(MoveArg<1>(&find_objects_attrs),
RunOnceCallback<2>(result_object_list, result_code)));
EXPECT_CALL(
chaps_client_,
Sign(pkcs11_slot_id_, chromeos::PKCS11_CKM_RSA_PKCS,
std::vector<uint8_t>(), expected_key_handle, digest.value(), _))
.WillOnce(DoAll(RunOnceCallback<5>(result_signature_bytes, result_code)));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().value(), result_signature);
chromeos::PKCS11_CK_OBJECT_CLASS priv_key_class =
chromeos::PKCS11_CKO_PRIVATE_KEY;
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_CLASS,
MakeSpan(&priv_key_class)));
EXPECT_TRUE(FindAttribute(find_objects_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
}
// Test that SignRsaPkcs1Raw correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawFailToSearch) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSearchForObjects);
}
// Test that SignRsaPkcs1Raw retries several times when the search for the key
// fails with a session error.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawRetrySearchOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that SignRsaPkcs1Raw correctly fails when Chaps fails to sign.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawFailToSign) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(
RunOnceCallback<2>(std::vector<ObjectHandle>({ObjectHandle(10)}),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, Sign)
.WillOnce(RunOnceCallback<5>(std::vector<uint8_t>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kFailedToSign);
}
// Test that SignRsaPkcs1Raw retries several times when Chaps fails to sign with
// a session error.
TEST_F(KcerTokenImplTest, SignRsaPkcs1RawRetrySignOnSessionError) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
DigestWithPrefix digest({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>({ObjectHandle(10)}),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, Sign)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(
std::vector<uint8_t>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key), digest,
sign_waiter.GetCallback());
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
TEST_F(KcerTokenImplTest, GetTokenInfoForUserToken) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<base::expected<TokenInfo, Error>> info_waiter;
token_.GetTokenInfo(info_waiter.GetCallback());
ASSERT_TRUE(info_waiter.Get().has_value());
const TokenInfo& info = info_waiter.Get().value();
EXPECT_EQ(info.pkcs11_id, pkcs11_slot_id_.value());
EXPECT_EQ(info.token_name, "User Token");
EXPECT_EQ(info.module_name, "Chaps");
}
TEST_F(KcerTokenImplTest, GetTokenInfoForDeviceToken) {
KcerTokenImpl device_token(Token::kDevice, &chaps_client_);
device_token.InitializeWithoutNss(pkcs11_slot_id_);
base::test::TestFuture<base::expected<TokenInfo, Error>> info_waiter;
device_token.GetTokenInfo(info_waiter.GetCallback());
ASSERT_TRUE(info_waiter.Get().has_value());
const TokenInfo& info = info_waiter.Get().value();
EXPECT_EQ(info.pkcs11_id, pkcs11_slot_id_.value());
EXPECT_EQ(info.token_name, "Device Token");
EXPECT_EQ(info.module_name, "Chaps");
}
// Test that GetKeyInfo can successfully get key info for RSA keys when PSS is
// supported.
TEST_F(KcerTokenImplTest, GetKeyInfoRsaPssSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
bool expected_is_hw_backed = true;
std::string expected_nickname = "new_label";
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(RunOnceCallback<1>(
std::vector<uint64_t>{chromeos::PKCS11_CKM_RSA_PKCS_PSS},
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(
GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
chromeos::PKCS11_CKK_RSA, expected_nickname),
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_TRUE(info_waiter.Get().has_value());
const KeyInfo& key_info = info_waiter.Get().value();
EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
EXPECT_EQ(key_info.key_type, KeyType::kRsa);
EXPECT_EQ(key_info.nickname, expected_nickname);
EXPECT_THAT(
key_info.supported_signing_schemes,
UnorderedElementsAre(
SigningScheme::kRsaPkcs1Sha1, SigningScheme::kRsaPkcs1Sha256,
SigningScheme::kRsaPkcs1Sha384, SigningScheme::kRsaPkcs1Sha512,
SigningScheme::kRsaPssRsaeSha256, SigningScheme::kRsaPssRsaeSha384,
SigningScheme::kRsaPssRsaeSha512));
}
// Test that GetKeyInfo can successfully get key info for RSA keys when PSS is
// not supported.
TEST_F(KcerTokenImplTest, GetKeyInfoRsaNoPssSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
// Also try a different value for is_hardware_backed.
bool expected_is_hw_backed = false;
std::string expected_nickname = "new_label";
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(
GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
chromeos::PKCS11_CKK_RSA, expected_nickname),
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_TRUE(info_waiter.Get().has_value());
const KeyInfo& key_info = info_waiter.Get().value();
EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
EXPECT_EQ(key_info.key_type, KeyType::kRsa);
EXPECT_EQ(key_info.nickname, expected_nickname);
EXPECT_THAT(key_info.supported_signing_schemes,
UnorderedElementsAre(SigningScheme::kRsaPkcs1Sha1,
SigningScheme::kRsaPkcs1Sha256,
SigningScheme::kRsaPkcs1Sha384,
SigningScheme::kRsaPkcs1Sha512));
}
// Test that GetKeyInfo can successfully get key info for EC keys.
TEST_F(KcerTokenImplTest, GetKeyInfoEcSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
bool expected_is_hw_backed = false;
std::string expected_nickname = "new_label";
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(
GetFakeKeyInfoAttrs(/*is_in_software=*/!expected_is_hw_backed,
chromeos::PKCS11_CKK_EC, expected_nickname),
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_TRUE(info_waiter.Get().has_value());
const KeyInfo& key_info = info_waiter.Get().value();
EXPECT_EQ(key_info.is_hardware_backed, expected_is_hw_backed);
EXPECT_EQ(key_info.key_type, KeyType::kEcc);
EXPECT_EQ(key_info.nickname, expected_nickname);
EXPECT_THAT(key_info.supported_signing_schemes,
UnorderedElementsAre(SigningScheme::kEcdsaSecp256r1Sha256,
SigningScheme::kEcdsaSecp384r1Sha384,
SigningScheme::kEcdsaSecp521r1Sha512));
}
// Test that GetKeyInfo correctly fails when it fails to fetch supported
// mechanisms.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToGetMechanisms) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(RunOnceCallback<1>(std::vector<uint64_t>{},
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToRetrieveMechanismList);
}
// Test that GetKeyInfo correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kKeyNotFound);
}
// Test that GetKeyInfo correctly fails when it fails to read attributes.
TEST_F(KcerTokenImplTest, GetKeyInfoFailToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToReadAttribute);
}
// Test that GetKeyInfo correctly fails when retrieved attributes are
// invalid.
TEST_F(KcerTokenImplTest, GetKeyInfoBadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
const uint32_t kInvalidKeyType = 9999;
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
// Imitate bad key type in the response.
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(
GetFakeKeyInfoAttrs(/*is_in_software=*/true,
/*pkcs_key_type=*/kInvalidKeyType, "nickname"),
chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}
// Test that GetKeyInfo retries several times when Chaps fails to get supported
// mechanisms with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToGetMechanisms) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<1>(
std::vector<uint64_t>{}, chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetKeyInfo retries several times when Chaps fails to find the key
// with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
// GetMechanismList's result should be cached and not repeated.
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.Times(1)
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetKeyInfo retries several times when Chaps fails to read
// attributes with a session error.
TEST_F(KcerTokenImplTest, GetKeyInfoRetryToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
// GetMechanismList's result should be cached and not repeated.
EXPECT_CALL(chaps_client_, GetMechanismList(pkcs11_slot_id_, _))
.Times(1)
.WillOnce(
RunOnceCallback<1>(std::vector<uint64_t>{}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<KeyInfo, Error>> info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key), info_waiter.GetCallback());
ASSERT_FALSE(info_waiter.Get().has_value());
EXPECT_EQ(info_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetKeyPermissions can successfully get key permissions.
TEST_F(KcerTokenImplTest, GetKeyPermissionsSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
chaps::KeyPermissions expected_key_permissions;
expected_key_permissions.mutable_key_usages()->set_corporate(true);
expected_key_permissions.mutable_key_usages()->set_arc(true);
chaps::AttributeList key_perm_attrs;
AddAttribute(
key_perm_attrs, pkcs11_custom_attributes::kCkaChromeOsKeyPermissions,
base::as_byte_span(expected_key_permissions.SerializeAsString()));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(key_perm_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_TRUE(result_waiter.Get().has_value());
const std::optional<chaps::KeyPermissions>& received_key_permissions =
result_waiter.Get().value();
EXPECT_TRUE(ExpectKeyPermissionsEqual(expected_key_permissions,
received_key_permissions));
}
// Test that GetKeyPermissions correctly fails when it fails to find the key.
TEST_F(KcerTokenImplTest, GetKeyPermissionsFailToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kKeyNotFound);
}
// Test that GetKeyPermissions correctly fails when it fails to read attributes.
TEST_F(KcerTokenImplTest, GetKeyPermissionsFailToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToReadAttribute);
}
// Test that GetKeyPermissions correctly fails when retrieved attributes are
// invalid.
TEST_F(KcerTokenImplTest, GetKeyPermissionsBadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
// Imitate bad key type in the response.
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(
RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}
// Test that GetKeyPermissions retries several times when Chaps fails to find
// the key with a session error.
TEST_F(KcerTokenImplTest, GetKeyPermissionsRetryToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetKeyPermissions retries several times when Chaps fails to read
// attributes with a session error.
TEST_F(KcerTokenImplTest, GetKeyPermissionsRetryToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
result_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetCertProvisioningProfileId can successfully get cert provisioning
// profile id.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
std::string expected_cert_prov_id = "new_cert_prov_id";
chaps::AttributeList cert_prov_id_attrs;
AddAttribute(
cert_prov_id_attrs,
pkcs11_custom_attributes::kCkaChromeOsBuiltinProvisioningProfileId,
base::as_byte_span(expected_cert_prov_id));
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(
RunOnceCallback<3>(cert_prov_id_attrs, chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_TRUE(result_waiter.Get().has_value());
EXPECT_EQ(expected_cert_prov_id, result_waiter.Get().value());
}
// Test that GetCertProvisioningProfileId correctly fails when it fails to find
// the key.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdFailToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kKeyNotFound);
}
// Test that GetCertProvisioningProfileId correctly fails when it fails to read
// attributes.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdFailToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(chaps::AttributeList(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToReadAttribute);
}
// Test that GetCertProvisioningProfileId correctly fails when retrieved
// attributes are invalid.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdBadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(
RunOnceCallback<3>(chaps::AttributeList(), chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kFailedToDecodeKeyAttributes);
}
// Test that GetCertProvisioningProfileId retries several times when Chaps fails
// to find the key with a session error.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdRetryToFindKey) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that GetCertProvisioningProfileId retries several times when Chaps fails
// to read attributes with a session error.
TEST_F(KcerTokenImplTest, GetCertProvisioningProfileIdRetryToReadAttributes) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
std::vector<ObjectHandle> key_handles{key_handle};
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<2>(key_handles, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_,
GetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<3>(
chaps::AttributeList(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
result_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
result_waiter.GetCallback());
ASSERT_FALSE(result_waiter.Get().has_value());
EXPECT_EQ(result_waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that SetKeyNickname can successfully set a nickname.
TEST_F(KcerTokenImplTest, SetKeyNicknameSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle{1};
std::vector<ObjectHandle> key_handles{key_handle};
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));
chaps::AttributeList nickname_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(DoAll(MoveArg<2>(&nickname_attrs),
RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));
std::string new_nickname = "new_nickname";
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), new_nickname,
waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(nickname_attrs, chromeos::PKCS11_CKA_LABEL,
base::as_byte_span(new_nickname)));
EXPECT_TRUE(waiter.Get().has_value());
}
// Test that SetKeyNickname can successfully set a nickname when the key is
// specified by SPKI.
TEST_F(KcerTokenImplTest, SetKeyNicknameBySpkiSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
ObjectHandle key_handle{1};
std::vector<ObjectHandle> key_handles{key_handle};
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));
chaps::AttributeList nickname_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(DoAll(MoveArg<2>(&nickname_attrs),
RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));
std::string new_nickname = "new_nickname";
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(Token::kUser, GetRsaSpki()),
new_nickname, waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(FindAttribute(nickname_attrs, chromeos::PKCS11_CKA_LABEL,
base::as_byte_span(new_nickname)));
EXPECT_TRUE(waiter.Get().has_value());
}
// Test that SetKeyNickname correctly fails when the key is specified by an
// invalid SPKI.
TEST_F(KcerTokenImplTest, SetKeyNicknameBySpkiFail) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
std::vector<uint8_t> bad_spki = GetRsaSpki().value();
bad_spki.pop_back();
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(PublicKeySpki(bad_spki)), "",
waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToGetPkcs11Id);
}
// Test that SetKeyNickname correctly fails when the key cannot be found.
TEST_F(KcerTokenImplTest, SetKeyNicknameFailToFind) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kKeyNotFound);
}
// Test that SetKeyNickname correctly fails when Chaps fails to set the
// attribute.
TEST_F(KcerTokenImplTest, SetKeyNicknameFailToSet) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{key_handle},
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handle, _, _))
.WillOnce(RunOnceCallback<3>(chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kFailedToWriteAttribute);
}
// Test that SetKeyNickname retries several times when Chaps fails to find the
// key with a session error.
TEST_F(KcerTokenImplTest, SetKeyNicknameRetryToFind) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that SetKeyNickname retries several times when Chaps fails to set an
// attribute with a session error.
TEST_F(KcerTokenImplTest, SetKeyNicknameRetryToSet) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle(1);
EXPECT_CALL(chaps_client_, FindObjects)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>{key_handle}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, SetAttributeValue(_, key_handle, _, _))
.Times(kDefaultAttempts)
.WillRepeatedly(
RunOnceCallbackRepeatedly<3>(chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), "", waiter.GetCallback());
ASSERT_FALSE(waiter.Get().has_value());
EXPECT_EQ(waiter.Get().error(), Error::kPkcs11SessionFailure);
}
// Test that SetKeyPermissions can successfully set a new value. The
// implementation is largely shared with SetKeyNickname and the tests for it
// cover the fail cases.
TEST_F(KcerTokenImplTest, SetKeyPermissionsSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle{1};
std::vector<ObjectHandle> key_handles{key_handle};
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));
chaps::AttributeList key_permissions_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(DoAll(MoveArg<2>(&key_permissions_attrs),
RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));
chaps::KeyPermissions new_key_permissions;
new_key_permissions.mutable_key_usages()->set_corporate(true);
new_key_permissions.mutable_key_usages()->set_arc(true);
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetKeyPermissions(PrivateKeyHandle(public_key), new_key_permissions,
waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
std::string serialized_key_permissions =
new_key_permissions.SerializeAsString();
EXPECT_TRUE(FindAttribute(key_permissions_attrs,
static_cast<uint32_t>(AttributeId::kKeyPermissions),
base::as_byte_span(serialized_key_permissions)));
EXPECT_TRUE(waiter.Get().has_value());
}
// Test that SetCertProvisioningProfileId can successfully set a new value. The
// implementation is largely shared with SetKeyNickname and the tests for it
// cover the fail cases.
TEST_F(KcerTokenImplTest, SetCertProvisioningProfileIdSuccess) {
token_.InitializeWithoutNss(pkcs11_slot_id_);
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
ObjectHandle key_handle{1};
std::vector<ObjectHandle> key_handles{key_handle};
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(pkcs11_slot_id_, _, _))
.WillOnce(
DoAll(MoveArg<1>(&find_key_attrs),
RunOnceCallback<2>(key_handles, chromeos::PKCS11_CKR_OK)));
chaps::AttributeList cert_prov_id_attrs;
EXPECT_CALL(chaps_client_,
SetAttributeValue(pkcs11_slot_id_, key_handle, _, _))
.WillOnce(DoAll(MoveArg<2>(&cert_prov_id_attrs),
RunOnceCallback<3>(chromeos::PKCS11_CKR_OK)));
std::string new_profile_id = "new_profile_id";
base::test::TestFuture<base::expected<void, Error>> waiter;
token_.SetCertProvisioningProfileId(PrivateKeyHandle(public_key),
new_profile_id, waiter.GetCallback());
EXPECT_TRUE(FindAttribute(find_key_attrs, chromeos::PKCS11_CKA_ID,
GetRsaPkcs11Id().value()));
EXPECT_TRUE(
FindAttribute(cert_prov_id_attrs,
static_cast<uint32_t>(AttributeId::kCertProvisioningId),
base::as_byte_span(new_profile_id)));
EXPECT_TRUE(waiter.Get().has_value());
}
// Test that all methods are queued until the token is initialized and unblocked
// after that. In this scenario fail all the methods for simplicity.
TEST_F(KcerTokenImplTest, AllMethodsAreBlockedUntilTokenInitialization) {
EXPECT_CALL(chaps_client_, GenerateKeyPair)
.WillRepeatedly(RunOnceCallbackRepeatedly<5>(
ObjectHandle(), ObjectHandle(), chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>(), chromeos::PKCS11_CKR_GENERAL_ERROR));
EXPECT_CALL(chaps_client_, GetMechanismList)
.WillRepeatedly(RunOnceCallbackRepeatedly<1>(
std::vector<uint64>(), chromeos::PKCS11_CKR_GENERAL_ERROR));
PublicKey public_key(Token::kUser, GetRsaPkcs11Id(), GetRsaSpki());
base::test::TestFuture<base::expected<PublicKey, Error>> generate_rsa_waiter;
token_.GenerateRsaKey(RsaModulusLength::k2048, true,
generate_rsa_waiter.GetCallback());
base::test::TestFuture<base::expected<PublicKey, Error>> generate_ec_waiter;
token_.GenerateEcKey(EllipticCurve::kP256, true,
generate_ec_waiter.GetCallback());
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
token_.ImportKey(Pkcs8PrivateKeyInfoDer(), import_key_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> import_cert_waiter;
token_.ImportCertFromBytes(CertDer(), import_cert_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> remove_key_waiter;
token_.RemoveKeyAndCerts(PrivateKeyHandle(public_key),
remove_key_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> remove_cert_waiter;
token_.RemoveCert(nullptr, remove_cert_waiter.GetCallback());
base::test::TestFuture<base::expected<std::vector<PublicKey>, Error>>
list_keys_waiter;
token_.ListKeys(list_keys_waiter.GetCallback());
base::test::TestFuture<
base::expected<std::vector<scoped_refptr<const Cert>>, Error>>
list_certs_waiter;
token_.ListCerts(list_certs_waiter.GetCallback());
base::test::TestFuture<base::expected<bool, Error>> key_exists_waiter;
token_.DoesPrivateKeyExist(PrivateKeyHandle(public_key),
key_exists_waiter.GetCallback());
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
token_.Sign(PrivateKeyHandle(public_key), SigningScheme::kRsaPkcs1Sha1,
DataToSign({1, 2, 3}), sign_waiter.GetCallback());
base::test::TestFuture<base::expected<Signature, Error>> sign_raw_waiter;
token_.SignRsaPkcs1Raw(PrivateKeyHandle(public_key),
DigestWithPrefix({1, 2, 3}),
sign_raw_waiter.GetCallback());
base::test::TestFuture<base::expected<TokenInfo, Error>> token_info_waiter;
token_.GetTokenInfo(token_info_waiter.GetCallback());
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
token_.GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
get_key_permissions_waiter;
token_.GetKeyPermissions(PrivateKeyHandle(public_key),
get_key_permissions_waiter.GetCallback());
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
get_cert_prov_id_waiter;
token_.GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
get_cert_prov_id_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> set_nickname_waiter;
token_.SetKeyNickname(PrivateKeyHandle(public_key), "",
set_nickname_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>>
set_key_permissions_waiter;
token_.SetKeyPermissions(PrivateKeyHandle(public_key),
chaps::KeyPermissions(),
set_key_permissions_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> set_cert_prov_id_waiter;
token_.SetCertProvisioningProfileId(PrivateKeyHandle(public_key), "",
set_cert_prov_id_waiter.GetCallback());
// Run GenerateRsaKey again to test that SetCertProvisioningProfileId will
// trigger it after initialization.
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_rsa_waiter_2;
token_.GenerateRsaKey(RsaModulusLength::k2048, true,
generate_rsa_waiter_2.GetCallback());
task_environment_.RunUntilIdle();
EXPECT_FALSE(generate_rsa_waiter.IsReady());
EXPECT_FALSE(generate_ec_waiter.IsReady());
EXPECT_FALSE(import_key_waiter.IsReady());
EXPECT_FALSE(import_cert_waiter.IsReady());
EXPECT_FALSE(remove_key_waiter.IsReady());
EXPECT_FALSE(remove_cert_waiter.IsReady());
EXPECT_FALSE(list_keys_waiter.IsReady());
EXPECT_FALSE(list_certs_waiter.IsReady());
EXPECT_FALSE(key_exists_waiter.IsReady());
EXPECT_FALSE(sign_waiter.IsReady());
EXPECT_FALSE(sign_raw_waiter.IsReady());
EXPECT_FALSE(token_info_waiter.IsReady());
EXPECT_FALSE(key_info_waiter.IsReady());
EXPECT_FALSE(get_key_permissions_waiter.IsReady());
EXPECT_FALSE(get_cert_prov_id_waiter.IsReady());
EXPECT_FALSE(set_nickname_waiter.IsReady());
EXPECT_FALSE(set_key_permissions_waiter.IsReady());
EXPECT_FALSE(set_cert_prov_id_waiter.IsReady());
EXPECT_FALSE(generate_rsa_waiter_2.IsReady());
token_.InitializeWithoutNss(pkcs11_slot_id_);
EXPECT_FALSE(generate_rsa_waiter.Get().has_value());
EXPECT_FALSE(generate_ec_waiter.Get().has_value());
EXPECT_FALSE(import_key_waiter.Get().has_value());
EXPECT_FALSE(import_cert_waiter.Get().has_value());
EXPECT_FALSE(remove_key_waiter.Get().has_value());
EXPECT_FALSE(remove_cert_waiter.Get().has_value());
EXPECT_FALSE(list_keys_waiter.Get().has_value());
EXPECT_TRUE(list_certs_waiter.Get().has_value());
EXPECT_FALSE(key_exists_waiter.Get().has_value());
EXPECT_FALSE(sign_waiter.Get().has_value());
EXPECT_FALSE(sign_raw_waiter.Get().has_value());
EXPECT_TRUE(token_info_waiter.Get().has_value());
EXPECT_FALSE(key_info_waiter.Get().has_value());
EXPECT_FALSE(get_key_permissions_waiter.Get().has_value());
EXPECT_FALSE(get_cert_prov_id_waiter.Get().has_value());
EXPECT_FALSE(set_nickname_waiter.Get().has_value());
EXPECT_FALSE(set_key_permissions_waiter.Get().has_value());
EXPECT_FALSE(set_cert_prov_id_waiter.Get().has_value());
EXPECT_FALSE(generate_rsa_waiter_2.Get().has_value());
}
} // namespace
} // namespace kcer::internal