Skip to content

Commit

Permalink
Kcer-over-NSS: Initial partial implementation
Browse files Browse the repository at this point in the history
Upload initial interface for Kcer.

Partially implement Kcer that relies on the KcerToken
abstraction.
Partially implement KcerTokenImplNss, which is an
implementation of KcerToken that uses NSS as a permanent
storage.

The focus of this CL is to introduce common infrastructure
that will be used by all the other methods.
The implemented methods and tests are present primarily
as a demonstration that the infrastructure works.
The introduced code is not used anywhere yet.

Bug: b:244408716
Test: KcerNssTest*
Change-Id: I3195d9e77ae8ae7d3610b38fd5d632f5bf4b9923
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4312022
Reviewed-by: Pavol Marko <pmarko@chromium.org>
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Reviewed-by: David Benjamin <davidben@chromium.org>
Commit-Queue: Michael Ershov <miersh@google.com>
Cr-Commit-Position: refs/heads/main@{#1132464}
  • Loading branch information
Michael Ershov authored and Chromium LUCI CQ committed Apr 19, 2023
1 parent 7538696 commit 008eeab
Show file tree
Hide file tree
Showing 29 changed files with 2,593 additions and 25 deletions.
9 changes: 9 additions & 0 deletions chrome/browser/chromeos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ source_set("chromeos") {
"extensions/login_screen/login_state/session_state_changed_event_dispatcher.h",
"extensions/system_log/system_log_api.cc",
"extensions/system_log/system_log_api.h",
"kcer_nss/cert_cache_nss.cc",
"kcer_nss/cert_cache_nss.h",
"kcer_nss/kcer_token_impl_nss.cc",
"kcer_nss/kcer_token_impl_nss.h",
"platform_keys/chaps_slot_session.cc",
"platform_keys/chaps_slot_session.h",
"platform_keys/chaps_util.cc",
Expand Down Expand Up @@ -246,6 +250,7 @@ source_set("chromeos") {
"//chrome/common:chrome_features",
"//chrome/common:constants",
"//chrome/common:non_code_constants",
"//chromeos/components/kcer",
"//chromeos/dbus/dlp",
"//chromeos/ui/base",
"//components/app_constants",
Expand All @@ -268,6 +273,7 @@ source_set("chromeos") {
"//extensions/browser/updater",
"//extensions/common:common_constants",
"//extensions/common:mojom",
"//net",
"//services/data_decoder/public/cpp",
"//services/network/public/cpp:cpp_base",
"//services/network/public/mojom",
Expand Down Expand Up @@ -438,6 +444,8 @@ source_set("unit_tests") {
"extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc",
"extensions/login_screen/login/external_logout_request/external_logout_request_event_handler_unittest.cc",
"extensions/login_screen/login_state/login_state_api_unittest.cc",
"kcer_nss/cert_cache_nss_unittest.cc",
"kcer_nss/kcer_nss_unittest.cc",
"platform_keys/chaps_util_impl_unittest.cc",
"policy/dlp/data_transfer_dlp_controller_unittest.cc",
"policy/dlp/dlp_clipboard_notifier_unittest.cc",
Expand Down Expand Up @@ -479,6 +487,7 @@ source_set("unit_tests") {
"//chrome/common:non_code_constants",
"//chrome/common/extensions/api",
"//chrome/test:test_support",
"//chromeos/components/kcer",
"//chromeos/crosapi/mojom",
"//chromeos/dbus/dlp",
"//components/account_id",
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/chromeos/kcer_nss/DEPS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include_rules = [
"+chromeos/components/kcer"
]
1 change: 1 addition & 0 deletions chrome/browser/chromeos/kcer_nss/DIR_METADATA
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mixins: "//chromeos/components/kcer/COMMON_METADATA"
1 change: 1 addition & 0 deletions chrome/browser/chromeos/kcer_nss/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file://chromeos/components/kcer/OWNERS
12 changes: 12 additions & 0 deletions chrome/browser/chromeos/kcer_nss/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
kcer_nss directory contains `KcerNss` - an implementation of `Kcer`
(see `//components/kcer`) that relies on NSS to store keys and client
certificates.

`KcerNss` is a temporary implementation for the transition period.
The plan is:
* Implement `KcerNss`.
* Refactor existing code that uses NSS to use `KcerNss`.
* Implement Kcer (that has the same interface as `KcerNss`,
but doesn't rely on NSS).
* Switch to using `Kcer` without NSS.
* Remove NSS and `KcerNss`.
76 changes: 76 additions & 0 deletions chrome/browser/chromeos/kcer_nss/cert_cache_nss.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 "chrome/browser/chromeos/kcer_nss/cert_cache_nss.h"

#include "base/containers/span.h"
#include "base/ranges/algorithm.h"
#include "chromeos/components/kcer/kcer.h"
#include "net/cert/scoped_nss_types.h"
#include "third_party/boringssl/src/include/openssl/pool.h"

namespace kcer::internal {

namespace {

// Extracts a pointer to bytes and length of the certificate itself and compares
// based on them. `cert` from the constructors must remain valid throughout the
// lifetime of CmpAdapter.
class CmpAdapter {
public:
explicit CmpAdapter(const scoped_refptr<const Cert>& cert)
: data_(CRYPTO_BUFFER_data(cert->GetX509Cert()->cert_buffer()),
CRYPTO_BUFFER_len(cert->GetX509Cert()->cert_buffer())) {}
explicit CmpAdapter(const net::ScopedCERTCertificate& cert)
: data_(cert->derCert.data, cert->derCert.len) {}

bool operator<(const CmpAdapter& other) {
return base::ranges::lexicographical_compare(data_, other.data_);
}

private:
base::span<const uint8_t> data_;
};

} // namespace

//====================== CertCacheNss ==========================================

CertCacheNss::CertCacheNss() = default;
CertCacheNss::CertCacheNss(CertCacheNss&&) = default;
CertCacheNss& CertCacheNss::operator=(CertCacheNss&&) = default;
CertCacheNss::~CertCacheNss() = default;

CertCacheNss::CertCacheNss(base::span<scoped_refptr<const Cert>> certs)
: certs_(certs.begin(), certs.end()) {}

scoped_refptr<const Cert> CertCacheNss::FindCert(
const net::ScopedCERTCertificate& cert) const {
auto iter = certs_.find(cert);
return (iter != certs_.end()) ? *iter : nullptr;
}

std::vector<scoped_refptr<const Cert>> CertCacheNss::GetAllCerts() const {
return std::vector<scoped_refptr<const Cert>>(certs_.begin(), certs_.end());
}

//====================== CertCacheNss::CertComparator ==========================

bool CertCacheNss::CertComparator::operator()(
const scoped_refptr<const Cert>& a,
const net::ScopedCERTCertificate& b) const {
return CmpAdapter(a) < CmpAdapter(b);
}
bool CertCacheNss::CertComparator::operator()(
const net::ScopedCERTCertificate& a,
const scoped_refptr<const Cert>& b) const {
return CmpAdapter(a) < CmpAdapter(b);
}
bool CertCacheNss::CertComparator::operator()(
const scoped_refptr<const Cert>& a,
const scoped_refptr<const Cert>& b) const {
return CmpAdapter(a) < CmpAdapter(b);
}

} // namespace kcer::internal
59 changes: 59 additions & 0 deletions chrome/browser/chromeos/kcer_nss/cert_cache_nss.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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.

#ifndef CHROME_BROWSER_CHROMEOS_KCER_NSS_CERT_CACHE_NSS_H_
#define CHROME_BROWSER_CHROMEOS_KCER_NSS_CERT_CACHE_NSS_H_

#include <set>

#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "net/cert/scoped_nss_types.h"

namespace kcer {

class Cert;

namespace internal {

// Cache for a collection of scoped_refptr<const Cert>-s.
class CertCacheNss {
public:
// Empty cache.
CertCacheNss();
// Cache that contains `certs` (`certs` can be unsorted).
explicit CertCacheNss(base::span<scoped_refptr<const Cert>> certs);
CertCacheNss(CertCacheNss&&);
CertCacheNss& operator=(CertCacheNss&&);
~CertCacheNss();

// Searches for Cert certificate with the same content as `cert` and returns a
// scoped_refptr<const Cert> on success, a nullptr if `cert` was not found.
scoped_refptr<const Cert> FindCert(
const net::ScopedCERTCertificate& cert) const;
// Returns ref-counting pointers to all certificate from the cache.
std::vector<scoped_refptr<const Cert>> GetAllCerts() const;

private:
// Comparator for sorting scoped_refptr<const Cert>-s and for enabling
// std::set::find() using the net::ScopedCERTCertificate representation of a
// cert.
struct CertComparator {
using is_transparent = void;

bool operator()(const scoped_refptr<const Cert>& a,
const net::ScopedCERTCertificate& b) const;
bool operator()(const net::ScopedCERTCertificate& a,
const scoped_refptr<const Cert>& b) const;
bool operator()(const scoped_refptr<const Cert>& a,
const scoped_refptr<const Cert>& b) const;
};

std::set<scoped_refptr<const Cert>, CertComparator> certs_;
};

} // namespace internal
} // namespace kcer

#endif // CHROME_BROWSER_CHROMEOS_KCER_NSS_CERT_CACHE_NSS_H_
121 changes: 121 additions & 0 deletions chrome/browser/chromeos/kcer_nss/cert_cache_nss_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// 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 "chrome/browser/chromeos/kcer_nss/cert_cache_nss.h"

#include <memory>
#include <string>

#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "chromeos/components/kcer/kcer.h"
#include "net/cert/x509_util_nss.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"

namespace kcer::internal {
namespace {

std::unique_ptr<net::CertBuilder> MakeCertBuilder() {
return net::CertBuilder::FromFile(
net::GetTestCertsDirectory().AppendASCII("ok_cert.pem"), nullptr);
}

scoped_refptr<const Cert> MakeKcerCert(
scoped_refptr<net::X509Certificate> cert) {
// CertCacheNss only cares about the `cert`, other fields are can be anything.
return base::MakeRefCounted<Cert>(Token::kUser, Pkcs11Id(),
/*nickname=*/std::string(),
std::move(cert));
}

// Test that an empty cache doesn't find an unrelated certificate and doesn't
// return any certs.
TEST(KcerCertCacheNssTest, EmptyCacheThenCertNotFound) {
std::unique_ptr<net::CertBuilder> builder = MakeCertBuilder();
net::ScopedCERTCertificate nss_cert =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder->GetX509Certificate().get());

CertCacheNss empty_cache;

EXPECT_EQ(empty_cache.FindCert(nss_cert), nullptr);
EXPECT_EQ(empty_cache.GetAllCerts().size(), 0u);
}

// Test that a cache with one cert can find and return that cert, but not an
// unrelated one.
TEST(KcerCertCacheNssTest, OneCert) {
std::unique_ptr<net::CertBuilder> builder_0 = MakeCertBuilder();
std::unique_ptr<net::CertBuilder> builder_1 = MakeCertBuilder();

net::ScopedCERTCertificate nss_cert_0 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_0->GetX509Certificate().get());
scoped_refptr<const Cert> kcer_cert_0 =
MakeKcerCert(builder_0->GetX509Certificate());

net::ScopedCERTCertificate nss_cert_1 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_1->GetX509Certificate().get());

std::vector<scoped_refptr<const Cert>> certs({kcer_cert_0});
CertCacheNss cache(certs);

EXPECT_EQ(cache.FindCert(nss_cert_0), kcer_cert_0);
EXPECT_EQ(cache.FindCert(nss_cert_1), nullptr);
EXPECT_THAT(cache.GetAllCerts(), testing::ElementsAre(kcer_cert_0));
}

// Test that CertCacheNss can hold, find and return multiple certs.
TEST(KcerCertCacheNssTest, MultipleCerts) {
std::unique_ptr<net::CertBuilder> builder_0 = MakeCertBuilder();
std::unique_ptr<net::CertBuilder> builder_1 = MakeCertBuilder();
std::unique_ptr<net::CertBuilder> builder_2 = MakeCertBuilder();
std::unique_ptr<net::CertBuilder> builder_3 = MakeCertBuilder();

net::ScopedCERTCertificate nss_cert_0 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_0->GetX509Certificate().get());
scoped_refptr<const Cert> kcer_cert_0 =
MakeKcerCert(builder_0->GetX509Certificate());

net::ScopedCERTCertificate nss_cert_1 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_1->GetX509Certificate().get());
scoped_refptr<const Cert> kcer_cert_1 =
MakeKcerCert(builder_1->GetX509Certificate());

net::ScopedCERTCertificate nss_cert_2 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_2->GetX509Certificate().get());
scoped_refptr<const Cert> kcer_cert_2 =
MakeKcerCert(builder_2->GetX509Certificate());

net::ScopedCERTCertificate nss_cert_3 =
net::x509_util::CreateCERTCertificateFromX509Certificate(
builder_3->GetX509Certificate().get());
scoped_refptr<const Cert> kcer_cert_3 =
MakeKcerCert(builder_3->GetX509Certificate());

// Add a lot of duplicates in different order to exercise the comparator.
std::vector<scoped_refptr<const Cert>> certs(
{kcer_cert_0, kcer_cert_1, kcer_cert_2, kcer_cert_3, kcer_cert_3,
kcer_cert_2, kcer_cert_1, kcer_cert_0, kcer_cert_0, kcer_cert_2,
kcer_cert_3, kcer_cert_1});
CertCacheNss cache(certs);

EXPECT_EQ(cache.FindCert(nss_cert_0), kcer_cert_0);
EXPECT_EQ(cache.FindCert(nss_cert_1), kcer_cert_1);
EXPECT_EQ(cache.FindCert(nss_cert_2), kcer_cert_2);
EXPECT_EQ(cache.FindCert(nss_cert_3), kcer_cert_3);
EXPECT_THAT(cache.GetAllCerts(),
testing::UnorderedElementsAre(kcer_cert_0, kcer_cert_1,
kcer_cert_2, kcer_cert_3));
}

} // namespace
} // namespace kcer::internal
Loading

0 comments on commit 008eeab

Please sign in to comment.