blob: fb9696997d4e5551c66410d5891c1a5c579a6299 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/arc/net/cert_manager_impl.h"
#include <optional>
#include <string>
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/test/test_future.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "crypto/scoped_test_nss_db.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/scoped_nss_types.h"
#include "net/cert/x509_util_nss.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/pki/pem.h"
namespace arc {
namespace {
constexpr char kPrivateKey[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXt3N1RS4Ntj7o\n"
"HucHcqO+nRuf/3dK1zDxhtnyrjBFBuhTTvNOL2Njm/LvB5EaaIc2UhavIPnnQEAt\n"
"OmhvEsi8A3HB4EU+Pu6UJnUtmEPMC/9WTZnLYAA/gMKYZ8KPZQ1FqNi3pkeWcxTN\n"
"twnTDEK4qtd6+1veqWTYuxU6IUNrE4GX1yhSV4fAq6PKqdTz7VLIZ/wasADMMZ/S\n"
"o3/MDR7rHH9hVBPby6liEunXUjzT7L5t+ZN3vUejOlcqdfikuB73oPitZ1vfQ3Ux\n"
"2+67hWMLXFrw+4JkBMxyHL2fLfGwczRMy82UVIgqJMYAOM5iTQeQcxVlqwAyEhFU\n"
"XSXSF4PFAgMBAAECggEBAMF/spa39oagOq92wOACanVacmREARrmCuYsg6ZXr77L\n"
"Ym0QPdmdUncQdYsKa5OXvenxGp3/Y4uXK7omUXWJEPztzgYOCa67PsEv+h5rHi2T\n"
"eXhN5a3zsGVGN8gEExcTmyMoQTYDduWy1y9sh+iDb/o8bUvI23DQ3EA5GOJq4hHR\n"
"6OODdEJiyDNXsE4cQ8lJ4/mGF+RS1cBkNHRWpZzWT3fZNSUQtMgYMrz72UR+usYY\n"
"OzCQPLNPCYgUXUp3caR16qKxOMQgZKEoAAxxVooASnvmuQiMUEY81rJlCeFXa+h9\n"
"/eN2eKUnwNac0kWf1SjFQEiFutdbyuVTDIg22UPnFsUCgYEA7mcLHBts1jmBp405\n"
"UoMGmckfJecaX1VE2HcYUXrNOjOCD7LEQRo6yOYF7VfgM0LzzYOit7LN7ouZ6Hgt\n"
"3zTuMVdvnyiD3BjgogjtppQ7LV/ALThSN+bApOAMtX6yF3QSVGPd7TpfrBz0/ROj\n"
"8bcJ+3hpBIq44LS2Si3Mo3QWcecCgYEA56O5lJUxC6cDyZHM+k+AiLTCAA2A+L/P\n"
"ToixW9xyssxsrVNtXXr+YYowGf/cgR3kAJ6NcYwFJfsJ27IFgbpX3pCFVAJF4bSb\n"
"feE5e7qnYF4NSJspOEr/17VoFAxk7INO6yW7fNSirpY402L1ldQRa7lk5Qmvucj5\n"
"TSBXB4fhv3MCgYBezEKyroUcuklAIvwEP23EgSENpVPrTLDPkqvs2nP5DLpPG7rG\n"
"WHO/pxf8RNE2EQ15TzrI6STSEljlA8TZ2OZOYIJWO3oTbyEDzaESeCb/5+83DApF\n"
"iFBaP21OTk7q3JDdVcjNqESa3/jbGZA7cZlakYrQ74iMcc96t7OD24mBSQKBgQCm\n"
"14J/xsXAwtczhFTDpifKT4e8Sf2vLVjAFCzLIYlrx1ovrXuEbWZ0Evh6gZPtW/4x\n"
"hAIU2umKZbrABwV4XyOTJz0hOVHkNBYbIPIqcFLGUnf25+tUpJCKahtA9Xxr7lgV\n"
"fuQAEZfrcEAV4Z1KAalakfpeDhAIHP2T08tbnT+4iQKBgEI4YfylyTsMAc5RVVE7\n"
"CIW3Gn3JSuWm3RRrDSeX3hJIZ8R7pF2rKYx7QYZi4p0fGny4Xw4bD1RWJwt9mRuE\n"
"WYG/EA9Q2PLsfyd8SmUTbnXU6JYUalu3apECNGg2EoXHk9OODhqYphlzSNEHP0LT\n"
"fuHaboQVbHNmCQR8DRZgzGH/\n"
"-----END PRIVATE KEY-----";
constexpr char kWrongPrivateKey[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZmo7mtBcxsvMp\n"
"T5a/p3L/1byrgyAd4zIDR3ivjf6anNlMVNTGSxdtrvCCRxMZPIrEOlq8kIzFqvXn\n"
"kZNzZms/ofrQ0c0K7aXhaH+zH3x7eMMZLCyl/gBjhThZac6BtlP6+muHCTL3EKLO\n"
"qpPFpkYjnYI3g2qj5dgDJcfhqF0RL5CXHSdHWc2OSK2C5krqT4nPu8tKhHiAOLRW\n"
"BBJQ3jpl7rlH1SqKtba3ppu4AioOTtlHfCcR0XZ3ym55QGufIci2yr9pvCwVRwXO\n"
"VwisNEyRmfEOXuSUyx6ZZy/vDeqtwLSuSL19gMsTk+HK1dpijnwzlowOwo+7ixZ4\n"
"G9g9jjivAgMBAAECggEAdcS1ZFzBVM+B1LDTaIRqs9Vsl/KOlj5Y2fd7dJ/H1Mvg\n"
"uvQKeAs58c3FMuzehEEE5TCj3PvqhCyDi8F46PLcRoMW6J8zdp+psDXLLxlyWKzC\n"
"AkSrIWc3tKTsG1AtSHxyNRoEyf+LirWBN5KQCV91BF+BkyPXuj5xyzpOVG23eM2i\n"
"QDdvvhqetGT0coM0ZrxyTO8hOqtwuBIV+b+PUDXHfiGbIhkt+9ZJL8T0WXOoHNII\n"
"lrf9t06FNFiFnSwEGhBIi/BiZ83wgJV67IH4yUO3VHYqRSfQCNWNfzqeOUc3X7VU\n"
"eiMyQw//shq9Wmx9R70crLDP6B/8voKMb/CCUK8DmQKBgQD85rtWx1IzdkDaDhNX\n"
"samV7JKppeUijTc/jv6K18EAyWa7iICWwcUw18eRWSYq4JbWF3936OX4RLeZmUoI\n"
"4jqRAH9Xlnfg7ceygKunKXLlm+TFpyBBtlrGsYhUb1kUskYdPgvY/5NpI9HsE1hy\n"
"xPZBqoQ3u3wGJxTdQPsh35moDQKBgQDcRRwOkxTa0ijkDapyGXDJrR6fF9Tm2HJJ\n"
"wTNLnLtxjFseHs1ZKefY5dbTeISNRlCye3xhrVTsbV1YlVItQVBfi1Qv1lMUhU3j\n"
"e06Ra1aKsVUDBMgv8jJBZU3Jx8ytkltq42HSbKk1BzCheNtfV7YXi8vcJV9fwCIa\n"
"ZI45BKDYqwKBgDYHUQSEBqKp48bx9N3qPbGi3d5Sa7ZK9v+kG+srlrcFT+ZGjjom\n"
"4WrC3obFxeqpGnBYisniPqcgfxzYa8GkGyD5OztKEQhDpEMVTBalOz+kY2Z6guCn\n"
"BZOnP9nSA/Tw9RuwMrXEPAjdNy65H089luKGfEKv0ho6ZTGzfTNKYrhNAoGARlDF\n"
"gR2Qxb3bEdoO9DeM2sSqBs17yGmGKmdDcbrJ15ifqcDZesI24fWVG5LYdaThs+hZ\n"
"r3C+sG7FIrcgMZQtDSMUL+UyRlW7pIfDcAac7M9pPPp00WF2i4vERkrC2xHinv+R\n"
"RbQsW+I8sv86wHfmiCO3Y0KG7LEP8e7xu9/vXNsCgYEA7/EI7EklUB4jIpEJ7oiq\n"
"IWIaEaUp6kcopTAKinDpTfVkKZRxtCu70SQL+55ovINA8UvrArGXiCwRfMIW/+Vv\n"
"vO6JLBdJU9XKpN+/OP6kdAivlQLuZNIlrMG8eP80KoV4ckQh0Gp2yNoLC+Vuyfjc\n"
"iow7aHtm4m4vjWVETAHFviw=\n"
"-----END PRIVATE KEY-----";
constexpr char kUserCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDazCCAlOgAwIBAgIUVLGPFfkgeQbMUT0k/FX3sL101dAwDQYJKoZIhvcNAQEL\n"
"BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\n"
"GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAyMTYxMzIwMjdaFw0yMjAz\n"
"MTgxMzIwMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\n"
"HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB\n"
"AQUAA4IBDwAwggEKAoIBAQDXt3N1RS4Ntj7oHucHcqO+nRuf/3dK1zDxhtnyrjBF\n"
"BuhTTvNOL2Njm/LvB5EaaIc2UhavIPnnQEAtOmhvEsi8A3HB4EU+Pu6UJnUtmEPM\n"
"C/9WTZnLYAA/gMKYZ8KPZQ1FqNi3pkeWcxTNtwnTDEK4qtd6+1veqWTYuxU6IUNr\n"
"E4GX1yhSV4fAq6PKqdTz7VLIZ/wasADMMZ/So3/MDR7rHH9hVBPby6liEunXUjzT\n"
"7L5t+ZN3vUejOlcqdfikuB73oPitZ1vfQ3Ux2+67hWMLXFrw+4JkBMxyHL2fLfGw\n"
"czRMy82UVIgqJMYAOM5iTQeQcxVlqwAyEhFUXSXSF4PFAgMBAAGjUzBRMB0GA1Ud\n"
"DgQWBBS1UScRUjNDA88mUXUuzwOJ35aCnDAfBgNVHSMEGDAWgBS1UScRUjNDA88m\n"
"UXUuzwOJ35aCnDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCf\n"
"gcfXjLfaOots4AFtd4sQglWA0RaIfaZl1QTAp6QvJQY7jFyyeNuYV3DfT2DCzJOY\n"
"NwRluNCfUZG/YbpTtMUDODpDjASF0z9kQc1bVg3NdcscI+LFtMivuvG7v3Bp7G3I\n"
"bhnbhDmWRU9Wss4P3F7x2EULX6NwUzcmyHtEQ+A9xjm6BpshCx3qZNEOYFqV7U82\n"
"ioxoifZ5JDF7fIkF22rsI+Ufo3mMvjYz5vSRxc0OVmbpfgmnhB5ValEAopwP5FN3\n"
"Jn8C5u+DExI0s93xzNMmJL0ONVeQORY8YkV+7E8wuD+VLSuoo4S/VZ108DRl+kxx\n"
"e8AkIK+5yjLpD/0P4TN0\n"
"-----END CERTIFICATE-----";
class ImportDoneWaiter
: public base::test::TestFuture<std::optional<std::string>,
std::optional<int>> {
public:
CertManager::ImportPrivateKeyAndCertCallback GetCallback() {
return TestFuture::GetCallback<const std::optional<std::string>&,
const std::optional<int>&>();
}
std::optional<std::string> imported_cert_id() { return Get<0>(); }
std::optional<int> imported_slot_id() { return Get<1>(); }
};
} // namespace
class CertManagerImplTest : public testing::Test {
public:
CertManagerImplTest() = default;
CertManagerImplTest(const CertManagerImplTest&) = delete;
CertManagerImplTest& operator=(const CertManagerImplTest&) = delete;
~CertManagerImplTest() override = default;
void SetUp() override {
profile_ = std::make_unique<TestingProfile>();
cert_manager_ = std::make_unique<CertManagerImpl>(profile());
nss_db_ = std::make_unique<crypto::ScopedTestNSSDB>();
cert_db_ = std::make_unique<net::NSSCertDatabase>(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())),
crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())));
}
void TearDown() override {
// Ensure that nothing is running before tearing down.
base::RunLoop().RunUntilIdle();
profile_.reset();
cert_manager_.reset();
cert_db_.reset();
nss_db_.reset();
}
Profile* profile() { return profile_.get(); }
CertManagerImpl* cert_manager() { return cert_manager_.get(); }
net::NSSCertDatabase* cert_db() { return cert_db_.get(); }
private:
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<CertManagerImpl> cert_manager_;
std::unique_ptr<crypto::ScopedTestNSSDB> nss_db_;
std::unique_ptr<net::NSSCertDatabase> cert_db_;
content::BrowserTaskEnvironment task_environment_;
};
// Imports with a valid certificate and key succeed.
TEST_F(CertManagerImplTest, ImportKeyAndCertTest) {
ImportDoneWaiter import_future;
cert_manager()->ImportPrivateKeyAndCertWithDB(
kPrivateKey, kUserCert, import_future.GetCallback(), cert_db());
EXPECT_TRUE(import_future.Wait());
EXPECT_TRUE(import_future.imported_cert_id().has_value());
EXPECT_TRUE(import_future.imported_slot_id().has_value());
// Assert that the imported key and certificate have the same ID.
bssl::PEMTokenizer tokenizer(kUserCert, {kCertificatePEMHeader});
EXPECT_TRUE(tokenizer.GetNext());
std::vector<uint8_t> cert_der(tokenizer.data().begin(),
tokenizer.data().end());
net::ScopedCERTCertificate cert(
net::x509_util::CreateCERTCertificateFromBytes(cert_der));
// Get the certificate ID.
crypto::ScopedSECItem cert_sec_item(
PK11_GetLowLevelKeyIDForCert(nullptr, cert.get(), nullptr));
EXPECT_TRUE(cert_sec_item);
std::string cert_id =
base::HexEncode(cert_sec_item->data, cert_sec_item->len);
// Get the key ID.
crypto::ScopedPK11Slot private_slot = cert_db()->GetPrivateSlot();
EXPECT_TRUE(private_slot);
crypto::ScopedSECKEYPrivateKey key(
PK11_FindPrivateKeyFromCert(private_slot.get(), cert.get(), nullptr));
EXPECT_TRUE(key);
crypto::ScopedSECItem key_sec_item(
PK11_GetLowLevelKeyIDForPrivateKey(key.get()));
EXPECT_TRUE(key_sec_item);
std::string key_id = base::HexEncode(key_sec_item->data, key_sec_item->len);
EXPECT_EQ(key_id, cert_id);
EXPECT_EQ(import_future.imported_cert_id().value(), cert_id);
EXPECT_EQ(import_future.imported_slot_id().value(),
static_cast<int>(PK11_GetSlotID(private_slot.get())));
}
// Importing a certificate with the wrong key fail.
TEST_F(CertManagerImplTest, ImportCertWithWrongKeyTest) {
ImportDoneWaiter import_future;
cert_manager()->ImportPrivateKeyAndCertWithDB(
kWrongPrivateKey, kUserCert, import_future.GetCallback(), cert_db());
EXPECT_TRUE(import_future.Wait());
EXPECT_FALSE(import_future.imported_cert_id().has_value());
EXPECT_FALSE(import_future.imported_slot_id().has_value());
}
// Imports with invalid certificate database fail.
TEST_F(CertManagerImplTest, ImportKeyAndCertWithInvalidDBTest) {
ImportDoneWaiter import_future;
cert_manager()->ImportPrivateKeyAndCertWithDB(kPrivateKey, kUserCert,
import_future.GetCallback(),
/*database=*/nullptr);
EXPECT_TRUE(import_future.Wait());
EXPECT_FALSE(import_future.imported_cert_id().has_value());
EXPECT_FALSE(import_future.imported_slot_id().has_value());
}
// Imports with invalid certificates or keys fail.
TEST_F(CertManagerImplTest, ImportInvalidDataTest) {
ImportDoneWaiter import_future;
cert_manager()->ImportPrivateKeyAndCertWithDB(
/*key_pem=*/"", /*cert_pem=*/"", import_future.GetCallback(), cert_db());
EXPECT_TRUE(import_future.Wait());
EXPECT_FALSE(import_future.imported_cert_id().has_value());
EXPECT_FALSE(import_future.imported_slot_id().has_value());
}
} // namespace arc