blob: 4aa0a4733a2638c3939d1a40a495c37deb9f91ad [file] [log] [blame]
// Copyright 2016 The Chromium Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "trust_store.h"
#include <cassert>
#include <cstring>
#include <optional>
#include <openssl/base.h>
#include <openssl/bytestring.h>
#include <openssl/mem.h>
#include <openssl/pool.h>
#include "merkle_tree.h"
#include "parse_certificate.h"
#include "parsed_certificate.h"
#include "string_util.h"
BSSL_NAMESPACE_BEGIN
namespace {
constexpr char kUnspecifiedStr[] = "UNSPECIFIED";
constexpr char kDistrustedStr[] = "DISTRUSTED";
constexpr char kTrustedAnchorStr[] = "TRUSTED_ANCHOR";
constexpr char kTrustedAnchorOrLeafStr[] = "TRUSTED_ANCHOR_OR_LEAF";
constexpr char kTrustedLeafStr[] = "TRUSTED_LEAF";
constexpr char kEnforceAnchorExpiry[] = "enforce_anchor_expiry";
constexpr char kEnforceAnchorConstraints[] = "enforce_anchor_constraints";
constexpr char kRequireAnchorBasicConstraints[] =
"require_anchor_basic_constraints";
constexpr char kRequireLeafSelfsigned[] = "require_leaf_selfsigned";
} // namespace
bool CertificateTrust::IsTrustAnchor() const {
switch (type) {
case CertificateTrustType::DISTRUSTED:
case CertificateTrustType::UNSPECIFIED:
case CertificateTrustType::TRUSTED_LEAF:
return false;
case CertificateTrustType::TRUSTED_ANCHOR:
case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
return true;
}
assert(0); // NOTREACHED
return false;
}
bool CertificateTrust::IsTrustLeaf() const {
switch (type) {
case CertificateTrustType::TRUSTED_LEAF:
case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
return true;
case CertificateTrustType::DISTRUSTED:
case CertificateTrustType::UNSPECIFIED:
case CertificateTrustType::TRUSTED_ANCHOR:
return false;
}
assert(0); // NOTREACHED
return false;
}
bool CertificateTrust::IsDistrusted() const {
switch (type) {
case CertificateTrustType::DISTRUSTED:
return true;
case CertificateTrustType::UNSPECIFIED:
case CertificateTrustType::TRUSTED_ANCHOR:
case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
case CertificateTrustType::TRUSTED_LEAF:
return false;
}
assert(0); // NOTREACHED
return false;
}
bool CertificateTrust::HasUnspecifiedTrust() const {
switch (type) {
case CertificateTrustType::UNSPECIFIED:
return true;
case CertificateTrustType::DISTRUSTED:
case CertificateTrustType::TRUSTED_ANCHOR:
case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
case CertificateTrustType::TRUSTED_LEAF:
return false;
}
assert(0); // NOTREACHED
return true;
}
std::string CertificateTrust::ToDebugString() const {
std::string result;
switch (type) {
case CertificateTrustType::UNSPECIFIED:
result = kUnspecifiedStr;
break;
case CertificateTrustType::DISTRUSTED:
result = kDistrustedStr;
break;
case CertificateTrustType::TRUSTED_ANCHOR:
result = kTrustedAnchorStr;
break;
case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF:
result = kTrustedAnchorOrLeafStr;
break;
case CertificateTrustType::TRUSTED_LEAF:
result = kTrustedLeafStr;
break;
}
if (enforce_anchor_expiry) {
result += '+';
result += kEnforceAnchorExpiry;
}
if (enforce_anchor_constraints) {
result += '+';
result += kEnforceAnchorConstraints;
}
if (require_anchor_basic_constraints) {
result += '+';
result += kRequireAnchorBasicConstraints;
}
if (require_leaf_selfsigned) {
result += '+';
result += kRequireLeafSelfsigned;
}
return result;
}
// static
std::optional<CertificateTrust> CertificateTrust::FromDebugString(
const std::string &trust_string) {
std::vector<std::string_view> split =
string_util::SplitString(trust_string, '+');
if (split.empty()) {
return std::nullopt;
}
CertificateTrust trust;
if (string_util::IsEqualNoCase(split[0], kUnspecifiedStr)) {
trust = CertificateTrust::ForUnspecified();
} else if (string_util::IsEqualNoCase(split[0], kDistrustedStr)) {
trust = CertificateTrust::ForDistrusted();
} else if (string_util::IsEqualNoCase(split[0], kTrustedAnchorStr)) {
trust = CertificateTrust::ForTrustAnchor();
} else if (string_util::IsEqualNoCase(split[0], kTrustedAnchorOrLeafStr)) {
trust = CertificateTrust::ForTrustAnchorOrLeaf();
} else if (string_util::IsEqualNoCase(split[0], kTrustedLeafStr)) {
trust = CertificateTrust::ForTrustedLeaf();
} else {
return std::nullopt;
}
for (auto i = ++split.begin(); i != split.end(); ++i) {
if (string_util::IsEqualNoCase(*i, kEnforceAnchorExpiry)) {
trust = trust.WithEnforceAnchorExpiry();
} else if (string_util::IsEqualNoCase(*i, kEnforceAnchorConstraints)) {
trust = trust.WithEnforceAnchorConstraints();
} else if (string_util::IsEqualNoCase(*i, kRequireAnchorBasicConstraints)) {
trust = trust.WithRequireAnchorBasicConstraints();
} else if (string_util::IsEqualNoCase(*i, kRequireLeafSelfsigned)) {
trust = trust.WithRequireLeafSelfSigned();
} else {
return std::nullopt;
}
}
return trust;
}
MTCAnchor::MTCAnchor(bssl::Span<const uint8_t> log_id,
Span<const TrustedSubtree> trusted_subtrees)
: trusted_subtrees_(trusted_subtrees.begin(), trusted_subtrees.end()) {
CreateSyntheticCert(log_id);
}
bool MTCAnchor::IsValid() const {
if (!synthetic_cert_) {
return false;
}
Subtree min_subtree;
for (const auto &subtree : trusted_subtrees_) {
if (!subtree.range.IsValid() || subtree.range < min_subtree) {
return false;
}
min_subtree = subtree.range;
}
return true;
}
der::Input MTCAnchor::NormalizedSubject() const {
return synthetic_cert_->normalized_subject();
}
CertificateTrust MTCAnchor::CertTrust() const {
CertificateTrust trust;
trust.type = CertificateTrustType::TRUSTED_ANCHOR;
return trust;
}
std::shared_ptr<const ParsedCertificate> MTCAnchor::AsCert() const {
return synthetic_cert_;
}
std::optional<TreeHashConstSpan> MTCAnchor::SubtreeHash(
Subtree target_range) const {
auto it = std::lower_bound(
trusted_subtrees_.begin(), trusted_subtrees_.end(), target_range,
[](const TrustedSubtree &subtree, Subtree range) -> bool {
return subtree.range < range;
});
if (it == trusted_subtrees_.end() || it->range != target_range) {
return std::nullopt;
}
return it->hash;
}
void MTCAnchor::CreateSyntheticCert(bssl::Span<const uint8_t> log_id) {
bssl::ScopedCBB cbb;
CBB cert, tbs_cert, version, validity, subject_seq, subject_set, subject_log,
signature;
BSSL_CHECK(CBB_init(cbb.get(), 0));
BSSL_CHECK(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE));
// Fill the TBSCertificate
BSSL_CHECK(CBB_add_asn1(&cert, &tbs_cert, CBS_ASN1_SEQUENCE));
// version
BSSL_CHECK(
CBB_add_asn1(&tbs_cert, &version,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
BSSL_CHECK(CBB_add_asn1_uint64(&version, 2)); // 2 is the enum value for v3
// serialNumber
BSSL_CHECK(CBB_add_asn1_uint64(&tbs_cert, 0));
// signature
BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));
// issuer
BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));
// validity
BSSL_CHECK(CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE));
static const uint8_t notBefore[] = "00000101000000Z";
static const uint8_t notAfter[] = "99991231235960Z";
BSSL_CHECK(CBB_add_asn1_element(&validity, CBS_ASN1_GENERALIZEDTIME,
notBefore, sizeof(notBefore) - 1));
BSSL_CHECK(CBB_add_asn1_element(&validity, CBS_ASN1_GENERALIZEDTIME, notAfter,
sizeof(notAfter) - 1));
// subject
BSSL_CHECK(CBB_add_asn1(&tbs_cert, &subject_seq, CBS_ASN1_SEQUENCE));
BSSL_CHECK(CBB_add_asn1(&subject_seq, &subject_set, CBS_ASN1_SET));
BSSL_CHECK(CBB_add_asn1(&subject_set, &subject_log, CBS_ASN1_SEQUENCE));
// Section 5.2: Use OID 1.3.6.1.4.1.44363.47.1 as the attribute type for the
// log ID's name. Note that this is the early experimentation OID in the
// draft rather than the real value of `id-rdna-trustAnchorID`.
static uint8_t log_attr_oid[] = {0x2b, 0x06, 0x01, 0x04, 0x01,
0x82, 0xda, 0x4b, 0x2f, 0x01};
BSSL_CHECK(CBB_add_asn1_element(&subject_log, CBS_ASN1_OBJECT, log_attr_oid,
sizeof(log_attr_oid)));
// Section 5.2's note for initial experimentation also says to use UTF8String
// to represent the attribute's value rather than RELATIVE-OID.
// Convert the relative OID |log_id| to a string. This can fail.
CBS log_id_oid(log_id);
bssl::UniquePtr<char> log_id_text(CBS_asn1_relative_oid_to_text(&log_id_oid));
if (!log_id_text) {
return;
}
BSSL_CHECK(
CBB_add_asn1_element(&subject_log, CBS_ASN1_UTF8STRING,
reinterpret_cast<const uint8_t *>(log_id_text.get()),
strlen(log_id_text.get())));
// subjectPublicKeyInfo
BSSL_CHECK(CBB_add_asn1_element(&tbs_cert, CBS_ASN1_SEQUENCE, nullptr, 0));
// finish the outer Certificate with the signatureAlgorithm and signature.
BSSL_CHECK(CBB_add_asn1_element(&cert, CBS_ASN1_SEQUENCE, nullptr, 0));
BSSL_CHECK(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING));
BSSL_CHECK(CBB_add_u8(&signature, 0));
BSSL_CHECK(CBB_flush(cbb.get()));
synthetic_cert_ = ParsedCertificate::Create(
bssl::UniquePtr<CRYPTO_BUFFER>(
CRYPTO_BUFFER_new(CBB_data(cbb.get()), CBB_len(cbb.get()), nullptr)),
ParseCertificateOptions{}, nullptr);
}
TrustAnchor::TrustAnchor(CertificateTrust trust)
: TrustAnchor(trust, nullptr) {}
TrustAnchor::TrustAnchor(CertificateTrust trust,
std::shared_ptr<const bssl::MTCAnchor> mtc_anchor)
: trust_(trust), mtc_anchor_(mtc_anchor) {}
void TrustAnchor::OverrideCertTrust(CertificateTrust new_trust) {
trust_ = new_trust;
}
CertificateTrust TrustAnchor::CertTrust() const { return trust_; }
std::shared_ptr<const MTCAnchor> TrustAnchor::MTCAnchor() const {
return mtc_anchor_;
}
TrustStore::TrustStore() = default;
std::shared_ptr<const MTCAnchor> TrustStore::GetTrustedMTCIssuerOf(
const ParsedCertificate *cert) {
return nullptr;
}
void TrustStore::AsyncGetIssuersOf(const ParsedCertificate *cert,
std::unique_ptr<Request> *out_req) {
out_req->reset();
}
BSSL_NAMESPACE_END