// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <ostream>
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ref.h"
#include "base/task/bind_post_task.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "chromeos/components/kcer/chaps/mock_high_level_chaps_client.h"
#include "chromeos/components/kcer/kcer.h"
#include "chromeos/components/kcer/kcer_histograms.h"
#include "chromeos/components/kcer/kcer_impl.h"
#include "chromeos/components/kcer/kcer_nss/kcer_token_impl_nss.h"
#include "chromeos/components/kcer/kcer_nss/test_utils.h"
#include "chromeos/components/kcer/kcer_token_utils.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "crypto/nss_util.h"
#include "crypto/scoped_test_nss_db.h"
#include "crypto/secure_hash.h"
#include "net/test/cert_builder.h"
#include "net/test/test_data_directory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/pki/pem.h"
using testing::DoAll;
using testing::UnorderedElementsAre;
using ObjectHandle = kcer::SessionChapsClient::ObjectHandle;
using SlotId = kcer::SessionChapsClient::SlotId;
using base::test::RunOnceCallback;
using base::test::RunOnceCallbackRepeatedly;
using kcer::MakeSpan;
using kcer::internal::KcerPkcs12ImportEvent;
using pkcs11_custom_attributes::kCkaChromeOsMigratedFromNss;
using testing::_;
namespace kcer {
// Test-only overloads for better errors from EXPECT_EQ, etc.
std::ostream& operator<<(std::ostream& stream, Error val) {
stream << static_cast<int>(val);
return stream;
}
std::ostream& operator<<(std::ostream& stream, Token val) {
stream << static_cast<int>(val);
return stream;
}
std::ostream& operator<<(std::ostream& stream, PublicKey val) {
stream << "{\n";
stream << " token: " << val.GetToken() << "\n";
stream << " pkcs11_id: " << base::Base64Encode(val.GetPkcs11Id().value())
<< "\n";
stream << " spki: " << base::Base64Encode(val.GetSpki().value()) << "\n";
stream << "}\n";
return stream;
}
namespace {
enum class TestKeyType {
kRsa,
kEcc,
kImportedRsa,
kImportedEcc,
};
constexpr char kPublicKeyBase64[] =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArURIGgAq8joyzjFdUpzmOeDa5VgTC8"
"n77sMCQsm01mwk+6NwHhCSyCfXoB9EuMcKynj9SZbCgArnsHcZiqBsKpU/VnBO/"
"vp5MSY5qFMYxEpjPYSQcASUkOlkVYieQN6NK4FUynPJBIh3Rs6LUHlGU+"
"w3GifCl3Be4Q0om61Eo+jxQJBlRFTyqETh0AeHI2lEK9hsePsn8AMJn2tv7GoaiS+"
"RoZsMAcDg8uhtmlQB/"
"eoy7MtXwSchI0e2Q8QdUneNp529Ee+pUQ5Uki1L2pE4Pnyj+j2i2x4wGFGdJgiBMSvtpvdPdF+"
"NMfjdbVaDzTF3rcL3lNCxRb4xk3TMFXV7dQIDAQAB";
// Password for client.p12 and 2_client_certs_1_key.p12 .
constexpr char kPkcs12RsaFilePassword[] = "12345";
// Password for client_with_ec_key.p12 .
constexpr char kPkcs12EcFilePassword[] = "123456";
constexpr int kDefaultAttempts = 5;
std::string TestKeyTypeToStr(TestKeyType key_type) {
switch (key_type) {
case TestKeyType::kRsa:
return "kRsa";
case TestKeyType::kEcc:
return "kEcc";
case TestKeyType::kImportedRsa:
return "kImportedRsa";
case TestKeyType::kImportedEcc:
return "kImportedEcc";
}
}
std::vector<uint8_t> StrToBytes(const std::string& val) {
return std::vector<uint8_t>(val.begin(), val.end());
}
scoped_refptr<base::SingleThreadTaskRunner> IOTaskRunner() {
return content::GetIOThreadTaskRunner({});
}
bool SpanEqual(base::span<const uint8_t> s1, base::span<const uint8_t> s2) {
return base::ranges::equal(s1, s2);
}
bool SpanEqual(base::span<const char> s1, base::span<const uint8_t> s2) {
return base::ranges::equal(base::as_bytes(s1), s2);
}
std::string ToString(const std::vector<SigningScheme>& vec) {
std::stringstream res;
res << "[";
for (const SigningScheme& s : vec) {
res << static_cast<int>(s) << ", ";
}
res << "]";
return res.str();
}
std::unique_ptr<kcer::Kcer> CreateKcer(
scoped_refptr<base::TaskRunner> token_task_runner,
base::WeakPtr<kcer::internal::KcerToken> user_token,
base::WeakPtr<kcer::internal::KcerToken> device_token) {
auto kcer = std::make_unique<kcer::internal::KcerImpl>();
kcer->Initialize(std::move(token_task_runner), std::move(user_token),
std::move(device_token));
return kcer;
}
bool KeyInfoEquals(const KeyInfo& expected, const KeyInfo& actual) {
if (expected.is_hardware_backed != actual.is_hardware_backed) {
LOG(ERROR) << "ERROR: is_hardware_backed: expected: "
<< expected.is_hardware_backed
<< ", actual: " << actual.is_hardware_backed;
return false;
}
if (expected.key_type != actual.key_type) {
LOG(ERROR) << "ERROR: key_type: expected: " << int(expected.key_type)
<< ", actual: " << int(actual.key_type);
return false;
}
if (expected.supported_signing_schemes != actual.supported_signing_schemes) {
LOG(ERROR) << "ERROR: supported_signing_schemes: expected: "
<< ToString(expected.supported_signing_schemes)
<< ", actual: " << ToString(actual.supported_signing_schemes);
return false;
}
if (expected.nickname != actual.nickname) {
LOG(ERROR) << "ERROR: nickname: expected: "
<< expected.nickname.value_or("<empty>")
<< ", actual: " << actual.nickname.value_or("<empty>");
return false;
}
return true;
}
// A helper class for receiving notifications from Kcer.
class NotificationsObserver {
public:
explicit NotificationsObserver(base::test::TaskEnvironment& task_environment)
: task_environment_(task_environment) {}
base::RepeatingClosure GetCallback() {
return base::BindRepeating(&NotificationsObserver::OnCertDbChanged,
weak_factory_.GetWeakPtr());
}
void OnCertDbChanged() {
notifications_counter_++;
if (run_loop_ &&
notifications_counter_ >= expected_notifications_.value()) {
run_loop_->Quit();
}
}
// Waits until the required number of notifications is received. Tries to
// check that no extra notifications is sent.
bool WaitUntil(size_t notifications) {
if (notifications_counter_ < notifications) {
expected_notifications_ = notifications;
run_loop_.emplace();
run_loop_->Run();
run_loop_.reset();
expected_notifications_.reset();
}
// An additional RunUntilIdle to try catching extra unwanted notifications.
task_environment_->RunUntilIdle();
if (notifications_counter_ != notifications) {
LOG(ERROR) << "Actual notifications: " << notifications_counter_;
return false;
}
return true;
}
size_t Notifications() const { return notifications_counter_; }
private:
const raw_ref<base::test::TaskEnvironment> task_environment_;
size_t notifications_counter_ = 0;
std::optional<base::RunLoop> run_loop_;
std::optional<size_t> expected_notifications_;
base::WeakPtrFactory<NotificationsObserver> weak_factory_{this};
};
// Test fixture for KcerNss tests. Provides the least amount of pre-configured
// setup to give more control to the tests themself.
class KcerNssTest : public testing::Test {
public:
KcerNssTest() : observer_(task_environment_) {}
protected:
void InitializeKcer(std::vector<Token> tokens) {
base::WeakPtr<internal::KcerToken> user_token_ptr;
base::WeakPtr<internal::KcerToken> device_token_ptr;
for (Token token_type : tokens) {
if (token_type == Token::kUser) {
CHECK(!user_token_ptr.MaybeValid());
user_token_ = std::make_unique<TokenHolder>(token_type, &chaps_client_,
/*initialize=*/true);
user_token_ptr = user_token_->GetWeakPtr();
} else if (token_type == Token::kDevice) {
CHECK(!device_token_ptr.MaybeValid());
device_token_ = std::make_unique<TokenHolder>(
token_type, &chaps_client_, /*initialize=*/true);
device_token_ptr = device_token_->GetWeakPtr();
}
}
kcer_ = CreateKcer(IOTaskRunner(), user_token_ptr, device_token_ptr);
observers_subscription_ = kcer_->AddObserver(observer_.GetCallback());
}
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::MainThreadType::UI,
content::BrowserTaskEnvironment::REAL_IO_THREAD};
NotificationsObserver observer_;
base::CallbackListSubscription observers_subscription_;
MockHighLevelChapsClient chaps_client_;
std::unique_ptr<TokenHolder> user_token_;
std::unique_ptr<TokenHolder> device_token_;
std::unique_ptr<Kcer> kcer_;
base::HistogramTester histogram_tester_;
};
// Test that if a method is called with a token that is not (and won't be)
// available, then an error is returned.
TEST_F(KcerNssTest, UseUnavailableTokenThenGetError) {
InitializeKcer(/*tokens=*/{});
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_waiter.GetCallback());
ASSERT_FALSE(generate_waiter.Get().has_value());
EXPECT_EQ(generate_waiter.Get().error(), Error::kTokenIsNotAvailable);
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that all methods can be queued while a Kcer instance and its token are
// initializing and that the entire task queue can be processed when
// initialization completes (in this case - completes with a failure).
TEST_F(KcerNssTest, QueueTasksThenFailInitializationThenGetErrors) {
// Do not initialize yet to simulate slow initialization.
TokenHolder user_token(Token::kUser, &chaps_client_, /*initialize=*/false);
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder = MakeCertBuilder(
issuer.get(), base::Base64Decode(kPublicKeyBase64).value());
// Internal values don't matter, they won't be accessed during this test.
scoped_refptr<Cert> fake_cert = base::MakeRefCounted<Cert>(
Token::kUser, Pkcs11Id(), /*nickname=*/std::string(),
/*x509_cert=*/nullptr);
// Create a Kcer instance without any tokens. It will queue all the incoming
// requests itself.
auto kcer = std::make_unique<kcer::internal::KcerImpl>();
auto subscription = kcer->AddObserver(observer_.GetCallback());
base::test::TestFuture<base::expected<PublicKey, Error>> generate_rsa_waiter;
kcer->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_rsa_waiter.GetCallback());
base::test::TestFuture<base::expected<PublicKey, Error>> generate_ec_waiter;
kcer->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true,
generate_ec_waiter.GetCallback());
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
kcer->ImportKey(Token::kUser, Pkcs8PrivateKeyInfoDer({1, 2, 3}),
import_key_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>>
import_cert_from_bytes_waiter;
kcer->ImportCertFromBytes(Token::kUser, CertDer({1, 2, 3}),
import_cert_from_bytes_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> import_x509_cert_waiter;
kcer->ImportX509Cert(Token::kUser,
/*cert=*/cert_builder->GetX509Certificate(),
import_x509_cert_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> import_pkcs12_cert_waiter;
kcer->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(), "password",
/*hardware_backed=*/true, /*mark_as_migrated=*/true,
import_pkcs12_cert_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>>
remove_key_and_certs_waiter;
kcer->RemoveKeyAndCerts(PrivateKeyHandle(PublicKeySpki()),
remove_key_and_certs_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> remove_cert_waiter;
kcer->RemoveCert(fake_cert, remove_cert_waiter.GetCallback());
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
list_certs_waiter;
kcer->ListCerts({Token::kUser}, list_certs_waiter.GetCallback());
base::test::TestFuture<base::expected<bool, Error>> does_key_exist_waiter;
kcer->DoesPrivateKeyExist(PrivateKeyHandle(PublicKeySpki()),
does_key_exist_waiter.GetCallback());
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer->Sign(PrivateKeyHandle(PublicKeySpki()), SigningScheme::kRsaPkcs1Sha512,
DataToSign({1, 2, 3}), sign_waiter.GetCallback());
base::test::TestFuture<base::expected<Signature, Error>> sign_digest_waiter;
kcer->SignRsaPkcs1Raw(PrivateKeyHandle(PublicKeySpki()),
DigestWithPrefix({1, 2, 3}),
sign_digest_waiter.GetCallback());
base::test::TestFuture<base::expected<TokenInfo, Error>>
get_token_info_waiter;
kcer->GetTokenInfo(Token::kUser, get_token_info_waiter.GetCallback());
base::test::TestFuture<base::expected<KeyInfo, Error>> get_key_info_waiter;
kcer->GetKeyInfo(PrivateKeyHandle(PublicKeySpki()),
get_key_info_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> set_nickname_waiter;
kcer->SetKeyNickname(PrivateKeyHandle(PublicKeySpki()), "new_nickname",
set_nickname_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> set_permissions_waiter;
kcer->SetKeyPermissions(PrivateKeyHandle(PublicKeySpki()),
chaps::KeyPermissions(),
set_permissions_waiter.GetCallback());
base::test::TestFuture<base::expected<void, Error>> set_cert_prov_waiter;
kcer->SetCertProvisioningProfileId(PrivateKeyHandle(PublicKeySpki()),
"cert_prov_id",
set_cert_prov_waiter.GetCallback());
// Close the list with one more GenerateRsaKey, so all methods are tested
// with other methods before and after them.
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_rsa_waiter_2;
kcer->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_rsa_waiter_2.GetCallback());
// TODO(244408716): Add more methods when they are implemented.
// Check some waiters that the requests are queued.
EXPECT_FALSE(generate_rsa_waiter.IsReady());
EXPECT_FALSE(list_keys_waiter.IsReady());
EXPECT_FALSE(set_permissions_waiter.IsReady());
EXPECT_FALSE(import_pkcs12_cert_waiter.IsReady());
// Initialize Kcer with a token. This will empty the queue in `kcer` and move
// all the requests into the token.
kcer->Initialize(IOTaskRunner(), user_token.GetWeakPtr(),
/*device_token=*/nullptr);
task_environment_.RunUntilIdle();
// Check some waiters that the requests are still queued.
EXPECT_FALSE(generate_rsa_waiter.IsReady());
EXPECT_FALSE(list_keys_waiter.IsReady());
EXPECT_FALSE(set_permissions_waiter.IsReady());
EXPECT_FALSE(import_pkcs12_cert_waiter.IsReady());
// This should process and fail all the requests.
user_token.FailTokenInitialization();
ASSERT_FALSE(generate_rsa_waiter.Get().has_value());
EXPECT_EQ(generate_rsa_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(generate_ec_waiter.Get().has_value());
EXPECT_EQ(generate_ec_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(import_key_waiter.Get().has_value());
EXPECT_EQ(import_key_waiter.Get().error(), Error::kTokenInitializationFailed);
ASSERT_FALSE(import_cert_from_bytes_waiter.Get().has_value());
EXPECT_EQ(import_cert_from_bytes_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(import_x509_cert_waiter.Get().has_value());
EXPECT_EQ(import_x509_cert_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(import_pkcs12_cert_waiter.Get().has_value());
EXPECT_EQ(import_pkcs12_cert_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(remove_key_and_certs_waiter.Get().has_value());
EXPECT_EQ(remove_key_and_certs_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(remove_cert_waiter.Get().has_value());
EXPECT_EQ(remove_cert_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(list_keys_waiter.Get<1>().empty());
EXPECT_EQ(list_keys_waiter.Get<1>().at(Token::kUser),
Error::kTokenInitializationFailed);
ASSERT_FALSE(list_certs_waiter.Get<1>().empty());
EXPECT_EQ(list_certs_waiter.Get<1>().at(Token::kUser),
Error::kTokenInitializationFailed);
ASSERT_FALSE(does_key_exist_waiter.Get().has_value());
EXPECT_EQ(does_key_exist_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(sign_waiter.Get().has_value());
EXPECT_EQ(sign_waiter.Get().error(), Error::kTokenInitializationFailed);
ASSERT_FALSE(sign_digest_waiter.Get().has_value());
EXPECT_EQ(sign_digest_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(get_token_info_waiter.Get().has_value());
EXPECT_EQ(get_token_info_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(get_key_info_waiter.Get().has_value());
EXPECT_EQ(get_key_info_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(set_nickname_waiter.Get().has_value());
EXPECT_EQ(set_nickname_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(set_permissions_waiter.Get().has_value());
EXPECT_EQ(set_permissions_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(set_cert_prov_waiter.Get().has_value());
EXPECT_EQ(set_cert_prov_waiter.Get().error(),
Error::kTokenInitializationFailed);
ASSERT_FALSE(generate_rsa_waiter_2.Get().has_value());
EXPECT_EQ(generate_rsa_waiter_2.Get().error(),
Error::kTokenInitializationFailed);
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that Kcer forwards notifications from external sources. (Notifications
// created by Kcer are tested together with the methods that create them.)
TEST_F(KcerNssTest, ObserveExternalNotification) {
TokenHolder user_token(Token::kUser, &chaps_client_, /*initialize=*/true);
std::unique_ptr<Kcer> kcer =
CreateKcer(IOTaskRunner(), user_token.GetWeakPtr(),
/*device_token=*/nullptr);
NotificationsObserver observer_1(task_environment_);
NotificationsObserver observer_2(task_environment_);
// Add the first observer.
auto subscription_1 = kcer->AddObserver(observer_1.GetCallback());
EXPECT_EQ(observer_1.Notifications(), 0u);
// Check that it receives a notification.
net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/1));
// Add one more observer.
auto subscription_2 = kcer->AddObserver(observer_2.GetCallback());
// Check that both of them receive a notification.
net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/1));
// Destroy the first subscription, the first observer should stop receiving
// notifications now.
subscription_1 = base::CallbackListSubscription();
// Check that only the second observer receives a notification.
net::CertDatabase::GetInstance()->NotifyObserversClientCertStoreChanged();
EXPECT_TRUE(observer_2.WaitUntil(/*notifications=*/2));
EXPECT_TRUE(observer_1.WaitUntil(/*notifications=*/2));
}
TEST_F(KcerNssTest, ListKeys) {
InitializeKcer({Token::kUser, Token::kDevice});
std::vector<PublicKey> all_expected_keys;
std::vector<PublicKey> user_expected_keys;
std::vector<PublicKey> device_expected_keys;
// Initially there should be no keys.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser, Token::kDevice},
list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(all_expected_keys));
}
// Generate a key.
{
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_key_waiter;
kcer_->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
user_expected_keys.push_back(generate_key_waiter.Get().value());
all_expected_keys.push_back(generate_key_waiter.Take().value());
}
// The new key should be found.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser, Token::kDevice},
list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(all_expected_keys));
}
// Generate a key on a different token.
{
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_key_waiter;
kcer_->GenerateRsaKey(Token::kDevice, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
device_expected_keys.push_back(generate_key_waiter.Get().value());
all_expected_keys.push_back(generate_key_waiter.Take().value());
}
// Keys from both tokens should be found.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser, Token::kDevice},
list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(all_expected_keys));
}
// Generate a key of a different type on user token.
{
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_key_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
user_expected_keys.push_back(generate_key_waiter.Get().value());
all_expected_keys.push_back(generate_key_waiter.Take().value());
}
// Generate a key of a different type on device token.
{
base::test::TestFuture<base::expected<PublicKey, Error>>
generate_key_waiter;
kcer_->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
device_expected_keys.push_back(generate_key_waiter.Get().value());
all_expected_keys.push_back(generate_key_waiter.Take().value());
}
// Keys of both types from both tokens should be found.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser, Token::kDevice},
list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(all_expected_keys));
}
// Keys of both types only from the user token should be found.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(user_expected_keys));
}
// Keys of both types only from the device token should be found.
{
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kDevice}, list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_THAT(list_keys_waiter.Get<std::vector<PublicKey>>(),
testing::UnorderedElementsAreArray(device_expected_keys));
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that Kcer::Sign() works correctly for RSA keys with different signing
// schemes.
// TODO(miersh): Expand crypto::SignatureVerifier to work with more signature
// schemes and add them to the test.
TEST_F(KcerNssTest, SignRsa) {
InitializeKcer({Token::kUser});
base::test::TestFuture<base::expected<PublicKey, Error>> generate_key_waiter;
kcer_->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
const PublicKey& public_key = generate_key_waiter.Get().value();
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Test kRsaPkcs1Sha1 signature.
{
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha1;
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
const Signature& signature = sign_waiter.Get().value();
EXPECT_TRUE(VerifySignature(signing_scheme, public_key.GetSpki(),
data_to_sign, signature));
}
// Test kRsaPkcs1Sha256 signature. Save signature to compare with it later.
Signature rsa256_signature;
{
SigningScheme signing_scheme = SigningScheme::kRsaPkcs1Sha256;
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
rsa256_signature = sign_waiter.Get().value();
EXPECT_TRUE(VerifySignature(signing_scheme, public_key.GetSpki(),
data_to_sign, rsa256_signature));
}
// Test kRsaPssRsaeSha256 signature.
{
SigningScheme signing_scheme = SigningScheme::kRsaPssRsaeSha256;
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
const Signature& signature = sign_waiter.Get().value();
EXPECT_TRUE(VerifySignature(signing_scheme, public_key.GetSpki(),
data_to_sign, signature));
}
// Test `Kcer::SignRsaPkcs1Raw()` (kRsaPkcs1Sha256, but for pre-hashed
// values).
{
// A caller would need to hash the data themself before calling
// `SignRsaPkcs1Digest`, do that here.
auto hasher = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
hasher->Update(data_to_sign->data(), data_to_sign->size());
std::vector<uint8_t> hash(hasher->GetHashLength());
hasher->Finish(hash.data(), hash.size());
DigestWithPrefix digest_with_prefix(PrependSHA256DigestInfo(hash));
// Generate the signature.
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->SignRsaPkcs1Raw(PrivateKeyHandle(public_key),
std::move(digest_with_prefix),
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
const Signature& signature = sign_waiter.Get().value();
// Verify the signature.
EXPECT_TRUE(VerifySignature(SigningScheme::kRsaPkcs1Sha256,
public_key.GetSpki(), data_to_sign, signature));
// Manual hashing + `SignRsaPkcs1Digest` should produce the same signature
// as just `Sign`.
EXPECT_EQ(signature, rsa256_signature);
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that Kcer::Sign() works correctly for ECC keys.
// TODO(miersh): Expand crypto::SignatureVerifier to work with more signature
// schemes and add them to the test.
TEST_F(KcerNssTest, SignEcc) {
InitializeKcer({Token::kUser});
base::test::TestFuture<base::expected<PublicKey, Error>> generate_key_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true,
generate_key_waiter.GetCallback());
ASSERT_TRUE(generate_key_waiter.Get().has_value());
const PublicKey& public_key = generate_key_waiter.Get().value();
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Test kEcdsaSecp256r1Sha256 signature.
{
SigningScheme signing_scheme = SigningScheme::kEcdsaSecp256r1Sha256;
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->Sign(PrivateKeyHandle(public_key), signing_scheme, data_to_sign,
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
const Signature& signature = sign_waiter.Get().value();
EXPECT_TRUE(VerifySignature(signing_scheme, public_key.GetSpki(),
data_to_sign, signature));
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that a certificate can not be imported, if there's no key for it on
// the token.
TEST_F(KcerNssTest, ImportCertWithoutKeyThenFail) {
InitializeKcer({Token::kUser});
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder = MakeCertBuilder(
issuer.get(), base::Base64Decode(kPublicKeyBase64).value());
CertDer cert(StrToBytes(cert_builder->GetDER()));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportCertFromBytes(Token::kUser, std::move(cert),
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kKeyNotFound);
// Double check that ListCerts doesn't find the cert.
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<0>().empty()); // Cert list is empty.
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that a certificate can not be imported, if there's no key for it on
// the token.
TEST_F(KcerNssTest, ImportCertWithKeyOnDifferentTokenThenFail) {
InitializeKcer({Token::kUser, Token::kDevice});
// Generate new key on the user token.
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true, generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
// Import a cert for the key into the device token.
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportX509Cert(Token::kDevice, cert_builder->GetX509Certificate(),
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kKeyNotFound);
// Double check that ListCerts doesn't find the cert.
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<0>().empty()); // Cert list is empty.
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test that Kcer::GetTokenInfo() method returns meaningful values.
TEST_F(KcerNssTest, GetTokenInfo) {
InitializeKcer({Token::kUser});
base::test::TestFuture<base::expected<TokenInfo, Error>>
get_token_info_waiter;
kcer_->GetTokenInfo(Token::kUser, get_token_info_waiter.GetCallback());
ASSERT_TRUE(get_token_info_waiter.Get().has_value());
const TokenInfo& token_info = get_token_info_waiter.Get().value();
// These values don't have to be exactly like this, they are what a software
// NSS slot returns in tests. Still useful to test that they are not
// completely off.
EXPECT_THAT(token_info.pkcs11_id, testing::Lt(1000u));
EXPECT_THAT(token_info.token_name,
testing::StartsWith("NSS Application Slot"));
EXPECT_EQ(token_info.module_name, "NSS Internal PKCS #11 Module");
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test RSA specific fields from GetKeyInfo's result.
TEST_F(KcerNssTest, GetKeyInfoForRsaKey) {
InitializeKcer({Token::kUser});
// Generate new key.
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateRsaKey(Token::kUser, RsaModulusLength::k2048,
/*hardware_backed=*/true,
generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
ASSERT_TRUE(key_info_waiter.Get().has_value());
const KeyInfo& key_info = key_info_waiter.Get().value();
EXPECT_EQ(key_info.key_type, KeyType::kRsa);
EXPECT_THAT(
key_info.supported_signing_schemes,
UnorderedElementsAre(
SigningScheme::kRsaPkcs1Sha1, SigningScheme::kRsaPkcs1Sha256,
SigningScheme::kRsaPkcs1Sha384, SigningScheme::kRsaPkcs1Sha512,
SigningScheme::kRsaPssRsaeSha256, SigningScheme::kRsaPssRsaeSha384,
SigningScheme::kRsaPssRsaeSha512));
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test ECC specific fields from GetKeyInfo's result.
TEST_F(KcerNssTest, GetKeyInfoForEccKey) {
InitializeKcer({Token::kUser});
// Generate new key.
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true, generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
ASSERT_TRUE(key_info_waiter.Get().has_value());
const KeyInfo& key_info = key_info_waiter.Get().value();
EXPECT_EQ(key_info.key_type, KeyType::kEcc);
EXPECT_THAT(key_info.supported_signing_schemes,
UnorderedElementsAre(SigningScheme::kEcdsaSecp256r1Sha256,
SigningScheme::kEcdsaSecp384r1Sha384,
SigningScheme::kEcdsaSecp521r1Sha512));
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Test generic fields from GetKeyInfo's result and that they get updated after
// related Set* methods. Test getters for custom attributes.
TEST_F(KcerNssTest, GetKeyInfoGenericAndCustomAttributes) {
InitializeKcer({Token::kUser});
// Generate new key.
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true, generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
KeyInfo expected_key_info;
// Hardware- vs software-backed indicators on real devices are provided by
// Chaps and are wrong in unit tests.
expected_key_info.is_hardware_backed = true;
// NSS sets an empty nickname by default, this doesn't have to be like this
// in general.
expected_key_info.nickname = "";
// Custom attributes are stored differently in tests and have empty values by
// default.
chaps::KeyPermissions expected_key_permissions = chaps::KeyPermissions();
std::string expected_cert_provisioning_profile_id = "";
{
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
ASSERT_TRUE(key_info_waiter.Get().has_value());
const KeyInfo& key_info = key_info_waiter.Get().value();
// Copy some fields, their values are covered by dedicated tests, this
// test only checks that they don't change when they shouldn't.
expected_key_info.key_type = key_info.key_type;
expected_key_info.supported_signing_schemes =
key_info.supported_signing_schemes;
EXPECT_TRUE(KeyInfoEquals(expected_key_info, key_info));
}
{
expected_key_info.nickname = "new_nickname";
base::test::TestFuture<base::expected<void, Error>> set_nickname_waiter;
kcer_->SetKeyNickname(PrivateKeyHandle(public_key),
expected_key_info.nickname.value(),
set_nickname_waiter.GetCallback());
ASSERT_TRUE(set_nickname_waiter.Get().has_value());
}
{
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
ASSERT_TRUE(key_info_waiter.Get().has_value());
EXPECT_TRUE(
KeyInfoEquals(expected_key_info, key_info_waiter.Get().value()));
}
{
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
key_permissions_waiter;
kcer_->GetKeyPermissions(PrivateKeyHandle(public_key),
key_permissions_waiter.GetCallback());
ASSERT_TRUE(key_permissions_waiter.Get().has_value());
const std::optional<chaps::KeyPermissions>& key_permissions =
key_permissions_waiter.Get().value();
EXPECT_TRUE(
ExpectKeyPermissionsEqual(expected_key_permissions, key_permissions));
}
{
expected_key_permissions.mutable_key_usages()->set_corporate(true);
expected_key_permissions.mutable_key_usages()->set_arc(true);
base::test::TestFuture<base::expected<void, Error>> set_permissions_waiter;
kcer_->SetKeyPermissions(PrivateKeyHandle(public_key),
expected_key_permissions,
set_permissions_waiter.GetCallback());
ASSERT_TRUE(set_permissions_waiter.Get().has_value());
}
{
base::test::TestFuture<
base::expected<std::optional<chaps::KeyPermissions>, Error>>
key_permissions_waiter;
kcer_->GetKeyPermissions(PrivateKeyHandle(public_key),
key_permissions_waiter.GetCallback());
ASSERT_TRUE(key_permissions_waiter.Get().has_value());
const std::optional<chaps::KeyPermissions>& key_permissions =
key_permissions_waiter.Get().value();
EXPECT_TRUE(
ExpectKeyPermissionsEqual(expected_key_permissions, key_permissions));
}
{
expected_cert_provisioning_profile_id = "cert_prov_id_123";
base::test::TestFuture<base::expected<void, Error>> set_cert_prov_id_waiter;
kcer_->SetCertProvisioningProfileId(PrivateKeyHandle(public_key),
expected_cert_provisioning_profile_id,
set_cert_prov_id_waiter.GetCallback());
ASSERT_TRUE(set_cert_prov_id_waiter.Get().has_value());
}
{
base::test::TestFuture<base::expected<std::optional<std::string>, Error>>
cert_prov_waiter;
kcer_->GetCertProvisioningProfileId(PrivateKeyHandle(public_key),
cert_prov_waiter.GetCallback());
ASSERT_TRUE(cert_prov_waiter.Get().has_value());
EXPECT_EQ(expected_cert_provisioning_profile_id,
cert_prov_waiter.Get().value());
}
// Check that the setters above didn't modify unrelated attributes.
{
base::test::TestFuture<base::expected<KeyInfo, Error>> key_info_waiter;
kcer_->GetKeyInfo(PrivateKeyHandle(public_key),
key_info_waiter.GetCallback());
ASSERT_TRUE(key_info_waiter.Get().has_value());
EXPECT_TRUE(
KeyInfoEquals(expected_key_info, key_info_waiter.Get().value()));
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
TEST_F(KcerNssTest, ImportCertForImportedKey) {
InitializeKcer({Token::kUser});
std::optional<std::vector<uint8_t>> key = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
ASSERT_TRUE(key.has_value() && (key->size() > 0));
std::optional<std::vector<uint8_t>> cert = ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.pem"));
ASSERT_TRUE(cert.has_value() && (cert->size() > 0));
base::test::TestFuture<base::expected<PublicKey, Error>> import_key_waiter;
kcer_->ImportKey(Token::kUser, Pkcs8PrivateKeyInfoDer(std::move(key.value())),
import_key_waiter.GetCallback());
ASSERT_TRUE(import_key_waiter.Get().has_value());
const PublicKey& public_key = import_key_waiter.Get().value();
EXPECT_EQ(public_key.GetToken(), Token::kUser);
// Arbitrary bytes, not much to check about them.
EXPECT_EQ(public_key.GetPkcs11Id()->size(), 20u);
// Arbitrary bytes, not much to check about them.
EXPECT_EQ(public_key.GetSpki()->size(), 294u);
base::test::TestFuture<base::expected<void, Error>> import_cert_waiter;
kcer_->ImportCertFromBytes(Token::kUser, CertDer(std::move(cert.value())),
import_cert_waiter.GetCallback());
EXPECT_TRUE(import_cert_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
// List certs, make sure the new cert is listed.
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
const auto& certs =
certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>();
EXPECT_EQ(certs.size(), 1u);
}
// Test different ways to call DoesPrivateKeyExist() method and that it
// returns correct results when Kcer has access to one token.
TEST_F(KcerNssTest, DoesPrivateKeyExistOneToken) {
InitializeKcer({Token::kDevice});
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateEcKey(Token::kDevice, EllipticCurve::kP256,
/*hardware_backed=*/true, generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
// The private key should be found by the PublicKey.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
// The private key should be found by the SPKI.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key.GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
// The private key should be found on the specified token by the SPKI.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(Token::kDevice, public_key.GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
// Looking for a key on a non-existing token should return an error.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(Token::kUser, public_key.GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_FALSE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().error(), Error::kTokenIsNotAvailable);
}
// Looking for a key by an invalid SPKI should return an error.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(PublicKeySpki(std::vector<uint8_t>{1, 2, 3})),
does_exist_waiter.GetCallback());
ASSERT_FALSE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().error(), Error::kFailedToGetKeyId);
}
// Looking for a non-existing key should return a negative result.
{
std::vector<uint8_t> non_existing_key =
base::Base64Decode(kPublicKeyBase64).value();
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(PublicKeySpki(std::move(non_existing_key))),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value())
<< does_exist_waiter.Get().error();
EXPECT_EQ(does_exist_waiter.Get().value(), false);
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
TEST_F(KcerNssTest, RemoveKeyAndCertsWithManyCerts) {
if (NSS_VersionCheck("3.68") != PR_TRUE) {
// TODO(b/283925148): Remove this when all the builders are updated.
GTEST_SKIP() << "NSS is too old";
}
InitializeKcer({Token::kUser});
// Generate new key.
base::test::TestFuture<base::expected<PublicKey, Error>> generate_waiter;
kcer_->GenerateEcKey(Token::kUser, EllipticCurve::kP256,
/*hardware_backed=*/true, generate_waiter.GetCallback());
ASSERT_TRUE(generate_waiter.Get().has_value());
const PublicKey& public_key = generate_waiter.Get().value();
// Import three certs, ids should be random, so they will be different.
{
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
// Import a cert.
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
}
{
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
// Import a cert.
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/2));
}
{
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key.GetSpki().value());
// Import a cert.
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportX509Cert(Token::kUser, cert_builder->GetX509Certificate(),
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/3));
}
// Check that the imported cert can be found.
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_EQ(certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>().size(),
3u);
base::test::TestFuture<base::expected<void, Error>> remove_waiter;
kcer_->RemoveKeyAndCerts(PrivateKeyHandle(public_key),
remove_waiter.GetCallback());
EXPECT_TRUE(remove_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/4));
// Check that the imported cert cannot be found anymore.
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter_2;
kcer_->ListCerts({Token::kUser}, certs_waiter_2.GetCallback());
EXPECT_TRUE(certs_waiter_2.Get<1>().empty()); // Error map is empty.
EXPECT_TRUE(
certs_waiter_2.Get<std::vector<scoped_refptr<const Cert>>>().empty());
// Check that the generated key cannot be found anymore.
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_TRUE(list_keys_waiter.Get<std::vector<PublicKey>>().empty());
}
class KcerNssAllKeyTypesTest : public KcerNssTest,
public testing::WithParamInterface<TestKeyType> {
protected:
TestKeyType GetKeyType() { return GetParam(); }
// Requires Kcer to be initialized.
std::optional<PublicKey> CreateKey(Token token, TestKeyType key_type) {
base::test::TestFuture<base::expected<PublicKey, Error>> key_waiter;
switch (key_type) {
case TestKeyType::kRsa:
kcer_->GenerateRsaKey(token, RsaModulusLength::k2048,
/*hardware_backed=*/true,
key_waiter.GetCallback());
key_can_be_listed_ = true;
key_type_ = KeyType::kRsa;
break;
case TestKeyType::kEcc:
kcer_->GenerateEcKey(token, EllipticCurve::kP256,
/*hardware_backed=*/true,
key_waiter.GetCallback());
key_can_be_listed_ = true;
key_type_ = KeyType::kEcc;
break;
case TestKeyType::kImportedRsa: {
std::optional<std::vector<uint8_t>> key_to_import =
ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("client_1.key"));
kcer_->ImportKey(token, Pkcs8PrivateKeyInfoDer(key_to_import.value()),
key_waiter.GetCallback());
key_can_be_listed_ = false;
key_type_ = KeyType::kRsa;
break;
}
case TestKeyType::kImportedEcc: {
std::optional<std::vector<uint8_t>> key_to_import =
ReadPemFileReturnDer(
net::GetTestCertsDirectory().AppendASCII("key_usage_p256.key"));
kcer_->ImportKey(token, Pkcs8PrivateKeyInfoDer(key_to_import.value()),
key_waiter.GetCallback());
key_can_be_listed_ = false;
key_type_ = KeyType::kEcc;
break;
}
}
if (!key_waiter.Get().has_value()) {
return std::nullopt;
}
return key_waiter.Take().value();
}
SigningScheme GetSuitableSigningScheme() {
switch (GetKeyType()) {
case TestKeyType::kRsa:
case TestKeyType::kImportedRsa:
return SigningScheme::kRsaPkcs1Sha256;
case TestKeyType::kEcc:
case TestKeyType::kImportedEcc:
return SigningScheme::kEcdsaSecp256r1Sha256;
}
}
// TODO(miersh): The implementation of ImportKey that uses NSS is not able to
// list imported keys (even though it can do other operations with them). This
// should be fixed in Kcer-without-NSS.
bool key_can_be_listed_ = false;
KeyType key_type_ = KeyType::kRsa;
};
// Test different ways to call DoesPrivateKeyExist() method and that it
// returns correct results when Kcer has access to two tokens.
TEST_P(KcerNssAllKeyTypesTest, DoesPrivateKeyExistTwoTokens) {
InitializeKcer({Token::kUser, Token::kDevice});
std::optional<PublicKey> public_key = CreateKey(Token::kDevice, GetKeyType());
ASSERT_TRUE(public_key.has_value());
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key.value()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key->GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(Token::kDevice, public_key->GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(Token::kUser, public_key->GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), false);
}
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(PublicKeySpki(std::vector<uint8_t>{1, 2, 3})),
does_exist_waiter.GetCallback());
ASSERT_FALSE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().error(), Error::kFailedToGetKeyId);
}
{
std::vector<uint8_t> non_existing_key =
base::Base64Decode(kPublicKeyBase64).value();
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(
PrivateKeyHandle(PublicKeySpki(std::move(non_existing_key))),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value())
<< does_exist_waiter.Get().error();
EXPECT_EQ(does_exist_waiter.Get().value(), false);
}
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Simulate a potential lifecycle of a key and related objects.
TEST_P(KcerNssAllKeyTypesTest, KeyLifecycle) {
InitializeKcer({Token::kUser, Token::kDevice});
// Check that the initialized tokens are returned as available.
base::test::TestFuture<base::flat_set<Token>> get_tokens_waiter;
kcer_->GetAvailableTokens(get_tokens_waiter.GetCallback());
EXPECT_EQ(get_tokens_waiter.Get(),
base::flat_set<Token>({Token::kUser, Token::kDevice}));
// Check that the information about both initialized tokens is available.
{
base::test::TestFuture<base::expected<TokenInfo, Error>>
get_token_info_waiter;
kcer_->GetTokenInfo(Token::kUser, get_token_info_waiter.GetCallback());
ASSERT_TRUE(get_token_info_waiter.Get().has_value());
const TokenInfo& token_info = get_token_info_waiter.Get().value();
EXPECT_THAT(token_info.pkcs11_id, testing::Lt(1000u));
EXPECT_THAT(token_info.token_name,
testing::StartsWith("NSS Application Slot"));
EXPECT_EQ(token_info.module_name, "NSS Internal PKCS #11 Module");
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
{
base::test::TestFuture<base::expected<TokenInfo, Error>>
get_token_info_waiter;
kcer_->GetTokenInfo(Token::kDevice, get_token_info_waiter.GetCallback());
ASSERT_TRUE(get_token_info_waiter.Get().has_value());
const TokenInfo& token_info = get_token_info_waiter.Get().value();
EXPECT_THAT(token_info.pkcs11_id, testing::Lt(1000u));
EXPECT_THAT(token_info.token_name,
testing::StartsWith("NSS Application Slot"));
EXPECT_EQ(token_info.module_name, "NSS Internal PKCS #11 Module");
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/0));
}
// Add a new key.
std::optional<PublicKey> public_key = CreateKey(Token::kUser, GetKeyType());
ASSERT_TRUE(public_key.has_value());
// Check that the key is listed.
if (key_can_be_listed_) {
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_EQ(list_keys_waiter.Get<std::vector<PublicKey>>().size(), 1u);
}
// Check that the key exists.
{
base::test::TestFuture<base::expected<bool, Error>> does_exist_waiter;
kcer_->DoesPrivateKeyExist(PrivateKeyHandle(public_key->GetSpki()),
does_exist_waiter.GetCallback());
ASSERT_TRUE(does_exist_waiter.Get().has_value());
EXPECT_EQ(does_exist_waiter.Get().value(), true);
}
// Check that the key can sign data.
{
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
SigningScheme signing_scheme = GetSuitableSigningScheme();
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->Sign(PrivateKeyHandle(public_key.value()), signing_scheme,
data_to_sign, sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value()) << sign_waiter.Get().error();
EXPECT_TRUE(VerifySignature(signing_scheme, public_key->GetSpki(),
data_to_sign, sign_waiter.Get().value()));
}
// Check that the key can sign pre-hashed data.
if (key_type_ == KeyType::kRsa) {
DataToSign data_to_sign({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// A caller would need to hash the data themself before calling
// `SignRsaPkcs1Digest`, do that here.
auto hasher = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
hasher->Update(data_to_sign->data(), data_to_sign->size());
std::vector<uint8_t> hash(hasher->GetHashLength());
hasher->Finish(hash.data(), hash.size());
DigestWithPrefix digest_with_prefix(PrependSHA256DigestInfo(hash));
// Generate the signature.
base::test::TestFuture<base::expected<Signature, Error>> sign_waiter;
kcer_->SignRsaPkcs1Raw(PrivateKeyHandle(public_key.value()),
std::move(digest_with_prefix),
sign_waiter.GetCallback());
ASSERT_TRUE(sign_waiter.Get().has_value());
const Signature& signature = sign_waiter.Get().value();
// Verify the signature.
EXPECT_TRUE(VerifySignature(SigningScheme::kRsaPkcs1Sha256,
public_key->GetSpki(), data_to_sign,
signature));
}
// Import a cert for the key.
scoped_refptr<net::X509Certificate> x509_cert_1;
{
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key->GetSpki().value());
x509_cert_1 = cert_builder->GetX509Certificate();
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportX509Cert(Token::kUser, x509_cert_1,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/1));
}
// List certs, make sure the new cert is listed.
scoped_refptr<const Cert> kcer_cert_1;
{
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
const auto& certs =
certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>();
ASSERT_EQ(certs.size(), 1u);
kcer_cert_1 = certs.front();
EXPECT_TRUE(
kcer_cert_1->GetX509Cert()->EqualsExcludingChain(x509_cert_1.get()));
}
// Remove the cert.
{
base::test::TestFuture<base::expected<void, Error>> remove_cert_waiter;
kcer_->RemoveCert(kcer_cert_1, remove_cert_waiter.GetCallback());
ASSERT_TRUE(remove_cert_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/2));
kcer_cert_1 = nullptr;
}
// Check that the cert cannot be found anymore.
{
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
ASSERT_EQ(certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>().size(),
0u);
}
{
std::unique_ptr<net::CertBuilder> issuer = MakeCertIssuer();
std::unique_ptr<net::CertBuilder> cert_builder =
MakeCertBuilder(issuer.get(), public_key->GetSpki().value());
// Import another cert for the key to check that the key was not removed and
// is still usable.
CertDer cert_der(StrToBytes(cert_builder->GetDER()));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportCertFromBytes(Token::kUser, std::move(cert_der),
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/3));
}
// Check that the cert can be found.
{
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter_3;
kcer_->ListCerts({Token::kUser}, certs_waiter_3.GetCallback());
EXPECT_TRUE(certs_waiter_3.Get<1>().empty()); // Error map is empty.
ASSERT_EQ(
certs_waiter_3.Get<std::vector<scoped_refptr<const Cert>>>().size(),
1u);
}
if ((key_type_ == KeyType::kEcc) && NSS_VersionCheck("3.68") != PR_TRUE) {
// TODO(b/283925148): Old NSS crashes on an attempt to remove an ECC key.
// Most test running builders are up-to-date enough, but for the remaining
// few just skip the rest of the test.
return;
}
// Remove key and its certs.
{
base::test::TestFuture<base::expected<void, Error>> remove_waiter;
kcer_->RemoveKeyAndCerts(PrivateKeyHandle(public_key.value()),
remove_waiter.GetCallback());
EXPECT_TRUE(remove_waiter.Get().has_value());
EXPECT_TRUE(observer_.WaitUntil(/*notifications=*/4));
}
// Check that the cert cannot be found anymore.
{
base::test::TestFuture<std::vector<scoped_refptr<const Cert>>,
base::flat_map<Token, Error>>
certs_waiter;
kcer_->ListCerts({Token::kUser}, certs_waiter.GetCallback());
EXPECT_TRUE(certs_waiter.Get<1>().empty()); // Error map is empty.
ASSERT_EQ(certs_waiter.Get<std::vector<scoped_refptr<const Cert>>>().size(),
0u);
}
// Check that the key is not listed anymore.
if (key_can_be_listed_) {
base::test::TestFuture<std::vector<PublicKey>, base::flat_map<Token, Error>>
list_keys_waiter;
kcer_->ListKeys({Token::kUser}, list_keys_waiter.GetCallback());
ASSERT_TRUE(list_keys_waiter.Get<1>().empty()); // Error map is empty.
EXPECT_EQ(list_keys_waiter.Get<std::vector<PublicKey>>().size(), 0u);
}
}
INSTANTIATE_TEST_SUITE_P(AllKeyTypes,
KcerNssAllKeyTypesTest,
testing::Values(TestKeyType::kRsa,
TestKeyType::kEcc,
TestKeyType::kImportedRsa,
TestKeyType::kImportedEcc),
// Make test names more readable:
[](const auto& info) {
return TestKeyTypeToStr(info.param);
});
// These tests are different from the ones above because
// KcerTokenImplNss::ImportPkcs12Cert communicates with both NSS and Chaps, but
// it's difficult to implement a sufficiently realistic fake chaps for NSS to be
// able to work with it. So instead they mostly just cover that the requests to
// Chaps are correct and on a real device that would be enough for NSS to detect
// the imported certs and keys. Import of PKCS#12 files is also additionally
// covered by the CertSettingsPage* tast tests.
using KcerNssImportPkcs12Test = KcerNssTest;
std::vector<uint8_t> ReadTestFile(const std::string& file_name) {
base::FilePath file_path =
net::GetTestCertsDirectory().AppendASCII(file_name);
std::optional<std::vector<uint8_t>> file_data =
base::ReadFileToBytes(file_path);
if (!file_data.has_value()) {
ADD_FAILURE() << "Couldn't read " << file_path;
return {};
}
return file_data.value();
}
const std::vector<uint8_t>& GetPkcs12DataRsa() {
static std::vector<uint8_t> pkcs12_data = ReadTestFile("client.p12");
return pkcs12_data;
}
const std::vector<uint8_t>& GetPkcs12DataEc() {
static std::vector<uint8_t> pkcs12_data =
ReadTestFile("client_with_ec_key.p12");
return pkcs12_data;
}
const std::vector<uint8_t>& GetPkcs12DataWith2Certs() {
static std::vector<uint8_t> pkcs12_data =
ReadTestFile("2_client_certs_1_key.p12");
return pkcs12_data;
}
base::flat_map<uint32_t /*attribute_id*/, const chaps::Attribute*> MakeMap(
const chaps::AttributeList& attrs) {
base::flat_map<uint32_t, const chaps::Attribute*> result;
for (int i = 0; i < attrs.attributes_size(); ++i) {
result[attrs.attributes(i).type()] = &attrs.attributes(i);
}
if (base::checked_cast<size_t>(attrs.attributes_size()) != result.size()) {
ADD_FAILURE() << "Duplicate attributes detected";
}
return result;
}
[[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;
}
// PKCS#12 import: test that importing a file with a cert and an RSA key works.
TEST_F(KcerNssImportPkcs12Test, CertWithRsaKeySuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(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;
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(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;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/true,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
constexpr CK_OBJECT_CLASS kPrivKeyClass = CKO_PRIVATE_KEY;
constexpr CK_OBJECT_CLASS kPublicKeyClass = CKO_PUBLIC_KEY;
constexpr CK_OBJECT_CLASS kCertClass = CKO_CERTIFICATE;
constexpr CK_KEY_TYPE kKeyType = CKK_RSA;
constexpr CK_KEY_TYPE kCertType = CKC_X_509;
constexpr CK_BBOOL kTrue = CK_TRUE;
constexpr CK_BBOOL kFalse = CK_FALSE;
// 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
// on a real device, so most likely they are correct. Long term this is a
// regression test.
const std::vector<uint8_t> kModulus =
base::Base64Decode(
"1JC7k5aWwwOpqoiNzoRHLRdmzH9h4kVmFlBU/vZ5e7hCSnnIbVJilMxDB+p0b7ozw1/"
"bHvsRqikARkMc0OnC4EMnm6BEopqiyOnNGBy1qXwol5Mw8T8zwlzJl7FQdQdlH7pMxuI"
"D8hZEu8VkoEyLYJVJ1Ylaasc5BC0pHxZdNKk=")
.value();
const std::vector<uint8_t> kPkcs11Id =
base::Base64Decode("U65QueEa+ljfdKySfD6QbFrXEcM=").value();
const std::vector<uint8_t> kPublicExponent =
base::Base64Decode("AQAB").value();
const std::vector<uint8_t> kPrivateExponent =
base::Base64Decode(
"y/k2hiFy+h+BqArxSMLWKgbStlll7GL7212qsh6B5J6jviOumHj98BsyF1577"
"NqY4VoSQmBaSxadFM9Bz5cBT8IrKr2/FjL1AC+wgdwUvGvbD426zN4Yb59cTf/"
"bhNkvd2xocFPHeMDETFD6ISEcV6YLbPAtNlom7qVxlSTn1KE=")
.value();
const std::vector<uint8_t> kPrime1 =
base::Base64Decode(
"8W127p18wtuvUBxz7MtZgAPk/1OGLj1RJghuVYbHaCJ9sT5AzK8eNcRqCld/"
"bKABDdmYf3QHKYDx+vcrhcNF8w==")
.value();
const std::vector<uint8_t> kPrime2 =
base::Base64Decode(
"4WVKE2h5oF7HYpX2sLgHXFhM77k6Hb1MalKk1MvXSYeKLnFf1Xh4Af2tUR73RmG/Mp/"
"evvUMu6h4AvlGvn+18w==")
.value();
const std::vector<uint8_t> kExponent1 =
base::Base64Decode(
"SUZzCXstKaspq4PnP2B8upj0APalzBT6MzPt4PF2RknpokkFu9oOrjz9/"
"kOOPjbV+xEm8tAReGxVhVlNkVyyNw==")
.value();
const std::vector<uint8_t> kExponent2 =
base::Base64Decode(
"DkFqwvl7n9H+yFR1ys2I4aVQEGVlsJXVbHAXrsHJtwPUkIVpK0Y4SN/"
"zg0rzFsd94UTNQMSc7o2EMaP0fn3zUw==")
.value();
const std::vector<uint8_t> kCoefficient =
base::Base64Decode(
"mV2Q/My7RVOOsSZGDEouCYMcVahOFWS84IcpYRwR9ds0KZ4hKcdyMGNR5/"
"4ryvr9XMA+DBR/L9GBSWe6CeK3RQ==")
.value();
const std::vector<uint8_t> kCertDer =
base::Base64Decode(
"MIICpTCCAg6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTETMBEG"
"A1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk"
"MQ8wDQYDVQQDEwZ0ZXN0Y2EwIBcNMTAwNzMwMDEwMjEyWhgPMjA2MDA3MTcwMTAyMTJa"
"MFwxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl"
"cm5ldCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMTDHRlc3R1c2VyY2VydDCBnzANBgkq"
"hkiG9w0BAQEFAAOBjQAwgYkCgYEA1JC7k5aWwwOpqoiNzoRHLRdmzH9h4kVmFlBU/"
"vZ5e7hCSnnIbVJilMxDB+p0b7ozw1/"
"bHvsRqikARkMc0OnC4EMnm6BEopqiyOnNGBy1qXwol5Mw8T8zwlzJl7FQdQdlH7pMxuI"
"D8hZEu8VkoEyLYJVJ1Ylaasc5BC0pHxZdNKkCAwEAAaN7MHkwCQYDVR0TBAIwADAsBgl"
"ghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBY"
"EFHqEH18NKRVbhkqTT8swZq22Dc4YMB8GA1UdIwQYMBaAFE8aGkwMhipgaDysVMfu3Ja"
"N29ILMA0GCSqGSIb3DQEBBQUAA4GBAKMT7cwjZtgmkFrJPAa/"
"oOt1cdoBD7MqErx+tdvVN62q0h0Vl6UM3a94Ic0/"
"sv1V8RT5TUYUyyuepr2Gm58uqkcbI3qflveVcvi96n7fCCo6NwxbKHmpVOx+"
"wcPlHtjfek2KGQnee3mEN0YY/HOP5Rvj0Bh302kLrfgFx3xN1G5I")
.value();
const std::vector<uint8_t> kIssuerDer =
base::Base64Decode(
"MFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl"
"cm5l"
"dCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYQ==")
.value();
const std::vector<uint8_t> kSubjectDer =
base::Base64Decode(
"MFwxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl"
"cm5l"
"dCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMTDHRlc3R1c2VyY2VydA==")
.value();
const std::vector<uint8_t> kSerialDer = base::Base64Decode("AgEB").value();
const std::string kNickname = "testusercert";
{
base::flat_map<uint32_t, const chaps::Attribute*> find_key_map =
MakeMap(find_key_attrs);
EXPECT_TRUE(
SpanEqual(find_key_map[CKA_CLASS]->value(), MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(SpanEqual(find_key_map[CKA_ID]->value(), kPkcs11Id));
}
{
// Use NSS constants (e.g CKA_CLASS instead of chromeos::PKCS11_CKA_CLASS)
// to verify that the outcome will be compatible with NSS.
base::flat_map<uint32_t, const chaps::Attribute*> private_key_map =
MakeMap(private_key_attrs);
EXPECT_EQ(private_key_map.size(), 19u);
EXPECT_TRUE(SpanEqual(private_key_map[CKA_CLASS]->value(),
MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_KEY_TYPE]->value(), MakeSpan(&kKeyType)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_SENSITIVE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_EXTRACTABLE]->value(),
MakeSpan(&kFalse)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_PRIVATE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_UNWRAP]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_DECRYPT]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_SIGN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_SIGN_RECOVER]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_MODULUS]->value(), kModulus));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_PUBLIC_EXPONENT]->value(),
kPublicExponent));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_PRIVATE_EXPONENT]->value(),
kPrivateExponent));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_PRIME_1]->value(), kPrime1));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_PRIME_2]->value(), kPrime2));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_EXPONENT_1]->value(), kExponent1));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_EXPONENT_2]->value(), kExponent2));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_COEFFICIENT]->value(), kCoefficient));
EXPECT_FALSE(
base::Contains(private_key_map, chaps::kForceSoftwareAttribute));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> public_key_map =
MakeMap(public_key_attrs);
EXPECT_EQ(public_key_map.size(), 9u);
EXPECT_TRUE(SpanEqual(public_key_map[CKA_CLASS]->value(),
MakeSpan(&kPublicKeyClass)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_KEY_TYPE]->value(), MakeSpan(&kKeyType)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_WRAP]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_ENCRYPT]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_VERIFY]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_MODULUS]->value(), kModulus));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_PUBLIC_EXPONENT]->value(),
kPublicExponent));
EXPECT_FALSE(
base::Contains(public_key_map, chaps::kForceSoftwareAttribute));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> cert_map =
MakeMap(cert_attrs);
EXPECT_EQ(cert_map.size(), 9u);
EXPECT_TRUE(SpanEqual(cert_map[CKA_CLASS]->value(), MakeSpan(&kCertClass)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_CERTIFICATE_TYPE]->value(),
MakeSpan(&kCertType)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_TRUE(
SpanEqual(cert_map[CKA_LABEL]->value(), base::as_byte_span(kNickname)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_VALUE]->value(), kCertDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_ISSUER]->value(), kIssuerDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_SUBJECT]->value(), kSubjectDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_SERIAL_NUMBER]->value(), kSerialDer));
}
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 1)));
}
}
// PKCS#12 import: test that imported certs and RSA keys will be marked
// as software-backed and migrated when necessary.
TEST_F(KcerNssImportPkcs12Test, CertWithRsaKeyAndExtraArgsSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
chaps::AttributeList private_key_attrs;
chaps::AttributeList public_key_attrs;
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(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;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
constexpr CK_BBOOL kTrue = CK_TRUE;
{
base::flat_map<uint32_t, const chaps::Attribute*> private_key_map =
MakeMap(private_key_attrs);
EXPECT_EQ(private_key_map.size(), 21u);
EXPECT_TRUE(
SpanEqual(private_key_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_EXTRACTABLE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> public_key_map =
MakeMap(public_key_attrs);
EXPECT_EQ(public_key_map.size(), 11u);
EXPECT_TRUE(
SpanEqual(public_key_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(public_key_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> cert_map =
MakeMap(cert_attrs);
EXPECT_EQ(cert_map.size(), 11u);
EXPECT_TRUE(SpanEqual(cert_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(cert_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 1)));
}
}
// PKCS#12 import: test that importing a file with a cert and an EC key works.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12CertEcSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
chaps::AttributeList find_key_attrs;
EXPECT_CALL(chaps_client_, FindObjects(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;
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(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;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataEc()),
kPkcs12EcFilePassword, /*hardware_backed=*/true,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
constexpr CK_OBJECT_CLASS kPrivKeyClass = CKO_PRIVATE_KEY;
constexpr CK_OBJECT_CLASS kPublicKeyClass = CKO_PUBLIC_KEY;
constexpr CK_OBJECT_CLASS kCertClass = CKO_CERTIFICATE;
constexpr CK_KEY_TYPE kKeyType = CKK_EC;
constexpr CK_KEY_TYPE kCertType = CKC_X_509;
constexpr CK_BBOOL kTrue = CK_TRUE;
constexpr CK_BBOOL kFalse = CK_FALSE;
// 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
// on a real device, so most likely they are correct. Long term this is a
// regression test.
const std::vector<uint8_t> kEcPoint =
base::Base64Decode(
"BEEE/"
"4hAEQ+bd7cAFAyFBpmUTTDyoiOfS0ofqNMR6RC+"
"0qhSEWjaczhD1UDcwuWNUXu9ptXwK4f39SQq3YUyDaIcYw==")
.value();
const std::vector<uint8_t> kEcParams =
base::Base64Decode("BggqhkjOPQMBBw==").value();
const std::vector<uint8_t> kPkcs11Id =
base::Base64Decode("9kVFdOhn8yYso7a/wG2uC0wdHWo=").value();
const std::vector<uint8_t> kPrivateKeyValue =
base::Base64Decode("fvWtrgVAq5JApBuCPK92IUAQQnnEoLUrBgZ/KGFhz7E=")
.value();
const std::vector<uint8_t> kCertDer =
base::Base64Decode(
"MIIB2zCCAX+"
"gAwIBAgIEFhkiazAMBggqhkjOPQQDBAUAMGIxCzAJBgNVBAYTAkRFMQswCQYDVQQIEwJ"
"CWTEMMAoGA1UEBxMDTXVjMRMwEQYDVQQKEwpDb21tZXJjaWFsMRMwEQYDVQQLEwpOZXR"
"3b3JraW5nMQ4wDAYDVQQDEwVDTmFtZTAeFw0yMzExMDIxODQ3MzBaFw0yNDExMDExODQ"
"3MzBaMGIxCzAJBgNVBAYTAkRFMQswCQYDVQQIEwJCWTEMMAoGA1UEBxMDTXVjMRMwEQY"
"DVQQKEwpDb21tZXJjaWFsMRMwEQYDVQQLEwpOZXR3b3JraW5nMQ4wDAYDVQQDEwVDTmF"
"tZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP+"
"IQBEPm3e3ABQMhQaZlE0w8qIjn0tKH6jTEekQvtKoUhFo2nM4Q9VA3MLljVF7vabV8Cu"
"H9/UkKt2FMg2iHGOjITAfMB0GA1UdDgQWBBT2RUV06GfzJiyjtr/"
"Aba4LTB0dajAMBggqhkjOPQQDBAUAA0gAMEUCIQCn1ViT++"
"SLXVv4sExxORcVHVGtyCp6HLVVkJW0IP+"
"SawIgSqGQMDoVRFHq6Zel10xDQM8AB0814PJK37LXQZMjRLg=")
.value();
const std::vector<uint8_t> kIssuerDer =
base::Base64Decode(
"MGIxCzAJBgNVBAYTAkRFMQswCQYDVQQIEwJCWTEMMAoGA1UEBxMDTXVjMRMwEQYDVQQK"
"EwpDb21tZXJjaWFsMRMwEQYDVQQLEwpOZXR3b3JraW5nMQ4wDAYDVQQDEwVDTmFtZQ="
"=")
.value();
const std::vector<uint8_t> kSubjectDer =
base::Base64Decode(
"MGIxCzAJBgNVBAYTAkRFMQswCQYDVQQIEwJCWTEMMAoGA1UEBxMDTXVjMRMwEQYDVQQK"
"EwpDb21tZXJjaWFsMRMwEQYDVQQLEwpOZXR3b3JraW5nMQ4wDAYDVQQDEwVDTmFtZQ="
"=")
.value();
const std::vector<uint8_t> kSerialDer =
base::Base64Decode("AgQWGSJr").value();
const std::string kNickname = "serverkey";
{
base::flat_map<uint32_t, const chaps::Attribute*> find_key_map =
MakeMap(find_key_attrs);
EXPECT_TRUE(
SpanEqual(find_key_map[CKA_CLASS]->value(), MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(SpanEqual(find_key_map[CKA_ID]->value(), kPkcs11Id));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> private_key_map =
MakeMap(private_key_attrs);
EXPECT_EQ(private_key_map.size(), 13u);
EXPECT_TRUE(SpanEqual(private_key_map[CKA_CLASS]->value(),
MakeSpan(&kPrivKeyClass)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_KEY_TYPE]->value(), MakeSpan(&kKeyType)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_SENSITIVE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_EXTRACTABLE]->value(),
MakeSpan(&kFalse)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_PRIVATE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_SIGN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_SIGN_RECOVER]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_DERIVE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_EC_POINT]->value(), kEcPoint));
EXPECT_TRUE(SpanEqual(private_key_map[CKA_EC_PARAMS]->value(), kEcParams));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_VALUE]->value(), kPrivateKeyValue));
EXPECT_FALSE(
base::Contains(private_key_map, chaps::kForceSoftwareAttribute));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> public_key_map =
MakeMap(public_key_attrs);
EXPECT_EQ(public_key_map.size(), 8u);
EXPECT_TRUE(SpanEqual(public_key_map[CKA_CLASS]->value(),
MakeSpan(&kPublicKeyClass)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_KEY_TYPE]->value(), MakeSpan(&kKeyType)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_VERIFY]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(public_key_map[CKA_DERIVE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_EC_PARAMS]->value(), kEcParams));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_EC_POINT]->value(), kEcPoint));
EXPECT_TRUE(SpanEqual(public_key_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_FALSE(
base::Contains(public_key_map, chaps::kForceSoftwareAttribute));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> cert_map =
MakeMap(cert_attrs);
EXPECT_EQ(cert_map.size(), 9u);
EXPECT_TRUE(SpanEqual(cert_map[CKA_CLASS]->value(), MakeSpan(&kCertClass)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_CERTIFICATE_TYPE]->value(),
MakeSpan(&kCertType)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_TOKEN]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_ID]->value(), kPkcs11Id));
EXPECT_TRUE(
SpanEqual(cert_map[CKA_LABEL]->value(), base::as_byte_span(kNickname)));
EXPECT_TRUE(SpanEqual(cert_map[CKA_VALUE]->value(), kCertDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_ISSUER]->value(), kIssuerDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_SUBJECT]->value(), kSubjectDer));
EXPECT_TRUE(SpanEqual(cert_map[CKA_SERIAL_NUMBER]->value(), kSerialDer));
}
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcCertImportTask, 1)));
}
}
// PKCS#12 import: test that imported certs and EC keys will be marked
// as software-backed and migrated when necessary.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12CertEcWithExtraArgsSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
chaps::AttributeList private_key_attrs;
chaps::AttributeList public_key_attrs;
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject(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;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataEc()),
kPkcs12EcFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
constexpr CK_BBOOL kTrue = CK_TRUE;
{
base::flat_map<uint32_t, const chaps::Attribute*> private_key_map =
MakeMap(private_key_attrs);
EXPECT_EQ(private_key_map.size(), 15u);
EXPECT_TRUE(
SpanEqual(private_key_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(
SpanEqual(private_key_map[CKA_EXTRACTABLE]->value(), MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(private_key_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> public_key_map =
MakeMap(public_key_attrs);
EXPECT_EQ(public_key_map.size(), 10u);
EXPECT_TRUE(
SpanEqual(public_key_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(public_key_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
base::flat_map<uint32_t, const chaps::Attribute*> cert_map =
MakeMap(cert_attrs);
EXPECT_EQ(cert_map.size(), 11u);
EXPECT_TRUE(SpanEqual(cert_map[chaps::kForceSoftwareAttribute]->value(),
MakeSpan(&kTrue)));
EXPECT_TRUE(SpanEqual(cert_map[kCkaChromeOsMigratedFromNss]->value(),
MakeSpan(&kTrue)));
}
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcCertImportTask, 1)));
}
}
// PKCS#12 import: test that the key is not imported again if it already exists.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12KeyExistsSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK));
chaps::AttributeList cert_attrs;
EXPECT_CALL(chaps_client_, CreateObject)
.WillOnce(
DoAll(MoveArg<1>(&cert_attrs),
RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK)));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
constexpr CK_OBJECT_CLASS kCertClass = CKO_CERTIFICATE;
EXPECT_TRUE(FindAttribute(cert_attrs, CKA_CLASS, MakeSpan(&kCertClass)));
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 1)));
}
}
// PKCS#12 import: test that the import fails correctly when Chaps fails to
// check whether the key already exists.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12FailToCheckKeyExists) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToSearchForObjects);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import is retried correctly when Chaps fails to
// check whether the key already exists with a session error.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12RetryToCheckKeyExists) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
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>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kPkcs11SessionFailure);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import fails correctly when Chaps fails to
// create private key.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12FailToCreatePrivKey) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
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));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportKey);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import is retried correctly when Chaps fails to
// create private key with a session error.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12RetryToCreatePrivKey) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
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));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kPkcs11SessionFailure);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import fails correctly when Chaps fails to
// create private key.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12FailToCreatePubKey) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
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(
slot_id, std::vector<ObjectHandle>{priv_key_handle}, _))
.WillOnce(RunOnceCallback<2>(chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataRsa()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
ASSERT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportKey);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import fails correctly when Chaps fails to
// create a cert.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12FailToCreateCert) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(3),
chromeos::PKCS11_CKR_GENERAL_ERROR));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataEc()),
kPkcs12EcFilePassword, /*hardware_backed=*/true,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
EXPECT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportCertificate);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcCertImportTask, 0)));
}
}
// PKCS#12 import: test that the import is retried correctly when Chaps fails to
// create a cert with a session error.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12RetryToCreateCert) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
// Simulate that the key is found to skip its creation for simplicity.
EXPECT_CALL(chaps_client_, FindObjects)
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>{ObjectHandle(1)},
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.Times(kDefaultAttempts)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
ObjectHandle(3), chromeos::PKCS11_CKR_SESSION_CLOSED));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataEc()),
kPkcs12EcFilePassword, /*hardware_backed=*/true,
/*mark_as_migrated=*/false,
import_waiter.GetCallback());
EXPECT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kPkcs11SessionFailure);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
6),
base::Bucket(KcerPkcs12ImportEvent::AttemptedEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcCertImportTask, 0)));
}
}
// PKCS#12 import: test that importing a file with two cert and a key works.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12With2CertsSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
chaps::AttributeList cert_1_attrs;
chaps::AttributeList cert_2_attrs;
EXPECT_CALL(chaps_client_, CreateObject(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK))
.WillOnce(
DoAll(MoveArg<1>(&cert_1_attrs),
RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK)))
.WillOnce(
DoAll(MoveArg<1>(&cert_2_attrs),
RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK)));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataWith2Certs()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
base::flat_map<uint32_t, const chaps::Attribute*> cert_map_1 =
MakeMap(cert_1_attrs);
base::flat_map<uint32_t, const chaps::Attribute*> cert_map_2 =
MakeMap(cert_2_attrs);
EXPECT_EQ(cert_map_1.size(), 11u);
EXPECT_EQ(cert_map_2.size(), 11u);
constexpr CK_OBJECT_CLASS kCertClass = CKO_CERTIFICATE;
EXPECT_TRUE(SpanEqual(cert_map_1[CKA_CLASS]->value(), MakeSpan(&kCertClass)));
EXPECT_TRUE(SpanEqual(cert_map_2[CKA_CLASS]->value(), MakeSpan(&kCertClass)));
// Check that two different certs were created.
EXPECT_NE(cert_map_1[CKA_VALUE]->value(), cert_map_2[CKA_VALUE]->value());
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 1)));
}
}
// PKCS#12 import: test that Kcer tries to import as many certs as possible and
// returns an error if at least one failed.
TEST_F(KcerNssImportPkcs12Test, ImportPkcs12With2CertsSemiSuccess) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(std::vector<ObjectHandle>(),
chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject(slot_id, _, _))
.WillOnce(RunOnceCallback<2>(ObjectHandle(1), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(2), chromeos::PKCS11_CKR_OK))
.WillOnce(RunOnceCallback<2>(ObjectHandle(0),
chromeos::PKCS11_CKR_GENERAL_ERROR))
.WillOnce(RunOnceCallback<2>(ObjectHandle(3), chromeos::PKCS11_CKR_OK));
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataWith2Certs()),
kPkcs12RsaFilePassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kFailedToImportCertificate);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaKeyImportTask, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessRsaCertImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedMultipleCertImport,
1)));
}
}
// PKCS#12 import: test that Kcer return the correct error when a wrong password
// for a PKCS#12 file is provided.
TEST_F(KcerNssImportPkcs12Test, WrongPassword) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
const char kWrongPassword[] = "00000000";
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(Token::kUser, Pkcs12Blob(GetPkcs12DataWith2Certs()),
kWrongPassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true,
import_waiter.GetCallback());
EXPECT_FALSE(import_waiter.Get().has_value());
EXPECT_EQ(import_waiter.Get().error(), Error::kPkcs12WrongPassword);
{
// Check UMA metrics recorded.
EXPECT_THAT(
histogram_tester_.GetAllSamples(
kcer::internal::KcerPkcs12ImportMetrics),
BucketsInclude(
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImport, 1),
base::Bucket(KcerPkcs12ImportEvent::SuccessPkcs12ChapsImport, 0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedPkcs12ChapsImportTask,
0),
base::Bucket(KcerPkcs12ImportEvent::AttemptedRsaKeyImportTask, 0),
base::Bucket(KcerPkcs12ImportEvent::SuccessEcKeyImportTask, 0)));
}
}
// PKCS#12 import: test that Kcer correctly handles files with empty passwords.
// An "empty" password can mean a a zero length password or no password.
TEST_F(KcerNssImportPkcs12Test, EmptyPassword) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>{ObjectHandle(1)}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(ObjectHandle(1),
chromeos::PKCS11_CKR_OK));
const char kEmptyPassword[] = "";
{
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(
Token::kUser, Pkcs12Blob(ReadTestFile("client-null-password.p12")),
kEmptyPassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true, import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
}
{
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(
Token::kUser, Pkcs12Blob(ReadTestFile("client-empty-password.p12")),
kEmptyPassword, /*hardware_backed=*/false,
/*mark_as_migrated=*/true, import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
}
}
// PKCS#12 import: test that Kcer correctly handles files with passwords that
// contain wide characters.
TEST_F(KcerNssImportPkcs12Test, NonAsciiPassword) {
InitializeKcer({Token::kUser});
SlotId slot_id(user_token_->GetSlotId());
EXPECT_CALL(chaps_client_, FindObjects)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(
std::vector<ObjectHandle>{ObjectHandle(1)}, chromeos::PKCS11_CKR_OK));
EXPECT_CALL(chaps_client_, CreateObject)
.WillRepeatedly(RunOnceCallbackRepeatedly<2>(ObjectHandle(1),
chromeos::PKCS11_CKR_OK));
std::vector<uint8_t> pkcs12_data = ReadTestFile("client_1_u16_password.p12");
// Incorrect password should be rejected.
{
const std::string kNonAsciiPassword = "Wrong Password, Hello, 世界";
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(
Token::kUser, Pkcs12Blob(pkcs12_data), kNonAsciiPassword,
/*hardware_backed=*/false,
/*mark_as_migrated=*/true, import_waiter.GetCallback());
EXPECT_FALSE(import_waiter.Get().has_value());
}
// Correct password should be accepted.
{
const std::string kNonAsciiPassword = "Hello, 世界";
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(
Token::kUser, Pkcs12Blob(pkcs12_data), kNonAsciiPassword,
/*hardware_backed=*/false,
/*mark_as_migrated=*/true, import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
}
// Chrome currently converts a std::u16string into std::string at some point
// using the helper from base::, double check that the helper works as
// expected.
{
const std::u16string kUtf16NonAsciiPassword = u"Hello, 世界";
const std::string kConvertedPassword =
base::UTF16ToUTF8(kUtf16NonAsciiPassword);
base::test::TestFuture<base::expected<void, Error>> import_waiter;
kcer_->ImportPkcs12Cert(
Token::kUser, Pkcs12Blob(pkcs12_data), kConvertedPassword,
/*hardware_backed=*/false,
/*mark_as_migrated=*/true, import_waiter.GetCallback());
EXPECT_TRUE(import_waiter.Get().has_value());
}
}
} // namespace
} // namespace kcer