blob: df66d333aaf20178e1a774d5216a9a8dc828d02c [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/trusted_vault/recovery_key_store_certificate.h"
#include <string>
#include <string_view>
#include <vector>
#include "base/base64.h"
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/notimplemented.h"
#include "components/trusted_vault/securebox.h"
#include "crypto/signature_verifier.h"
#include "net/cert/asn1_util.h"
#include "net/cert/time_conversions.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "third_party/boringssl/src/pki/cert_issuer_source_static.h"
#include "third_party/boringssl/src/pki/parse_certificate.h"
#include "third_party/boringssl/src/pki/parsed_certificate.h"
#include "third_party/boringssl/src/pki/path_builder.h"
#include "third_party/boringssl/src/pki/simple_path_builder_delegate.h"
#include "third_party/boringssl/src/pki/trust_store_in_memory.h"
#include "third_party/libxml/chromium/xml_reader.h"
namespace trusted_vault {
namespace {
// This is the root of trust for the backend certificate list verification. This
// is generated by the trusted execution platforms team
// (trusted-execution-platforms@google.com) and hardcoded in the server code
// that uses this cert in the google3 codebase. As a root CA cert, it should
// very rarely be changed.
constexpr uint8_t kRecoverableKeyStoreServiceRootCaCert[] = {
0x30, 0x82, 0x05, 0x0f, 0x30, 0x82, 0x02, 0xf7, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x10, 0x6c, 0xd7, 0x6e, 0x79, 0x4d, 0xa8, 0xd2, 0xf3, 0x3d,
0x80, 0x6a, 0xb8, 0x37, 0xa6, 0xe1, 0x8f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x31,
0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x47,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20,
0x4b, 0x65, 0x79, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31,
0x38, 0x32, 0x34, 0x30, 0x32, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x35,
0x30, 0x38, 0x31, 0x39, 0x32, 0x34, 0x30, 0x32, 0x5a, 0x30, 0x31, 0x31,
0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x47, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x4b,
0x65, 0x79, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xad, 0x48, 0x33,
0xbb, 0xee, 0x28, 0xf7, 0x29, 0x76, 0xd9, 0xea, 0xa5, 0xd4, 0x18, 0x86,
0x06, 0xad, 0xe0, 0x59, 0x7a, 0x28, 0x87, 0x6a, 0xa5, 0xdc, 0x9f, 0xaf,
0x56, 0xec, 0xdf, 0xfd, 0x38, 0x63, 0xcd, 0xd2, 0x20, 0xd3, 0x19, 0x24,
0x93, 0x0f, 0xcd, 0x00, 0x5c, 0x58, 0x16, 0x2e, 0x3d, 0x12, 0x8d, 0x5f,
0x6b, 0xf8, 0x5f, 0xf3, 0x00, 0x88, 0xa0, 0x0a, 0x82, 0x12, 0xcd, 0x65,
0x0f, 0xab, 0x44, 0xdd, 0xc0, 0x83, 0xdd, 0x3d, 0xfe, 0x11, 0x03, 0xea,
0xba, 0x1e, 0x82, 0x07, 0x62, 0xa6, 0x64, 0x32, 0x7a, 0x98, 0xf9, 0xd7,
0xbd, 0x55, 0x25, 0x52, 0xe1, 0x6b, 0xd0, 0xed, 0x8c, 0xc1, 0x99, 0x30,
0xca, 0x5a, 0x81, 0x11, 0x3c, 0xca, 0xd3, 0x1e, 0x4d, 0x08, 0x78, 0x0b,
0xfe, 0x9e, 0x3b, 0xbe, 0x48, 0xe1, 0x1f, 0x1e, 0x8b, 0xa9, 0x56, 0xa8,
0x6e, 0x28, 0x0a, 0x94, 0x7c, 0x6c, 0xce, 0xf5, 0x62, 0xe1, 0xf9, 0x2d,
0xfe, 0xaa, 0xbb, 0x29, 0x6d, 0xd8, 0x4d, 0x5c, 0x61, 0xc1, 0xd2, 0xc6,
0x11, 0xa6, 0xfe, 0x3a, 0xa4, 0x9f, 0xc0, 0xcc, 0x5d, 0x04, 0xb8, 0x4c,
0x7c, 0x4d, 0x0a, 0xd1, 0xdb, 0xc5, 0xb7, 0xc6, 0xec, 0xf3, 0x22, 0x40,
0x17, 0x4e, 0x03, 0x26, 0xc3, 0x1b, 0x44, 0x28, 0x45, 0x14, 0x1a, 0x53,
0xd7, 0xb6, 0x74, 0xbb, 0x9d, 0xe2, 0x20, 0x00, 0x2e, 0xe6, 0xa5, 0x50,
0x84, 0xaa, 0xd0, 0x5e, 0x22, 0x00, 0xc2, 0x06, 0xe8, 0x66, 0xa7, 0x7e,
0x26, 0x41, 0xcb, 0x5d, 0x4d, 0x5f, 0x25, 0xe5, 0x53, 0xe5, 0x62, 0x4e,
0x26, 0x0a, 0x09, 0x15, 0x61, 0xe4, 0x75, 0x69, 0x07, 0xb5, 0xae, 0x26,
0x49, 0x89, 0x52, 0xef, 0x62, 0x75, 0x43, 0xdd, 0xbb, 0x24, 0x3d, 0x49,
0x74, 0x44, 0xf5, 0x90, 0x5b, 0x47, 0xf8, 0x40, 0xed, 0x60, 0x0b, 0x71,
0xef, 0x1e, 0xc5, 0xf7, 0x10, 0x00, 0x6d, 0xbd, 0xad, 0x30, 0x84, 0xf0,
0xb3, 0xfc, 0x30, 0x77, 0x6a, 0xc0, 0xcd, 0x94, 0xd7, 0xfe, 0x4c, 0x51,
0x6c, 0x39, 0x57, 0x54, 0xb4, 0xe8, 0x53, 0x4e, 0x4b, 0x15, 0x83, 0xeb,
0xf9, 0xd1, 0x55, 0xd7, 0x0b, 0xd7, 0x9a, 0x2d, 0x23, 0x96, 0x42, 0x31,
0x9e, 0x17, 0x5a, 0x54, 0x1a, 0x96, 0x0b, 0xdd, 0xe9, 0xe7, 0x6f, 0x14,
0x89, 0x47, 0x0b, 0xa6, 0x26, 0xfe, 0x1d, 0x5c, 0xcc, 0x58, 0x67, 0x58,
0x23, 0x71, 0xb5, 0x34, 0xe6, 0xbf, 0x95, 0x3a, 0x74, 0x73, 0xc2, 0xdc,
0x6c, 0x98, 0xda, 0xa6, 0x28, 0x95, 0x9d, 0xe4, 0x50, 0x27, 0x77, 0x08,
0xa8, 0x33, 0xce, 0x48, 0x49, 0xc4, 0xab, 0x8d, 0x21, 0xc9, 0x97, 0x75,
0x8f, 0x1d, 0xc9, 0x9c, 0xec, 0x49, 0x33, 0x01, 0xec, 0xf2, 0xfe, 0x2c,
0xf4, 0x62, 0x25, 0x6f, 0x70, 0x5c, 0x3a, 0x60, 0xef, 0x03, 0xf3, 0x2e,
0xd3, 0xdc, 0x44, 0x30, 0xac, 0x29, 0x1c, 0x19, 0xb8, 0x4c, 0x50, 0xca,
0x5d, 0xe1, 0x87, 0x39, 0x68, 0x5a, 0xed, 0xc7, 0x16, 0x10, 0x40, 0x9b,
0xc8, 0xee, 0x67, 0x72, 0xee, 0x97, 0xb8, 0xdd, 0xa2, 0xcb, 0x3f, 0x52,
0xf9, 0x3b, 0x8c, 0xca, 0x36, 0x41, 0x13, 0x9e, 0x76, 0xa7, 0xa3, 0xee,
0xe6, 0x01, 0x02, 0xdb, 0x19, 0x3e, 0xa9, 0xa6, 0xf4, 0x34, 0x60, 0xd3,
0x1d, 0xd2, 0xca, 0x2d, 0xbc, 0x96, 0x9f, 0x72, 0x31, 0x76, 0x60, 0x47,
0xc9, 0x3a, 0xfb, 0x88, 0xf0, 0xaa, 0x9a, 0x9c, 0x87, 0x9e, 0x09, 0x02,
0xfe, 0x96, 0xc6, 0x7e, 0xf1, 0xae, 0xb1, 0xce, 0x41, 0xa4, 0x1b, 0xa0,
0xb0, 0x1b, 0x65, 0xcf, 0xae, 0xe6, 0xe1, 0x15, 0x8b, 0x27, 0xbd, 0xb2,
0x01, 0xe5, 0x4f, 0x3b, 0xf9, 0x72, 0xff, 0xcc, 0x38, 0xa2, 0xb3, 0x6c,
0x19, 0x68, 0xe7, 0xde, 0xcd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x23,
0x30, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x68, 0x96, 0x96, 0xef, 0xb8,
0xab, 0x2c, 0x60, 0x3d, 0xbe, 0x91, 0xb0, 0x0d, 0x26, 0xc1, 0x1e, 0xd5,
0xdb, 0x55, 0x09, 0xf6, 0xb1, 0x29, 0x62, 0x14, 0x8d, 0xf8, 0x04, 0x69,
0x4d, 0x61, 0xa2, 0x23, 0x6d, 0x91, 0x08, 0x94, 0x86, 0x2b, 0x46, 0x6b,
0x3c, 0x67, 0x85, 0x43, 0x31, 0x36, 0x06, 0x99, 0xd5, 0x45, 0xd9, 0x8b,
0x4d, 0x5e, 0x63, 0x7f, 0xd2, 0x54, 0x4a, 0xd8, 0xc7, 0xa0, 0xfb, 0xcd,
0x60, 0x59, 0xf0, 0x2d, 0x32, 0x74, 0x13, 0x67, 0xbd, 0x3a, 0x58, 0xfc,
0x32, 0xb1, 0xeb, 0x02, 0xe7, 0x00, 0x50, 0x52, 0xe1, 0x97, 0x8c, 0x28,
0x5b, 0x3d, 0x24, 0x53, 0xf5, 0xf4, 0x10, 0x1d, 0x63, 0xe9, 0x8d, 0x92,
0x5e, 0xb0, 0x1a, 0x64, 0xf8, 0x8c, 0x32, 0x9a, 0xbc, 0x04, 0xa8, 0xf7,
0x98, 0x7d, 0x4c, 0x6d, 0x5e, 0x2c, 0xb5, 0x9d, 0x24, 0x88, 0x75, 0x29,
0x66, 0xee, 0x8f, 0x12, 0x6a, 0xe8, 0x8d, 0x81, 0xbb, 0x14, 0xa2, 0x8d,
0xdf, 0x49, 0xda, 0xdb, 0x15, 0xdf, 0x88, 0xe7, 0xe5, 0xd8, 0x9b, 0xac,
0x56, 0xcc, 0x87, 0x0e, 0xa9, 0x94, 0x9f, 0xf1, 0xe3, 0x4e, 0x1e, 0xb3,
0x01, 0x22, 0xaa, 0x13, 0xa8, 0x71, 0x13, 0xe1, 0x49, 0x46, 0x5d, 0x95,
0x43, 0x87, 0x5c, 0xaf, 0x84, 0xaf, 0x88, 0x7b, 0x5d, 0x6e, 0x29, 0x8c,
0xcb, 0x62, 0xe0, 0x84, 0xb9, 0xd9, 0xe0, 0x51, 0x38, 0x5e, 0x12, 0x60,
0x46, 0x7c, 0x3d, 0x4d, 0xe4, 0x55, 0xe4, 0x2f, 0x52, 0x9f, 0xba, 0x66,
0x9c, 0x00, 0x39, 0x5f, 0x40, 0xbc, 0x65, 0x78, 0x57, 0xbd, 0xfb, 0xeb,
0x77, 0x1e, 0x1f, 0x2f, 0x75, 0x0a, 0x96, 0x4d, 0x4b, 0xaa, 0x56, 0xf9,
0xb2, 0x4c, 0x68, 0x2b, 0x54, 0x7d, 0xc3, 0x0a, 0xfe, 0x68, 0xd2, 0x0e,
0xf9, 0xcb, 0x91, 0xf0, 0x90, 0x39, 0x0e, 0x61, 0x03, 0x58, 0x5c, 0x39,
0x06, 0xe7, 0x61, 0xfe, 0xe5, 0x96, 0x66, 0x50, 0x53, 0xc9, 0x55, 0xb6,
0xc9, 0xb6, 0xd3, 0x07, 0xab, 0x64, 0x98, 0xe7, 0x3c, 0x73, 0x4f, 0x17,
0x3a, 0x0c, 0x41, 0x68, 0xaa, 0xec, 0x5c, 0x02, 0xe6, 0x25, 0xc9, 0x6e,
0x69, 0xb8, 0xe6, 0x37, 0x58, 0xe9, 0xf3, 0x56, 0x9e, 0xd5, 0xea, 0x5e,
0x37, 0x7d, 0x63, 0x27, 0x83, 0x79, 0x20, 0x4e, 0x5a, 0xab, 0xb5, 0xdb,
0x4c, 0x59, 0x8a, 0x93, 0x0a, 0x60, 0x4d, 0x3b, 0xc9, 0x9a, 0xa0, 0x91,
0xd9, 0x7c, 0xd6, 0xba, 0x39, 0x6f, 0x75, 0xb7, 0x63, 0x7e, 0x20, 0x01,
0x96, 0x25, 0xac, 0x9c, 0xbc, 0xcb, 0x40, 0x23, 0xfc, 0x79, 0x9c, 0x1c,
0x33, 0x82, 0xc0, 0xe5, 0x42, 0xb7, 0x7f, 0x59, 0x2d, 0x1e, 0x89, 0x6b,
0xd4, 0xe2, 0x3d, 0x7a, 0xd4, 0x8e, 0x91, 0xe6, 0xc4, 0xda, 0x9c, 0xbc,
0x2c, 0xf6, 0x01, 0x25, 0x69, 0x57, 0xdf, 0xab, 0x51, 0x01, 0xa8, 0x19,
0x85, 0x4f, 0xcd, 0xa3, 0x23, 0xad, 0xbe, 0x14, 0x73, 0xab, 0xda, 0xdb,
0xde, 0xb6, 0xdf, 0xf7, 0xa0, 0x0f, 0x10, 0x72, 0x94, 0x16, 0xc5, 0xc6,
0xf6, 0x70, 0x44, 0x77, 0x60, 0x54, 0x29, 0x98, 0x21, 0x2c, 0xa3, 0xdf,
0xf8, 0x93, 0x42, 0x53, 0xfd, 0xa9, 0x38, 0x6c, 0x74, 0x8d, 0xd3, 0x1c,
0x7f, 0xe2, 0xbe, 0x6b, 0x50, 0xde, 0x1b, 0x46, 0x33, 0x19, 0x83, 0x11,
0x90, 0x5b, 0xe9, 0x37, 0x78, 0x6f, 0x1d, 0x8a, 0xc9, 0x74, 0x1c, 0x8b,
0xde, 0x44, 0x8f, 0xdd, 0x4b, 0x43, 0xd0, 0x7d, 0xa0, 0xf0, 0xfb, 0x3b,
0x81, 0x12, 0xc4, 0x2c, 0x6d, 0x87, 0x38, 0x3c, 0x5b, 0x4a, 0x7f, 0x0f,
0x57, 0x75, 0xed, 0xb9, 0x2b, 0xf1, 0x55, 0xa9, 0xc0, 0xea, 0xec, 0x9a,
0x69, 0x2d, 0xc0, 0x67, 0xa4, 0xb1, 0x79, 0x52, 0xef, 0x9b, 0x0e, 0x74,
0xed, 0xa6, 0x08};
// Returns a list of certificates from a certificate tag, traversing its entire
// contents. Ignores anything that is not a <cert> tag directly below the
// certificate tag.
// |xml_reader| must be pointing at the opening element that wraps certificates.
// After processing, |xml_reader| will be pointing at the matching closing tag.
// <intermediates> <-- Point xml_reader here to start.
// <cert>cert1</cert>
// <not-cert>not a certificate</not-cert> <-- ignored
// <cert>cert2</cert>
// </intermediates> <-- xml_reader will point here after processing.
// In this case, the return value would be { cert1, cert2 }.
std::vector<std::string> ParseCertList(XmlReader* xml_reader) {
std::vector<std::string> cert_list;
const int starting_depth = xml_reader->Depth();
while (xml_reader->Read() && xml_reader->Depth() > starting_depth) {
// Look for a <cert> element nested just under the starting tag.
if (!xml_reader->IsElement() || xml_reader->Depth() != starting_depth + 1 ||
xml_reader->NodeName() != "cert") {
continue;
}
std::string cert;
if (!xml_reader->ReadElementContent(&cert) || cert.empty()) {
continue;
}
cert_list.push_back(std::move(cert));
}
return cert_list;
}
// Given a base64-encoded x509 certificate, returns the bssl representation (or
// nullptr if decoding failed).
std::shared_ptr<const bssl::ParsedCertificate> CertificateFromBase64(
std::string_view b64) {
std::optional<std::vector<uint8_t>> certificate_data =
base::Base64Decode(b64);
if (!certificate_data) {
return nullptr;
}
return bssl::ParsedCertificate::Create(
net::x509_util::CreateCryptoBuffer(*certificate_data),
net::x509_util::DefaultParseCertificateOptions(), /*errors=*/nullptr);
}
// Returns a fresh trusted cert store for the cloud root.
std::unique_ptr<bssl::TrustStoreInMemory> ConstructTrustedCertStore() {
// This code will run at most once a day. So there's no point keeping the
// trust store around after use.
auto trust_store = std::make_unique<bssl::TrustStoreInMemory>();
std::shared_ptr<const bssl::ParsedCertificate> root_cert =
bssl::ParsedCertificate::Create(
net::x509_util::CreateCryptoBuffer(
kRecoverableKeyStoreServiceRootCaCert),
net::x509_util::DefaultParseCertificateOptions(), /*errors=*/nullptr);
CHECK(root_cert);
trust_store->AddTrustAnchor(root_cert);
return trust_store;
}
} // namespace
namespace internal {
ParsedRecoveryKeyStoreCertXML::ParsedRecoveryKeyStoreCertXML(
std::vector<std::string> intermediates,
std::vector<std::string> endpoints)
: intermediates(std::move(intermediates)),
endpoints(std::move(endpoints)) {}
ParsedRecoveryKeyStoreCertXML::ParsedRecoveryKeyStoreCertXML(
ParsedRecoveryKeyStoreCertXML&&) = default;
ParsedRecoveryKeyStoreCertXML& ParsedRecoveryKeyStoreCertXML::operator=(
ParsedRecoveryKeyStoreCertXML&&) = default;
ParsedRecoveryKeyStoreCertXML::~ParsedRecoveryKeyStoreCertXML() = default;
ParsedRecoveryKeyStoreSigXML::ParsedRecoveryKeyStoreSigXML(
std::vector<std::string> intermediates,
std::string certificate,
std::string signature)
: intermediates(std::move(intermediates)),
certificate(std::move(certificate)),
signature(std::move(signature)) {}
ParsedRecoveryKeyStoreSigXML::ParsedRecoveryKeyStoreSigXML(
ParsedRecoveryKeyStoreSigXML&&) = default;
ParsedRecoveryKeyStoreSigXML& ParsedRecoveryKeyStoreSigXML::operator=(
ParsedRecoveryKeyStoreSigXML&&) = default;
ParsedRecoveryKeyStoreSigXML::~ParsedRecoveryKeyStoreSigXML() = default;
std::optional<ParsedRecoveryKeyStoreCertXML> ParseRecoveryKeyStoreCertXML(
std::string_view cert_xml) {
XmlReader xml_reader;
xml_reader.Load(cert_xml);
if (!xml_reader.Read() || xml_reader.NodeName() != "certificate") {
return std::nullopt;
}
std::vector<std::string> intermediates;
std::vector<std::string> endpoints;
while (xml_reader.Read()) {
// Skip anything that is not an element nested under the root.
if (!xml_reader.IsElement() || xml_reader.Depth() != 1) {
continue;
}
// We expect a single <intermediates> and <endpoints> tags. This code only
// has the last tags take effect, but that should be okay.
if (xml_reader.NodeName() == "intermediates") {
intermediates = ParseCertList(&xml_reader);
} else if (xml_reader.NodeName() == "endpoints") {
endpoints = ParseCertList(&xml_reader);
}
}
if (intermediates.empty() || endpoints.empty()) {
return std::nullopt;
}
return ParsedRecoveryKeyStoreCertXML(std::move(intermediates),
std::move(endpoints));
}
std::optional<ParsedRecoveryKeyStoreSigXML> ParseRecoveryKeyStoreSigXML(
std::string_view sig_xml) {
XmlReader xml_reader;
xml_reader.Load(sig_xml);
if (!xml_reader.Read() || xml_reader.NodeName() != "signature") {
return std::nullopt;
}
std::vector<std::string> intermediates;
std::string certificate;
std::string signature;
while (xml_reader.Read()) {
// Skip anything that is not an element nested under the root.
if (!xml_reader.IsElement() || xml_reader.Depth() != 1) {
continue;
}
// We expect a single <intermediates>, <certificate>, and <value> tags. This
// code only has the last tags take effect, but that should be okay.
if (xml_reader.NodeName() == "intermediates") {
intermediates = ParseCertList(&xml_reader);
} else if (xml_reader.NodeName() == "certificate") {
if (!xml_reader.ReadElementContent(&certificate)) {
return std::nullopt;
}
} else if (xml_reader.NodeName() == "value") {
if (!xml_reader.ReadElementContent(&signature)) {
return std::nullopt;
}
}
}
if (intermediates.empty() || certificate.empty() || signature.empty()) {
return std::nullopt;
}
return ParsedRecoveryKeyStoreSigXML(
std::move(intermediates), std::move(certificate), std::move(signature));
}
std::shared_ptr<const bssl::ParsedCertificate> VerifySignatureChain(
std::string_view certificate_b64,
base::span<std::string> intermediates_b64,
base::Time current_time) {
std::shared_ptr<const bssl::ParsedCertificate> certificate =
CertificateFromBase64(certificate_b64);
if (!certificate) {
return nullptr;
}
bssl::CertIssuerSourceStatic intermediates;
for (const auto& intermediate_b64 : intermediates_b64) {
std::shared_ptr<const bssl::ParsedCertificate> intermediate =
CertificateFromBase64(intermediate_b64);
if (!intermediate) {
// Ignore invalid base64. We may still be able to build a valid path.
continue;
}
intermediates.AddCert(std::move(intermediate));
}
std::unique_ptr<bssl::TrustStoreInMemory> trusted_cert_store =
ConstructTrustedCertStore();
bssl::SimplePathBuilderDelegate builder_delegate(
/*min_rsa_modulus_length_bits=*/2048,
bssl::SimplePathBuilderDelegate::DigestPolicy::kStrong);
bssl::der::GeneralizedTime verification_time;
CHECK(net::EncodeTimeAsGeneralizedTime(current_time, &verification_time));
bssl::CertPathBuilder path_builder(certificate, trusted_cert_store.get(),
&builder_delegate, verification_time,
bssl::KeyPurpose::ANY_EKU,
bssl::InitialExplicitPolicy::kFalse,
{bssl::der::Input(bssl::kAnyPolicyOid)},
bssl::InitialPolicyMappingInhibit::kFalse,
bssl::InitialAnyPolicyInhibit::kFalse);
path_builder.AddCertIssuerSource(&intermediates);
bssl::CertPathBuilder::Result result = path_builder.Run();
if (!result.HasValidPath()) {
return nullptr;
}
return certificate;
}
bool VerifySignature(std::shared_ptr<const bssl::ParsedCertificate> certificate,
std::string_view cert_xml,
std::string_view signature_b64) {
std::optional<std::vector<uint8_t>> signature =
base::Base64Decode(signature_b64);
if (!signature) {
return false;
}
size_t size_bits;
net::X509Certificate::PublicKeyType type;
net::X509Certificate::GetPublicKeyInfo(certificate->cert_buffer(), &size_bits,
&type);
crypto::SignatureVerifier::SignatureAlgorithm algo;
switch (type) {
case net::X509Certificate::PublicKeyType::kPublicKeyTypeECDSA:
algo = crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
break;
case net::X509Certificate::PublicKeyType::kPublicKeyTypeRSA:
algo = crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256;
break;
case net::X509Certificate::PublicKeyType::kPublicKeyTypeUnknown:
return false;
}
crypto::SignatureVerifier signature_verifier;
if (!net::x509_util::SignatureVerifierInitWithCertificate(
&signature_verifier, algo, *signature, certificate->cert_buffer())) {
return false;
}
signature_verifier.VerifyUpdate(base::as_byte_span(cert_xml));
return signature_verifier.VerifyFinal();
}
std::vector<std::unique_ptr<SecureBoxPublicKey>> ExtractEndpointPublicKeys(
ParsedRecoveryKeyStoreCertXML cert_xml,
base::Time current_time) {
std::vector<std::unique_ptr<SecureBoxPublicKey>> result;
for (const auto& endpoint : cert_xml.endpoints) {
std::shared_ptr<const bssl::ParsedCertificate> certificate =
VerifySignatureChain(endpoint, cert_xml.intermediates, current_time);
if (!certificate) {
continue;
}
// Only ECDSA public keys are supported.
size_t size_bits;
net::X509Certificate::PublicKeyType type;
net::X509Certificate::GetPublicKeyInfo(certificate->cert_buffer(),
&size_bits, &type);
if (type != net::X509Certificate::kPublicKeyTypeECDSA) {
continue;
}
// Extract the public key from the SPKI.
std::string_view ec_point_oct;
if (!net::asn1::ExtractSubjectPublicKeyFromSPKI(
certificate->tbs().spki_tlv.AsStringView(), &ec_point_oct)) {
continue;
}
// ExtractSubjectPublicKeyFromSPKI does not remove the initial octet
// encoding the number of unused bits in the ASN.1 BIT STRING so we do it
// here. The public key is always byte-aligned.
ec_point_oct.remove_prefix(1);
std::unique_ptr<SecureBoxPublicKey> key =
SecureBoxPublicKey::CreateByImport(base::as_byte_span(ec_point_oct));
if (!key) {
continue;
}
result.push_back(std::move(key));
}
return result;
}
} // namespace internal
// static
std::optional<RecoveryKeyStoreCertificate> RecoveryKeyStoreCertificate::Parse(
std::string_view cert_xml,
std::string_view sig_xml,
base::Time current_time) {
std::optional<internal::ParsedRecoveryKeyStoreSigXML> parsed_sig_xml =
internal::ParseRecoveryKeyStoreSigXML(sig_xml);
if (!parsed_sig_xml) {
return std::nullopt;
}
std::shared_ptr<const bssl::ParsedCertificate> signing_certificate =
internal::VerifySignatureChain(std::move(parsed_sig_xml->certificate),
parsed_sig_xml->intermediates,
current_time);
if (!signing_certificate || !signing_certificate->has_key_usage() ||
!signing_certificate->key_usage().AssertsBit(
bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE)) {
return std::nullopt;
}
if (!internal::VerifySignature(std::move(signing_certificate), cert_xml,
parsed_sig_xml->signature)) {
return std::nullopt;
}
std::optional<internal::ParsedRecoveryKeyStoreCertXML> parsed_cert_xml =
internal::ParseRecoveryKeyStoreCertXML(cert_xml);
if (!parsed_cert_xml) {
return std::nullopt;
}
std::vector<std::unique_ptr<SecureBoxPublicKey>> endpoint_public_keys =
internal::ExtractEndpointPublicKeys(std::move(*parsed_cert_xml),
current_time);
if (endpoint_public_keys.empty()) {
return std::nullopt;
}
return RecoveryKeyStoreCertificate(std::move(endpoint_public_keys));
}
RecoveryKeyStoreCertificate::RecoveryKeyStoreCertificate(
RecoveryKeyStoreCertificate&& other) = default;
RecoveryKeyStoreCertificate& RecoveryKeyStoreCertificate::operator=(
RecoveryKeyStoreCertificate&& other) = default;
RecoveryKeyStoreCertificate::~RecoveryKeyStoreCertificate() = default;
RecoveryKeyStoreCertificate::RecoveryKeyStoreCertificate(
std::vector<std::unique_ptr<SecureBoxPublicKey>> endpoint_public_keys)
: endpoint_public_keys_(std::move(endpoint_public_keys)) {}
} // namespace trusted_vault