// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "verify_certificate_chain.h" #include #include #include #include "cert_error_params.h" #include "cert_errors.h" #include "common_cert_errors.h" #include "extended_key_usage.h" #include "input.h" #include "name_constraints.h" #include "parse_certificate.h" #include "signature_algorithm.h" #include "trust_store.h" #include "verify_signed_data.h" namespace bssl { namespace { bool IsHandledCriticalExtension(const ParsedExtension &extension, const ParsedCertificate &cert) { if (extension.oid == der::Input(kBasicConstraintsOid)) { return true; } // Key Usage is NOT processed for end-entity certificates (this is the // responsibility of callers), however it is considered "handled" here in // order to allow being marked as critical. if (extension.oid == der::Input(kKeyUsageOid)) { return true; } if (extension.oid == der::Input(kExtKeyUsageOid)) { return true; } if (extension.oid == der::Input(kNameConstraintsOid)) { return true; } if (extension.oid == der::Input(kSubjectAltNameOid)) { return true; } if (extension.oid == der::Input(kCertificatePoliciesOid)) { // Policy qualifiers are skipped during processing, so if the // extension is marked critical need to ensure there weren't any // qualifiers other than User Notice / CPS. // // This follows from RFC 5280 section 4.2.1.4: // // If this extension is critical, the path validation software MUST // be able to interpret this extension (including the optional // qualifier), or MUST reject the certificate. std::vector unused_policies; CertErrors unused_errors; return ParseCertificatePoliciesExtensionOids( extension.value, true /*fail_parsing_unknown_qualifier_oids*/, &unused_policies, &unused_errors); // TODO(eroman): Give a better error message. } if (extension.oid == der::Input(kPolicyMappingsOid)) { return true; } if (extension.oid == der::Input(kPolicyConstraintsOid)) { return true; } if (extension.oid == der::Input(kInhibitAnyPolicyOid)) { return true; } if (extension.oid == der::Input(kMSApplicationPoliciesOid)) { // Per https://crbug.com/1439638 and // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/supported-extensions#msapplicationpolicies // The MSApplicationPolicies extension may be ignored if the // extendedKeyUsage extension is also present. return cert.has_extended_key_usage(); } return false; } // Adds errors to |errors| if the certificate contains unconsumed _critical_ // extensions. void VerifyNoUnconsumedCriticalExtensions(const ParsedCertificate &cert, CertErrors *errors, bool allow_precertificate) { for (const auto &it : cert.extensions()) { const ParsedExtension &extension = it.second; if (allow_precertificate && extension.oid == der::Input(kCtPoisonOid)) { continue; } if (extension.critical && !IsHandledCriticalExtension(extension, cert)) { errors->AddError(cert_errors::kUnconsumedCriticalExtension, CreateCertErrorParams2Der("oid", extension.oid, "value", extension.value)); } } } // Returns true if |cert| was self-issued. The definition of self-issuance // comes from RFC 5280 section 6.1: // // A certificate is self-issued if the same DN appears in the subject // and issuer fields (the two DNs are the same if they match according // to the rules specified in Section 7.1). In general, the issuer and // subject of the certificates that make up a path are different for // each certificate. However, a CA may issue a certificate to itself to // support key rollover or changes in certificate policies. These // self-issued certificates are not counted when evaluating path length // or name constraints. [[nodiscard]] bool IsSelfIssued(const ParsedCertificate &cert) { return cert.normalized_subject() == cert.normalized_issuer(); } // Adds errors to |errors| if |cert| is not valid at time |time|. // // The certificate's validity requirements are described by RFC 5280 section // 4.1.2.5: // // The validity period for a certificate is the period of time from // notBefore through notAfter, inclusive. void VerifyTimeValidity(const ParsedCertificate &cert, const der::GeneralizedTime &time, CertErrors *errors) { if (time < cert.tbs().validity_not_before) { errors->AddError(cert_errors::kValidityFailedNotBefore); } if (cert.tbs().validity_not_after < time) { errors->AddError(cert_errors::kValidityFailedNotAfter); } } // Adds errors to |errors| if |cert| has internally inconsistent signature // algorithms. // // X.509 certificates contain two different signature algorithms: // (1) The signatureAlgorithm field of Certificate // (2) The signature field of TBSCertificate // // According to RFC 5280 section 4.1.1.2 and 4.1.2.3 these two fields must be // equal: // // This field MUST contain the same algorithm identifier as the // signature field in the sequence tbsCertificate (Section 4.1.2.3). // // The spec is not explicit about what "the same algorithm identifier" means. // Our interpretation is that the two DER-encoded fields must be byte-for-byte // identical. // // In practice however there are certificates which use different encodings for // specifying RSA with SHA1 (different OIDs). This is special-cased for // compatibility sake. bool VerifySignatureAlgorithmsMatch(const ParsedCertificate &cert, CertErrors *errors) { der::Input alg1_tlv = cert.signature_algorithm_tlv(); der::Input alg2_tlv = cert.tbs().signature_algorithm_tlv; // Ensure that the two DER-encoded signature algorithms are byte-for-byte // equal. if (alg1_tlv == alg2_tlv) { return true; } // But make a compatibility concession if alternate encodings are used // TODO(eroman): Turn this warning into an error. // TODO(eroman): Add a unit-test that exercises this case. std::optional alg1 = ParseSignatureAlgorithm(alg1_tlv); if (!alg1) { errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm); return false; } std::optional alg2 = ParseSignatureAlgorithm(alg2_tlv); if (!alg2) { errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm); return false; } if (*alg1 == *alg2) { errors->AddWarning( cert_errors::kSignatureAlgorithmsDifferentEncoding, CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv, "TBSCertificate.signature", alg2_tlv)); return true; } errors->AddError( cert_errors::kSignatureAlgorithmMismatch, CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv, "TBSCertificate.signature", alg2_tlv)); return false; } // Verify that |cert| can be used for |required_key_purpose|. void VerifyExtendedKeyUsage(const ParsedCertificate &cert, KeyPurpose required_key_purpose, CertErrors *errors, bool is_target_cert, bool is_target_cert_issuer) { // We treat a required KeyPurpose of ANY_EKU to mean "Do not check EKU" if (required_key_purpose == KeyPurpose::ANY_EKU) { return; } bool has_any_eku = false; bool has_server_auth_eku = false; bool has_client_auth_eku = false; bool has_code_signing_eku = false; bool has_time_stamping_eku = false; bool has_ocsp_signing_eku = false; if (cert.has_extended_key_usage()) { for (const auto &key_purpose_oid : cert.extended_key_usage()) { if (key_purpose_oid == der::Input(kAnyEKU)) { has_any_eku = true; } if (key_purpose_oid == der::Input(kServerAuth)) { has_server_auth_eku = true; } if (key_purpose_oid == der::Input(kClientAuth)) { has_client_auth_eku = true; } if (key_purpose_oid == der::Input(kCodeSigning)) { has_code_signing_eku = true; } if (key_purpose_oid == der::Input(kTimeStamping)) { has_time_stamping_eku = true; } if (key_purpose_oid == der::Input(kOCSPSigning)) { has_ocsp_signing_eku = true; } } } // Apply strict only to leaf certificates in these cases. if (required_key_purpose == KeyPurpose::CLIENT_AUTH_STRICT_LEAF) { if (!is_target_cert) { required_key_purpose = KeyPurpose::CLIENT_AUTH; } else { required_key_purpose = KeyPurpose::CLIENT_AUTH_STRICT; } } if (required_key_purpose == KeyPurpose::SERVER_AUTH_STRICT_LEAF) { if (!is_target_cert) { required_key_purpose = KeyPurpose::SERVER_AUTH; } else { required_key_purpose = KeyPurpose::SERVER_AUTH_STRICT; } } auto add_error_if_strict = [&](CertErrorId id) { if (required_key_purpose == KeyPurpose::SERVER_AUTH_STRICT || required_key_purpose == KeyPurpose::CLIENT_AUTH_STRICT) { errors->AddError(id); } else { errors->AddWarning(id); } }; if (is_target_cert) { // Loosely based upon CABF BR version 1.8.4, 7.1.2.3(f). We are more // permissive in that we still allow EKU any to be present in a leaf // certificate, but we ignore it for purposes of server or client auth. We // are less permissive in that we prohibit Code Signing, OCSP Signing, and // Time Stamping which are currently only a SHOULD NOT. The BR does // explicitly allow Email authentication to be present, as this still exists // in the wild (2022), so we do not prohibit Email authentication here (and // by extension must allow it to be present in the signer, below). if (!cert.has_extended_key_usage()) { // This is added as a warning, an error will be added in STRICT modes // if we then lack client or server auth due to this not being present. errors->AddWarning(cert_errors::kEkuNotPresent); } else { if (has_code_signing_eku) { add_error_if_strict(cert_errors::kEkuHasProhibitedCodeSigning); } if (has_ocsp_signing_eku) { add_error_if_strict(cert_errors::kEkuHasProhibitedOCSPSigning); } if (has_time_stamping_eku) { add_error_if_strict(cert_errors::kEkuHasProhibitedTimeStamping); } } } else if (is_target_cert_issuer) { // Handle the decision to overload EKU as a constraint on issuers. // // CABF BR version 1.8.4, 7.1.2.2(g) pertains to the case of "Certs used to // issue TLS certificates", While the BR refers to the entire chain of // intermediates, there are a number of exceptions regarding CA ownership // and cross signing which are impossible for us to know or enforce here. // Therefore, we can only enforce at the level of the intermediate that // issued our target certificate. This means we we differ in the following // ways: // - We only enforce at the issuer of the TLS certificate. // - We allow email protection to exist in the issuer, since without // this it can not be allowed in the client (other than via EKU any)) // - As in the leaf certificate case, we allow EKU any to be present, but // we ignore it for the purposes of server or client auth. // // At this time (until at least 2023) some intermediates are lacking EKU in // the world at large from common CA's, so we allow the noEKU case to permit // everything. // TODO(bbe): enforce requiring EKU in the issuer when we can manage it. if (cert.has_extended_key_usage()) { if (has_code_signing_eku) { add_error_if_strict(cert_errors::kEkuHasProhibitedCodeSigning); } if (has_time_stamping_eku) { add_error_if_strict(cert_errors::kEkuHasProhibitedTimeStamping); } } } // Otherwise, we are a parent of an issuer of a TLS certificate. The CABF // BR version 1.8.4, 7.1.2.2(g) goes as far as permitting EKU any in certain // cases of Cross Signing and CA Ownership, having permitted cases where EKU // is permitted to not be present at all. These cases are not practical to // differentiate here and therefore we don't attempt to enforce any further // EKU "constraints" on such certificates. Unlike the above cases we also // allow the use of EKU any for client or server auth constraint purposes. switch (required_key_purpose) { case KeyPurpose::ANY_EKU: case KeyPurpose::CLIENT_AUTH_STRICT_LEAF: case KeyPurpose::SERVER_AUTH_STRICT_LEAF: assert(0); // NOTREACHED return; case KeyPurpose::SERVER_AUTH: case KeyPurpose::SERVER_AUTH_STRICT: { if (has_any_eku && !has_server_auth_eku) { if (is_target_cert || is_target_cert_issuer) { errors->AddWarning(cert_errors::kEkuLacksServerAuthButHasAnyEKU); } else { // Accept anyEKU for server auth below target issuer. has_server_auth_eku = true; } } if (is_target_cert_issuer && !cert.has_extended_key_usage()) { // Accept noEKU for server auth in target issuer. // TODO(bbe): remove this once BR requirements catch up with CA's. has_server_auth_eku = true; } if (required_key_purpose == KeyPurpose::SERVER_AUTH) { // Legacy compatible. if (cert.has_extended_key_usage() && !has_server_auth_eku && !has_any_eku) { errors->AddError(cert_errors::kEkuLacksServerAuth); } } else { if (!has_server_auth_eku) { errors->AddError(cert_errors::kEkuLacksServerAuth); } } break; } case KeyPurpose::CLIENT_AUTH: case KeyPurpose::CLIENT_AUTH_STRICT: { if (has_any_eku && !has_client_auth_eku) { if (is_target_cert || is_target_cert_issuer) { errors->AddWarning(cert_errors::kEkuLacksClientAuthButHasAnyEKU); } else { // accept anyEKU for client auth. has_client_auth_eku = true; } } if (required_key_purpose == KeyPurpose::CLIENT_AUTH) { // Legacy-compatible. if (cert.has_extended_key_usage() && !has_client_auth_eku && !has_any_eku) { errors->AddError(cert_errors::kEkuLacksClientAuth); } } else { if (!has_client_auth_eku) { errors->AddError(cert_errors::kEkuLacksClientAuth); } } break; } } } // Representation of RFC 5280's "valid_policy_tree", used to keep track of the // valid policies and policy re-mappings. This structure is defined in // section 6.1.2. // // ValidPolicyGraph differs from RFC 5280's description in that: // // (1) It does not track "qualifier_set". This is not needed as it is not // output by this implementation. // // (2) It builds a directed acyclic graph, rather than a tree. When a given // policy matches multiple parents, RFC 5280 makes a separate node for // each parent. This representation condenses them into one node with // multiple parents. // // (3) It does not track "expected_policy_set" or anyPolicy nodes directly. // Rather it maintains, only for the most recent level, whether there is an // anyPolicy node and an inverted map of all "expected_policy_set" values. // // (4) Some pruning steps are deferred to when policies are evaluated, as a // reachability pass. class ValidPolicyGraph { public: ValidPolicyGraph() = default; ValidPolicyGraph(const ValidPolicyGraph &) = delete; ValidPolicyGraph &operator=(const ValidPolicyGraph &) = delete; // A Node is an entry in the policy graph. It contains information about some // policy asserted by a certificate in the chain. The policy OID itself is // omitted because it is the key in the Level map. struct Node { // The list of "valid_policy" values for all nodes which are a parent of // this node, other than anyPolicy. If empty, this node has a single parent, // anyPolicy. // // Nodes whose parent is anyPolicy are root policies, and may be returned // in the authorities-constrained-policy-set. Nodes with a concrete policy // as a parent are derived from that policy in the issuer certificate, // possibly with a policy mapping applied. // // Note it is not possible for a policy to have both anyPolicy and a // concrete policy as a parent. Section 6.1.3, step d.1.ii only runs if // there was no match in step d.1.i. std::vector parent_policies; // Whether this node matches a policy mapping in the certificate. If true, // its "expected_policy_set" comes from the policy mappings extension. If // false, its "expected_policy_set" is itself. bool mapped = false; // Whether this node is reachable from some valid policy in the end-entity // certificate. Computed during GetValidRootPolicySet(). bool reachable = false; }; // The policy graph is organized into "levels", each corresponding to a // certificate in the chain. We maintain a map from "valid_policy" to the // corresponding Node. This is the set of policies asserted by this // certificate. The special anyPolicy OID is handled separately below. using Level = std::map; // Additional per-level information that only needs to be maintained for the // bottom-most level. struct LevelDetails { // Maintains the "expected_policy_set" values for nodes in a level of the // graph, but the map is inverted from RFC 5280's formulation. For a given // policy OID P, other than anyPolicy, this map gives the set of nodes where // P appears in the node's "expected_policy_set". anyPolicy is handled // separately below. std::map> expected_policy_map; // Whether there is a node at this level whose "valid_policy" is anyPolicy. // // Note anyPolicy's "expected_policy_set" always {anyPolicy}, and anyPolicy // will never appear in the "expected_policy_set" of any other policy. That // means this field also captures how anyPolicy appears in // "expected_policy_set". bool has_any_policy = false; }; // Initializes the ValidPolicyGraph. void Init() { SetNull(); StartLevel(); AddAnyPolicyNode(); } // In RFC 5280 valid_policy_tree may be set to null. That is represented here // by emptiness. bool IsNull() const { return !current_level_.has_any_policy && (levels_.empty() || levels_.back().empty()); } void SetNull() { levels_.clear(); current_level_ = LevelDetails{}; } // Completes the previous level, returning a corresponding LevelDetails // structure, and starts a new level. LevelDetails StartLevel() { // Finish building expected_policy_map for the previous level. if (!levels_.empty()) { for (const auto &[policy, node] : levels_.back()) { if (!node.mapped) { current_level_.expected_policy_map[policy].push_back(policy); } } } LevelDetails prev_level = std::move(current_level_); levels_.emplace_back(); current_level_ = LevelDetails{}; return prev_level; } // Gets the set of policies (in terms of root authority's policy domain) that // are valid at the bottom level of the policy graph, intersected with // |user_initial_policy_set|. This is what X.509 calls // "user-constrained-policy-set". // // This method may only be called once, after the policy graph is constructed. std::set GetUserConstrainedPolicySet( const std::set &user_initial_policy_set) { if (levels_.empty()) { return {}; } bool user_has_any_policy = user_initial_policy_set.count(der::Input(kAnyPolicyOid)) != 0; if (current_level_.has_any_policy) { if (user_has_any_policy) { return {der::Input(kAnyPolicyOid)}; } return user_initial_policy_set; } // The root's policy domain is determined by nodes with anyPolicy as a // parent. However, we must limit to those which are reachable from the // end-entity certificate because we defer some pruning steps. for (auto &[policy, node] : levels_.back()) { // GCC before 8.1 tracks individual unused bindings and does not support // marking them [[maybe_unused]]. (void)policy; node.reachable = true; } std::set policy_set; for (size_t i = levels_.size() - 1; i < levels_.size(); i--) { for (auto &[policy, node] : levels_[i]) { if (!node.reachable) { continue; } if (node.parent_policies.empty()) { // |node|'s parent is anyPolicy, so this is in the root policy domain. // Add it to the set if it is also in user's list. if (user_has_any_policy || user_initial_policy_set.count(policy) > 0) { policy_set.insert(policy); } } else if (i > 0) { // Otherwise, continue searching the previous level. for (der::Input parent : node.parent_policies) { auto iter = levels_[i - 1].find(parent); if (iter != levels_[i - 1].end()) { iter->second.reachable = true; } } } } } return policy_set; } // Adds a node with policy anyPolicy to the current level. void AddAnyPolicyNode() { assert(!levels_.empty()); current_level_.has_any_policy = true; } // Adds a node to the current level which is a child of |parent_policies| with // the specified policy. void AddNode(der::Input policy, std::vector parent_policies) { assert(policy != der::Input(kAnyPolicyOid)); AddNodeReturningIterator(policy, std::move(parent_policies)); } // Adds a node to the current level which is a child of anyPolicy with the // specified policy. void AddNodeWithParentAnyPolicy(der::Input policy) { // An empty parent set represents a node parented by anyPolicy. AddNode(policy, {}); } // Maps |issuer_policy| to |subject_policy|, as in RFC 5280, section 6.1.4, // step b.1. void AddPolicyMapping(der::Input issuer_policy, der::Input subject_policy) { assert(issuer_policy != der::Input(kAnyPolicyOid)); assert(subject_policy != der::Input(kAnyPolicyOid)); if (levels_.empty()) { return; } // The mapping only applies if |issuer_policy| exists in the current level. auto issuer_policy_iter = levels_.back().find(issuer_policy); if (issuer_policy_iter == levels_.back().end()) { // If there is no match, it can instead match anyPolicy. if (!current_level_.has_any_policy) { return; } // From RFC 5280, section 6.1.4, step b.1: // // If no node of depth i in the valid_policy_tree has a // valid_policy of ID-P but there is a node of depth i with a // valid_policy of anyPolicy, then generate a child node of // the node of depth i-1 that has a valid_policy of anyPolicy // as follows: [...] // // The anyPolicy node of depth i-1 is referring to the parent of the // anyPolicy node of depth i. The parent of anyPolicy is always anyPolicy. issuer_policy_iter = AddNodeReturningIterator(issuer_policy, {}); } // Unmapped nodes have a singleton "expected_policy_set" containing their // valid_policy. Track whether nodes have been mapped so this can be filled // in at StartLevel(). issuer_policy_iter->second.mapped = true; // Add |subject_policy| to |issuer_policy|'s "expected_policy_set". current_level_.expected_policy_map[subject_policy].push_back(issuer_policy); } // Removes the node with the specified policy from the current level. void DeleteNode(der::Input policy) { if (!levels_.empty()) { levels_.back().erase(policy); } } private: Level::iterator AddNodeReturningIterator( der::Input policy, std::vector parent_policies) { assert(policy != der::Input(kAnyPolicyOid)); auto [iter, inserted] = levels_.back().insert( std::pair{policy, Node{std::move(parent_policies)}}); // GCC before 8.1 tracks individual unused bindings and does not support // marking them [[maybe_unused]]. (void)inserted; assert(inserted); return iter; } // The list of levels, starting from the root. std::vector levels_; // Additional information about the current level. LevelDetails current_level_; }; // Class that encapsulates the state variables used by certificate path // validation. class PathVerifier { public: // Same parameters and meaning as VerifyCertificateChain(). void Run(const ParsedCertificateList &certs, const CertificateTrust &last_cert_trust, VerifyCertificateChainDelegate *delegate, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, const std::set &user_initial_policy_set, InitialPolicyMappingInhibit initial_policy_mapping_inhibit, InitialAnyPolicyInhibit initial_any_policy_inhibit, std::set *user_constrained_policy_set, CertPathErrors *errors); private: // Verifies and updates the valid policies. This corresponds with RFC 5280 // section 6.1.3 steps d-f. void VerifyPolicies(const ParsedCertificate &cert, bool is_target_cert, CertErrors *errors); // Applies the policy mappings. This corresponds with RFC 5280 section 6.1.4 // steps a-b. void VerifyPolicyMappings(const ParsedCertificate &cert, CertErrors *errors); // Applies policyConstraints and inhibitAnyPolicy. This corresponds with RFC // 5280 section 6.1.4 steps i-j. void ApplyPolicyConstraints(const ParsedCertificate &cert); // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate // Processing" procedure. void BasicCertificateProcessing(const ParsedCertificate &cert, bool is_target_cert, bool is_target_cert_issuer, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors, bool *shortcircuit_chain_validation); // This function corresponds to RFC 5280 section 6.1.4's "Preparation for // Certificate i+1" procedure. |cert| is expected to be an intermediate. void PrepareForNextCertificate(const ParsedCertificate &cert, CertErrors *errors); // This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up // Procedure". It does processing for the final certificate (the target cert). void WrapUp(const ParsedCertificate &cert, KeyPurpose required_key_purpose, const std::set &user_initial_policy_set, bool allow_precertificate, CertErrors *errors); // Enforces trust anchor constraints compatibile with RFC 5937. // // Note that the anchor constraints are encoded via the attached certificate // itself. void ApplyTrustAnchorConstraints(const ParsedCertificate &cert, KeyPurpose required_key_purpose, CertErrors *errors); // Initializes the path validation algorithm given anchor constraints. This // follows the description in RFC 5937 void ProcessRootCertificate(const ParsedCertificate &cert, const CertificateTrust &trust, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors, bool *shortcircuit_chain_validation); // Processes verification when the input is a single certificate. This is not // defined by any standard. We attempt to match the de-facto behaviour of // Operating System verifiers. void ProcessSingleCertChain(const ParsedCertificate &cert, const CertificateTrust &trust, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors); // Parses |spki| to an EVP_PKEY and checks whether the public key is accepted // by |delegate_|. On failure parsing returns nullptr. If either parsing the // key or key policy failed, adds a high-severity error to |errors|. bssl::UniquePtr ParseAndCheckPublicKey(der::Input spki, CertErrors *errors); ValidPolicyGraph valid_policy_graph_; std::set user_constrained_policy_set_; // Will contain a NameConstraints for each previous cert in the chain which // had nameConstraints. This corresponds to the permitted_subtrees and // excluded_subtrees state variables from RFC 5280. std::vector name_constraints_list_; // |explicit_policy_| corresponds with the same named variable from RFC 5280 // section 6.1.2: // // explicit_policy: an integer that indicates if a non-NULL // valid_policy_tree is required. The integer indicates the // number of non-self-issued certificates to be processed before // this requirement is imposed. Once set, this variable may be // decreased, but may not be increased. That is, if a certificate in the // path requires a non-NULL valid_policy_tree, a later certificate cannot // remove this requirement. If initial-explicit-policy is set, then the // initial value is 0, otherwise the initial value is n+1. size_t explicit_policy_; // |inhibit_any_policy_| corresponds with the same named variable from RFC // 5280 section 6.1.2: // // inhibit_anyPolicy: an integer that indicates whether the // anyPolicy policy identifier is considered a match. The // integer indicates the number of non-self-issued certificates // to be processed before the anyPolicy OID, if asserted in a // certificate other than an intermediate self-issued // certificate, is ignored. Once set, this variable may be // decreased, but may not be increased. That is, if a // certificate in the path inhibits processing of anyPolicy, a // later certificate cannot permit it. If initial-any-policy- // inhibit is set, then the initial value is 0, otherwise the // initial value is n+1. size_t inhibit_any_policy_; // |policy_mapping_| corresponds with the same named variable from RFC 5280 // section 6.1.2: // // policy_mapping: an integer that indicates if policy mapping // is permitted. The integer indicates the number of non-self- // issued certificates to be processed before policy mapping is // inhibited. Once set, this variable may be decreased, but may // not be increased. That is, if a certificate in the path // specifies that policy mapping is not permitted, it cannot be // overridden by a later certificate. If initial-policy- // mapping-inhibit is set, then the initial value is 0, // otherwise the initial value is n+1. size_t policy_mapping_; // |working_public_key_| is an amalgamation of 3 separate variables from RFC // 5280: // * working_public_key // * working_public_key_algorithm // * working_public_key_parameters // // They are combined for simplicity since the signature verification takes an // EVP_PKEY, and the parameter inheritence is not applicable for the supported // key types. |working_public_key_| may be null if parsing failed. // // An approximate explanation of |working_public_key_| is this description // from RFC 5280 section 6.1.2: // // working_public_key: the public key used to verify the // signature of a certificate. bssl::UniquePtr working_public_key_; // |working_normalized_issuer_name_| is the normalized value of the // working_issuer_name variable in RFC 5280 section 6.1.2: // // working_issuer_name: the issuer distinguished name expected // in the next certificate in the chain. der::Input working_normalized_issuer_name_; // |max_path_length_| corresponds with the same named variable in RFC 5280 // section 6.1.2. // // max_path_length: this integer is initialized to n, is // decremented for each non-self-issued certificate in the path, // and may be reduced to the value in the path length constraint // field within the basic constraints extension of a CA // certificate. size_t max_path_length_; VerifyCertificateChainDelegate *delegate_; }; void PathVerifier::VerifyPolicies(const ParsedCertificate &cert, bool is_target_cert, CertErrors *errors) { // From RFC 5280 section 6.1.3: // // (d) If the certificate policies extension is present in the // certificate and the valid_policy_tree is not NULL, process // the policy information by performing the following steps in // order: if (cert.has_policy_oids() && !valid_policy_graph_.IsNull()) { ValidPolicyGraph::LevelDetails previous_level = valid_policy_graph_.StartLevel(); // (1) For each policy P not equal to anyPolicy in the // certificate policies extension, let P-OID denote the OID // for policy P and P-Q denote the qualifier set for policy // P. Perform the following steps in order: bool cert_has_any_policy = false; for (der::Input p_oid : cert.policy_oids()) { if (p_oid == der::Input(kAnyPolicyOid)) { cert_has_any_policy = true; continue; } // (i) For each node of depth i-1 in the valid_policy_tree // where P-OID is in the expected_policy_set, create a // child node as follows: set the valid_policy to P-OID, // set the qualifier_set to P-Q, and set the // expected_policy_set to {P-OID}. auto iter = previous_level.expected_policy_map.find(p_oid); if (iter != previous_level.expected_policy_map.end()) { valid_policy_graph_.AddNode( p_oid, /*parent_policies=*/std::move(iter->second)); previous_level.expected_policy_map.erase(iter); } else if (previous_level.has_any_policy) { // (ii) If there was no match in step (i) and the // valid_policy_tree includes a node of depth i-1 with // the valid_policy anyPolicy, generate a child node with // the following values: set the valid_policy to P-OID, // set the qualifier_set to P-Q, and set the // expected_policy_set to {P-OID}. valid_policy_graph_.AddNodeWithParentAnyPolicy(p_oid); } } // (2) If the certificate policies extension includes the policy // anyPolicy with the qualifier set AP-Q and either (a) // inhibit_anyPolicy is greater than 0 or (b) i 0) || (!is_target_cert && IsSelfIssued(cert)))) { for (auto &[p_oid, parent_policies] : previous_level.expected_policy_map) { valid_policy_graph_.AddNode(p_oid, std::move(parent_policies)); } if (previous_level.has_any_policy) { valid_policy_graph_.AddAnyPolicyNode(); } } // (3) If there is a node in the valid_policy_tree of depth i-1 // or less without any child nodes, delete that node. Repeat // this step until there are no nodes of depth i-1 or less // without children. // // This implementation does this as part of GetUserConstrainedPolicySet(). // Only the current level needs to be pruned to compute the policy graph. } // (e) If the certificate policies extension is not present, set the // valid_policy_tree to NULL. if (!cert.has_policy_oids()) { valid_policy_graph_.SetNull(); } // (f) Verify that either explicit_policy is greater than 0 or the // valid_policy_tree is not equal to NULL; if (!((explicit_policy_ > 0) || !valid_policy_graph_.IsNull())) { errors->AddError(cert_errors::kNoValidPolicy); } } void PathVerifier::VerifyPolicyMappings(const ParsedCertificate &cert, CertErrors *errors) { if (!cert.has_policy_mappings()) { return; } // From RFC 5280 section 6.1.4: // // (a) If a policy mappings extension is present, verify that the // special value anyPolicy does not appear as an // issuerDomainPolicy or a subjectDomainPolicy. for (const ParsedPolicyMapping &mapping : cert.policy_mappings()) { if (mapping.issuer_domain_policy == der::Input(kAnyPolicyOid) || mapping.subject_domain_policy == der::Input(kAnyPolicyOid)) { // Because this implementation continues processing certificates after // this error, clear the valid policy graph to ensure the // "user_constrained_policy_set" output upon failure is empty. valid_policy_graph_.SetNull(); errors->AddError(cert_errors::kPolicyMappingAnyPolicy); return; } } // (b) If a policy mappings extension is present, then for each // issuerDomainPolicy ID-P in the policy mappings extension: // // (1) If the policy_mapping variable is greater than 0, for each // node in the valid_policy_tree of depth i where ID-P is the // valid_policy, set expected_policy_set to the set of // subjectDomainPolicy values that are specified as // equivalent to ID-P by the policy mappings extension. // // If no node of depth i in the valid_policy_tree has a // valid_policy of ID-P but there is a node of depth i with a // valid_policy of anyPolicy, then generate a child node of // the node of depth i-1 that has a valid_policy of anyPolicy // as follows: // // (i) set the valid_policy to ID-P; // // (ii) set the qualifier_set to the qualifier set of the // policy anyPolicy in the certificate policies // extension of certificate i; and // // (iii) set the expected_policy_set to the set of // subjectDomainPolicy values that are specified as // equivalent to ID-P by the policy mappings extension. // if (policy_mapping_ > 0) { for (const ParsedPolicyMapping &mapping : cert.policy_mappings()) { valid_policy_graph_.AddPolicyMapping(mapping.issuer_domain_policy, mapping.subject_domain_policy); } } // (b) If a policy mappings extension is present, then for each // issuerDomainPolicy ID-P in the policy mappings extension: // // ... // // (2) If the policy_mapping variable is equal to 0: // // (i) delete each node of depth i in the valid_policy_tree // where ID-P is the valid_policy. // // (ii) If there is a node in the valid_policy_tree of depth // i-1 or less without any child nodes, delete that // node. Repeat this step until there are no nodes of // depth i-1 or less without children. // // Step (ii) is deferred to part of GetUserConstrainedPolicySet(). if (policy_mapping_ == 0) { for (const ParsedPolicyMapping &mapping : cert.policy_mappings()) { valid_policy_graph_.DeleteNode(mapping.issuer_domain_policy); } } } void PathVerifier::ApplyPolicyConstraints(const ParsedCertificate &cert) { // RFC 5280 section 6.1.4 step i-j: // (i) If a policy constraints extension is included in the // certificate, modify the explicit_policy and policy_mapping // state variables as follows: if (cert.has_policy_constraints()) { // (1) If requireExplicitPolicy is present and is less than // explicit_policy, set explicit_policy to the value of // requireExplicitPolicy. if (cert.policy_constraints().require_explicit_policy && cert.policy_constraints().require_explicit_policy.value() < explicit_policy_) { explicit_policy_ = cert.policy_constraints().require_explicit_policy.value(); } // (2) If inhibitPolicyMapping is present and is less than // policy_mapping, set policy_mapping to the value of // inhibitPolicyMapping. if (cert.policy_constraints().inhibit_policy_mapping && cert.policy_constraints().inhibit_policy_mapping.value() < policy_mapping_) { policy_mapping_ = cert.policy_constraints().inhibit_policy_mapping.value(); } } // (j) If the inhibitAnyPolicy extension is included in the // certificate and is less than inhibit_anyPolicy, set // inhibit_anyPolicy to the value of inhibitAnyPolicy. if (cert.inhibit_any_policy() && cert.inhibit_any_policy().value() < inhibit_any_policy_) { inhibit_any_policy_ = cert.inhibit_any_policy().value(); } } void PathVerifier::BasicCertificateProcessing( const ParsedCertificate &cert, bool is_target_cert, bool is_target_cert_issuer, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors, bool *shortcircuit_chain_validation) { *shortcircuit_chain_validation = false; // Check that the signature algorithms in Certificate vs TBSCertificate // match. This isn't part of RFC 5280 section 6.1.3, but is mandated by // sections 4.1.1.2 and 4.1.2.3. if (!VerifySignatureAlgorithmsMatch(cert, errors)) { BSSL_CHECK(errors->ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH)); *shortcircuit_chain_validation = true; } // Check whether this signature algorithm is allowed. if (!cert.signature_algorithm().has_value() || !delegate_->IsSignatureAlgorithmAcceptable(*cert.signature_algorithm(), errors)) { *shortcircuit_chain_validation = true; errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm); return; } if (working_public_key_) { // Verify the digital signature using the previous certificate's key (RFC // 5280 section 6.1.3 step a.1). if (!VerifySignedData(*cert.signature_algorithm(), cert.tbs_certificate_tlv(), cert.signature_value(), working_public_key_.get(), delegate_->GetVerifyCache())) { *shortcircuit_chain_validation = true; errors->AddError(cert_errors::kVerifySignedDataFailed); } } else { // If `working_public_key_` is null, that indicates the SPKI of the issuer // could not be parsed. Handle this the same way as an invalid signature by // shortcircuiting the rest of verification. // An error should already have been added by ParseAndCheckPublicKey, but // it's added on the CertErrors for the issuer, so we can't BSSL_CHECK // errors->ContainsAnyErrorWithSeverity here. (It will be BSSL_CHECKed when // the shortcircuit_chain_validation is acted on in PathVerifier::Run.) *shortcircuit_chain_validation = true; } if (*shortcircuit_chain_validation) { return; } // Check the time range for the certificate's validity, ensuring it is valid // at |time|. // (RFC 5280 section 6.1.3 step a.2) VerifyTimeValidity(cert, time, errors); // RFC 5280 section 6.1.3 step a.3 calls for checking the certificate's // revocation status here. In this implementation revocation checking is // implemented separately from path validation. // Verify the certificate's issuer name matches the issuing certificate's // subject name. (RFC 5280 section 6.1.3 step a.4) if (cert.normalized_issuer() != working_normalized_issuer_name_) { errors->AddError(cert_errors::kSubjectDoesNotMatchIssuer); } // Name constraints (RFC 5280 section 6.1.3 step b & c) // If certificate i is self-issued and it is not the final certificate in the // path, skip this step for certificate i. if (!name_constraints_list_.empty() && (!IsSelfIssued(cert) || is_target_cert)) { for (const NameConstraints *nc : name_constraints_list_) { nc->IsPermittedCert(cert.normalized_subject(), cert.subject_alt_names(), errors); } } // RFC 5280 section 6.1.3 step d - f. VerifyPolicies(cert, is_target_cert, errors); // The key purpose is checked not just for the end-entity certificate, but // also interpreted as a constraint when it appears in intermediates. This // goes beyond what RFC 5280 describes, but is the de-facto standard. See // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questions VerifyExtendedKeyUsage(cert, required_key_purpose, errors, is_target_cert, is_target_cert_issuer); } void PathVerifier::PrepareForNextCertificate(const ParsedCertificate &cert, CertErrors *errors) { // RFC 5280 section 6.1.4 step a-b VerifyPolicyMappings(cert, errors); // From RFC 5280 section 6.1.4 step c: // // Assign the certificate subject name to working_normalized_issuer_name. working_normalized_issuer_name_ = cert.normalized_subject(); // From RFC 5280 section 6.1.4 step d: // // Assign the certificate subjectPublicKey to working_public_key. working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); // Note that steps e and f are omitted as they are handled by // the assignment to |working_spki| above. See the definition // of |working_spki|. // From RFC 5280 section 6.1.4 step g: if (cert.has_name_constraints()) { name_constraints_list_.push_back(&cert.name_constraints()); } // (h) If certificate i is not self-issued: if (!IsSelfIssued(cert)) { // (1) If explicit_policy is not 0, decrement explicit_policy by // 1. if (explicit_policy_ > 0) { explicit_policy_ -= 1; } // (2) If policy_mapping is not 0, decrement policy_mapping by 1. if (policy_mapping_ > 0) { policy_mapping_ -= 1; } // (3) If inhibit_anyPolicy is not 0, decrement inhibit_anyPolicy // by 1. if (inhibit_any_policy_ > 0) { inhibit_any_policy_ -= 1; } } // RFC 5280 section 6.1.4 step i-j: ApplyPolicyConstraints(cert); // From RFC 5280 section 6.1.4 step k: // // If certificate i is a version 3 certificate, verify that the // basicConstraints extension is present and that cA is set to // TRUE. (If certificate i is a version 1 or version 2 // certificate, then the application MUST either verify that // certificate i is a CA certificate through out-of-band means // or reject the certificate. Conforming implementations may // choose to reject all version 1 and version 2 intermediate // certificates.) // // This code implicitly rejects non version 3 intermediates, since they // can't contain a BasicConstraints extension. if (!cert.has_basic_constraints()) { errors->AddError(cert_errors::kMissingBasicConstraints); } else if (!cert.basic_constraints().is_ca) { errors->AddError(cert_errors::kBasicConstraintsIndicatesNotCa); } // From RFC 5280 section 6.1.4 step l: // // If the certificate was not self-issued, verify that // max_path_length is greater than zero and decrement // max_path_length by 1. if (!IsSelfIssued(cert)) { if (max_path_length_ == 0) { errors->AddError(cert_errors::kMaxPathLengthViolated); } else { --max_path_length_; } } // From RFC 5280 section 6.1.4 step m: // // If pathLenConstraint is present in the certificate and is // less than max_path_length, set max_path_length to the value // of pathLenConstraint. if (cert.has_basic_constraints() && cert.basic_constraints().has_path_len && cert.basic_constraints().path_len < max_path_length_) { max_path_length_ = cert.basic_constraints().path_len; } // From RFC 5280 section 6.1.4 step n: // // If a key usage extension is present, verify that the // keyCertSign bit is set. if (cert.has_key_usage() && !cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) { errors->AddError(cert_errors::kKeyCertSignBitNotSet); } // From RFC 5280 section 6.1.4 step o: // // Recognize and process any other critical extension present in // the certificate. Process any other recognized non-critical // extension present in the certificate that is relevant to path // processing. VerifyNoUnconsumedCriticalExtensions(cert, errors, delegate_->AcceptPreCertificates()); } // Checks if the target certificate has the CA bit set. If it does, add // the appropriate error or warning to |errors|. void VerifyTargetCertIsNotCA(const ParsedCertificate &cert, KeyPurpose required_key_purpose, CertErrors *errors) { if (cert.has_basic_constraints() && cert.basic_constraints().is_ca) { // In spite of RFC 5280 4.2.1.9 which says the CA properties MAY exist in // an end entity certificate, the CABF Baseline Requirements version // 1.8.4, 7.1.2.3(d) prohibit the CA bit being set in an end entity // certificate. switch (required_key_purpose) { case KeyPurpose::ANY_EKU: break; case KeyPurpose::SERVER_AUTH: case KeyPurpose::CLIENT_AUTH: errors->AddWarning(cert_errors::kTargetCertShouldNotBeCa); break; case KeyPurpose::SERVER_AUTH_STRICT: case KeyPurpose::CLIENT_AUTH_STRICT: case KeyPurpose::CLIENT_AUTH_STRICT_LEAF: case KeyPurpose::SERVER_AUTH_STRICT_LEAF: errors->AddError(cert_errors::kTargetCertShouldNotBeCa); break; } } } void PathVerifier::WrapUp(const ParsedCertificate &cert, KeyPurpose required_key_purpose, const std::set &user_initial_policy_set, bool allow_precertificate, CertErrors * errors) { // From RFC 5280 section 6.1.5: // (a) If explicit_policy is not 0, decrement explicit_policy by 1. if (explicit_policy_ > 0) { explicit_policy_ -= 1; } // (b) If a policy constraints extension is included in the // certificate and requireExplicitPolicy is present and has a // value of 0, set the explicit_policy state variable to 0. if (cert.has_policy_constraints() && cert.policy_constraints().require_explicit_policy.has_value() && cert.policy_constraints().require_explicit_policy == 0) { explicit_policy_ = 0; } // Note step c-e are omitted as the verification function does // not output the working public key. // From RFC 5280 section 6.1.5 step f: // // Recognize and process any other critical extension present in // the certificate n. Process any other recognized non-critical // extension present in certificate n that is relevant to path // processing. // // Note that this is duplicated by PrepareForNextCertificate() so as to // directly match the procedures in RFC 5280's section 6.1. VerifyNoUnconsumedCriticalExtensions(cert, errors, allow_precertificate); // This calculates the intersection from RFC 5280 section 6.1.5 step g, as // well as applying the deferred recursive node that were skipped earlier in // the process. user_constrained_policy_set_ = valid_policy_graph_.GetUserConstrainedPolicySet(user_initial_policy_set); // From RFC 5280 section 6.1.5 step g: // // If either (1) the value of explicit_policy variable is greater than // zero or (2) the valid_policy_tree is not NULL, then path processing // has succeeded. if (explicit_policy_ == 0 && user_constrained_policy_set_.empty()) { errors->AddError(cert_errors::kNoValidPolicy); } // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure", // however is implied by RFC 5280 section 4.2.1.9, as well as CABF Base // Requirements. VerifyTargetCertIsNotCA(cert, required_key_purpose, errors); // Check the public key for the target certificate. The public key for the // other certificates is already checked by PrepareForNextCertificate(). // Note that this step is not part of RFC 5280 6.1.5. ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); } void PathVerifier::ApplyTrustAnchorConstraints(const ParsedCertificate &cert, KeyPurpose required_key_purpose, CertErrors *errors) { // If certificatePolicies is present, process the policies. This matches the // handling for intermediates from RFC 5280 section 6.1.3.d (except that for // intermediates it is non-optional). It intentionally deviates from RFC 5937 // section 3.2 which says to intersect with user-initial-policy-set, since // processing as part of user-initial-policy-set has subtly different // semantics from being handled as part of the chain processing (see // https://crbug.com/1403258). if (cert.has_policy_oids()) { VerifyPolicies(cert, /*is_target_cert=*/false, errors); } // Process policyMappings, if present. This matches the handling for // intermediates from RFC 5280 section 6.1.4 step a-b. VerifyPolicyMappings(cert, errors); // Process policyConstraints and inhibitAnyPolicy. This matches the // handling for intermediates from RFC 5280 section 6.1.4 step i-j. // This intentionally deviates from RFC 5937 section 3.2 which says to // initialize the initial-any-policy-inhibit, initial-explicit-policy, and/or // initial-policy-mapping-inhibit inputs to verification. Those are all // bools, so they cannot properly represent the constraints encoded in the // policyConstraints and inhibitAnyPolicy extensions. ApplyPolicyConstraints(cert); // If keyUsage is present, verify that |cert| has correct keyUsage bits for a // CA. This matches the handling for intermediates from RFC 5280 section // 6.1.4 step n. if (cert.has_key_usage() && !cert.key_usage().AssertsBit(KEY_USAGE_BIT_KEY_CERT_SIGN)) { errors->AddError(cert_errors::kKeyCertSignBitNotSet); } // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling // done for intermediates (described in Web PKI's Baseline Requirements). VerifyExtendedKeyUsage(cert, required_key_purpose, errors, /*is_target_cert=*/false, /*is_target_cert_issuer=*/false); // The following enforcements follow from RFC 5937 (primarily section 3.2): // Initialize name constraints initial-permitted/excluded-subtrees. if (cert.has_name_constraints()) { name_constraints_list_.push_back(&cert.name_constraints()); } if (cert.has_basic_constraints()) { // Enforce CA=true if basicConstraints is present. This matches behavior of // other verifiers, and seems like a good thing to do to avoid a // certificate being used in the wrong context if it was specifically // marked as not being a CA. if (!cert.basic_constraints().is_ca) { errors->AddError(cert_errors::kBasicConstraintsIndicatesNotCa); } // From RFC 5937 section 3.2: // // If a basic constraints extension is associated with the trust // anchor and contains a pathLenConstraint value, set the // max_path_length state variable equal to the pathLenConstraint // value from the basic constraints extension. // if (cert.basic_constraints().has_path_len) { max_path_length_ = cert.basic_constraints().path_len; } } // From RFC 5937 section 2: // // Extensions may be marked critical or not critical. When trust anchor // constraints are enforced, clients MUST reject certification paths // containing a trust anchor with unrecognized critical extensions. VerifyNoUnconsumedCriticalExtensions(cert, errors, /*allow_precertificate=*/false); } void PathVerifier::ProcessRootCertificate(const ParsedCertificate &cert, const CertificateTrust &trust, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors, bool *shortcircuit_chain_validation) { *shortcircuit_chain_validation = false; switch (trust.type) { case CertificateTrustType::UNSPECIFIED: case CertificateTrustType::TRUSTED_LEAF: // Doesn't chain to a trust anchor - implicitly distrusted errors->AddError(cert_errors::kCertIsNotTrustAnchor); *shortcircuit_chain_validation = true; break; case CertificateTrustType::DISTRUSTED: // Chains to an actively distrusted certificate. errors->AddError(cert_errors::kDistrustedByTrustStore); *shortcircuit_chain_validation = true; break; case CertificateTrustType::TRUSTED_ANCHOR: case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: break; } if (*shortcircuit_chain_validation) { return; } if (trust.enforce_anchor_expiry) { VerifyTimeValidity(cert, time, errors); } if (trust.enforce_anchor_constraints) { if (trust.require_anchor_basic_constraints && !cert.has_basic_constraints()) { switch (cert.tbs().version) { case CertificateVersion::V1: case CertificateVersion::V2: break; case CertificateVersion::V3: errors->AddError(cert_errors::kMissingBasicConstraints); break; } } ApplyTrustAnchorConstraints(cert, required_key_purpose, errors); } // Use the certificate's SPKI and subject when verifying the next certificate. working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); working_normalized_issuer_name_ = cert.normalized_subject(); } void PathVerifier::ProcessSingleCertChain(const ParsedCertificate &cert, const CertificateTrust &trust, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, CertErrors *errors) { switch (trust.type) { case CertificateTrustType::UNSPECIFIED: case CertificateTrustType::TRUSTED_ANCHOR: // Target doesn't have a chain and isn't a directly trusted leaf - // implicitly distrusted. errors->AddError(cert_errors::kCertIsNotTrustAnchor); return; case CertificateTrustType::DISTRUSTED: // Target is directly distrusted. errors->AddError(cert_errors::kDistrustedByTrustStore); return; case CertificateTrustType::TRUSTED_LEAF: case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: break; } // Check the public key for the target certificate regardless of whether // `require_leaf_selfsigned` is true. This matches the check in WrapUp and // fulfills the documented behavior of the IsPublicKeyAcceptable delegate. ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); if (trust.require_leaf_selfsigned) { if (!VerifyCertificateIsSelfSigned(cert, delegate_->GetVerifyCache(), errors)) { // VerifyCertificateIsSelfSigned should have added an error, but just // double check to be safe. if (!errors->ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH)) { errors->AddError(cert_errors::kInternalError); } return; } } // There is no standard for what it means to verify a directly trusted leaf // certificate, so this is basically just checking common sense things that // also mirror what we observed to be enforced with the Operating System // native verifiers. VerifyTimeValidity(cert, time, errors); VerifyExtendedKeyUsage(cert, required_key_purpose, errors, /*is_target_cert=*/true, /*is_target_cert_issuer=*/false); // Checking for unknown critical extensions matches Windows, but is stricter // than the Mac verifier. VerifyNoUnconsumedCriticalExtensions(cert, errors, /*allow_precertificate=*/false); } bssl::UniquePtr PathVerifier::ParseAndCheckPublicKey( der::Input spki, CertErrors *errors) { // Parse the public key. bssl::UniquePtr pkey; if (!ParsePublicKey(spki, &pkey)) { errors->AddError(cert_errors::kFailedParsingSpki); return nullptr; } // Check if the key is acceptable by the delegate. if (!delegate_->IsPublicKeyAcceptable(pkey.get(), errors)) { errors->AddError(cert_errors::kUnacceptablePublicKey); } return pkey; } void PathVerifier::Run( const ParsedCertificateList &certs, const CertificateTrust &last_cert_trust, VerifyCertificateChainDelegate *delegate, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, const std::set &user_initial_policy_set, InitialPolicyMappingInhibit initial_policy_mapping_inhibit, InitialAnyPolicyInhibit initial_any_policy_inhibit, std::set *user_constrained_policy_set, CertPathErrors *errors) { // This implementation is structured to mimic the description of certificate // path verification given by RFC 5280 section 6.1. BSSL_CHECK(delegate); BSSL_CHECK(errors); delegate_ = delegate; // An empty chain is necessarily invalid. if (certs.empty()) { errors->GetOtherErrors()->AddError(cert_errors::kChainIsEmpty); return; } // Verifying a trusted leaf certificate isn't a well-specified operation, so // it's handled separately from the RFC 5280 defined verification process. if (certs.size() == 1) { ProcessSingleCertChain(*certs.front(), last_cert_trust, time, required_key_purpose, errors->GetErrorsForCert(0)); return; } // RFC 5280's "n" variable is the length of the path, which does not count // the trust anchor. (Although in practice it doesn't really change behaviors // if n is used in place of n+1). const size_t n = certs.size() - 1; valid_policy_graph_.Init(); // RFC 5280 section section 6.1.2: // // If initial-explicit-policy is set, then the initial value // [of explicit_policy] is 0, otherwise the initial value is n+1. explicit_policy_ = initial_explicit_policy == InitialExplicitPolicy::kTrue ? 0 : n + 1; // RFC 5280 section section 6.1.2: // // If initial-any-policy-inhibit is set, then the initial value // [of inhibit_anyPolicy] is 0, otherwise the initial value is n+1. inhibit_any_policy_ = initial_any_policy_inhibit == InitialAnyPolicyInhibit::kTrue ? 0 : n + 1; // RFC 5280 section section 6.1.2: // // If initial-policy-mapping-inhibit is set, then the initial value // [of policy_mapping] is 0, otherwise the initial value is n+1. policy_mapping_ = initial_policy_mapping_inhibit == InitialPolicyMappingInhibit::kTrue ? 0 : n + 1; // RFC 5280 section section 6.1.2: // // max_path_length: this integer is initialized to n, ... max_path_length_ = n; // Iterate over all the certificates in the reverse direction: starting from // the root certificate and progressing towards the target certificate. // // * i=0 : Root certificate (i.e. trust anchor) // * i=1 : Certificate issued by root // * i=x : Certificate i=x is issued by certificate i=x-1 // * i=n : Target certificate. for (size_t i = 0; i < certs.size(); ++i) { const size_t index_into_certs = certs.size() - i - 1; // |is_target_cert| is true if the current certificate is the target // certificate being verified. The target certificate isn't necessarily an // end-entity certificate. const bool is_target_cert = index_into_certs == 0; const bool is_target_cert_issuer = index_into_certs == 1; const bool is_root_cert = i == 0; const ParsedCertificate &cert = *certs[index_into_certs]; // Output errors for the current certificate into an error bucket that is // associated with that certificate. CertErrors *cert_errors = errors->GetErrorsForCert(index_into_certs); if (is_root_cert) { bool shortcircuit_chain_validation = false; ProcessRootCertificate(cert, last_cert_trust, time, required_key_purpose, cert_errors, &shortcircuit_chain_validation); if (shortcircuit_chain_validation) { // Chains that don't start from a trusted root should short-circuit the // rest of the verification, as accumulating more errors from untrusted // certificates would not be meaningful. BSSL_CHECK(cert_errors->ContainsAnyErrorWithSeverity( CertError::SEVERITY_HIGH)); return; } // Don't do any other checks for root certificates. continue; } bool shortcircuit_chain_validation = false; // Per RFC 5280 section 6.1: // * Do basic processing for each certificate // * If it is the last certificate in the path (target certificate) // - Then run "Wrap up" // - Otherwise run "Prepare for Next cert" BasicCertificateProcessing(cert, is_target_cert, is_target_cert_issuer, time, required_key_purpose, cert_errors, &shortcircuit_chain_validation); if (shortcircuit_chain_validation) { // Signature errors or unparsable SPKIs should short-circuit the rest of // the verification, as accumulating more errors from untrusted // certificates would not be meaningful. BSSL_CHECK( errors->ContainsAnyErrorWithSeverity(CertError::SEVERITY_HIGH)); return; } if (!is_target_cert) { PrepareForNextCertificate(cert, cert_errors); } else { WrapUp(cert, required_key_purpose, user_initial_policy_set, delegate->AcceptPreCertificates(), cert_errors); } } if (user_constrained_policy_set) { *user_constrained_policy_set = user_constrained_policy_set_; } // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1: // // A certificate MUST NOT appear more than once in a prospective // certification path. } } // namespace VerifyCertificateChainDelegate::~VerifyCertificateChainDelegate() = default; void VerifyCertificateChain( const ParsedCertificateList &certs, const CertificateTrust &last_cert_trust, VerifyCertificateChainDelegate *delegate, const der::GeneralizedTime &time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, const std::set &user_initial_policy_set, InitialPolicyMappingInhibit initial_policy_mapping_inhibit, InitialAnyPolicyInhibit initial_any_policy_inhibit, std::set *user_constrained_policy_set, CertPathErrors *errors) { PathVerifier verifier; verifier.Run(certs, last_cert_trust, delegate, time, required_key_purpose, initial_explicit_policy, user_initial_policy_set, initial_policy_mapping_inhibit, initial_any_policy_inhibit, user_constrained_policy_set, errors); } bool VerifyCertificateIsSelfSigned(const ParsedCertificate &cert, SignatureVerifyCache *cache, CertErrors *errors) { if (cert.normalized_subject() != cert.normalized_issuer()) { if (errors) { errors->AddError(cert_errors::kSubjectDoesNotMatchIssuer); } return false; } // Note that we do not restrict the available algorithms when determining if // something is a self-signed cert. The signature isn't very important on a // self-signed cert so just allow any supported algorithm here, to avoid // breakage. if (!cert.signature_algorithm().has_value()) { if (errors) { errors->AddError(cert_errors::kUnacceptableSignatureAlgorithm); } return false; } if (!VerifySignedData(*cert.signature_algorithm(), cert.tbs_certificate_tlv(), cert.signature_value(), cert.tbs().spki_tlv, cache)) { if (errors) { errors->AddError(cert_errors::kVerifySignedDataFailed); } return false; } return true; } } // namespace bssl