| // 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 |