| Index: net/cert/internal/verify_certificate_chain.cc
|
| diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc
|
| index 9a1f6e008401812c794b9515943f2fcb8db40413..1a0010e90a4fb1e59dfa045c5ac17a128dc07ac0 100644
|
| --- a/net/cert/internal/verify_certificate_chain.cc
|
| +++ b/net/cert/internal/verify_certificate_chain.cc
|
| @@ -37,6 +37,8 @@ DEFINE_CERT_ERROR_ID(
|
| DEFINE_CERT_ERROR_ID(kInvalidOrUnsupportedSignatureAlgorithm,
|
| "Invalid or unsupported signature algorithm");
|
| DEFINE_CERT_ERROR_ID(kChainIsEmpty, "Chain is empty");
|
| +DEFINE_CERT_ERROR_ID(kChainIsLength1,
|
| + "TODO: Cannot verify a chain of length 1");
|
| DEFINE_CERT_ERROR_ID(kUnconsumedCriticalExtension,
|
| "Unconsumed critical extension");
|
| DEFINE_CERT_ERROR_ID(
|
| @@ -60,6 +62,9 @@ DEFINE_CERT_ERROR_ID(kEkuLacksServerAuth,
|
| "The extended key usage does not include server auth");
|
| DEFINE_CERT_ERROR_ID(kEkuLacksClientAuth,
|
| "The extended key usage does not include client auth");
|
| +DEFINE_CERT_ERROR_ID(kCertIsDistrusted, "Certificate is distrusted");
|
| +DEFINE_CERT_ERROR_ID(kCertIsNotTrustAnchor,
|
| + "Certificate is not a trust anchor");
|
|
|
| bool IsHandledCriticalExtensionOid(const der::Input& oid) {
|
| if (oid == BasicConstraintsOid())
|
| @@ -439,24 +444,16 @@ void WrapUp(const ParsedCertificate& cert, CertErrors* errors) {
|
| VerifyTargetCertHasConsistentCaBits(cert, errors);
|
| }
|
|
|
| -// Initializes the path validation algorithm given anchor constraints. This
|
| -// follows the description in RFC 5937
|
| -void ProcessTrustAnchorConstraints(
|
| - const TrustAnchor& trust_anchor,
|
| +// 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,
|
| size_t* max_path_length_ptr,
|
| std::vector<const NameConstraints*>* name_constraints_list,
|
| CertErrors* errors) {
|
| - // In RFC 5937 the enforcement of anchor constraints is governed by the input
|
| - // enforceTrustAnchorConstraints to path validation. In our implementation
|
| - // this is always on, and enforcement is controlled solely by whether or not
|
| - // the trust anchor specified constraints.
|
| - if (!trust_anchor.enforces_constraints())
|
| - return;
|
| -
|
| - // Anchor constraints are encoded via the attached certificate.
|
| - const ParsedCertificate& cert = *trust_anchor.cert();
|
| -
|
| // 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);
|
| @@ -498,16 +495,57 @@ void ProcessTrustAnchorConstraints(
|
| VerifyNoUnconsumedCriticalExtensions(cert, errors);
|
| }
|
|
|
| +// Initializes the path validation algorithm given anchor constraints. This
|
| +// follows the description in RFC 5937
|
| +void ProcessRootCertificate(
|
| + const ParsedCertificate& cert,
|
| + const CertificateTrust& trust,
|
| + KeyPurpose required_key_purpose,
|
| + size_t* max_path_length_ptr,
|
| + std::vector<const NameConstraints*>* name_constraints_list,
|
| + der::Input* working_spki,
|
| + der::Input* working_normalized_issuer_name,
|
| + CertErrors* errors) {
|
| + // Use the certificate's SPKI and subject when verifying the next certificate.
|
| + // Note this is initialized even in the case of untrusted roots (they already
|
| + // emit an error for the distrust).
|
| + *working_spki = cert.tbs().spki_tlv;
|
| + *working_normalized_issuer_name = cert.normalized_subject();
|
| +
|
| + switch (trust.type) {
|
| + case CertificateTrustType::UNSPECIFIED:
|
| + // Doesn't chain to a trust anchor - implicitly distrusted
|
| + errors->AddError(kCertIsNotTrustAnchor);
|
| + break;
|
| + case CertificateTrustType::DISTRUSTED:
|
| + // Chains to an actively distrusted certificate.
|
| + //
|
| + // TODO(eroman): There are not currently any verification or path building
|
| + // tests for the distrusted case.
|
| + errors->AddError(kCertIsDistrusted);
|
| + break;
|
| + case CertificateTrustType::TRUSTED_ANCHOR:
|
| + case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
|
| + // If the trust anchor has constraints, enforce them.
|
| + if (trust.type == CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS) {
|
| + ApplyTrustAnchorConstraints(cert, required_key_purpose,
|
| + max_path_length_ptr, name_constraints_list,
|
| + errors);
|
| + }
|
| + break;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| // This implementation is structured to mimic the description of certificate
|
| // path verification given by RFC 5280 section 6.1.
|
| -void VerifyCertificateChainNoReturnValue(
|
| - const ParsedCertificateList& certs,
|
| - const TrustAnchor* trust_anchor,
|
| - const SignaturePolicy* signature_policy,
|
| - const der::GeneralizedTime& time,
|
| - KeyPurpose required_key_purpose,
|
| - CertPathErrors* errors) {
|
| - DCHECK(trust_anchor);
|
| +void VerifyCertificateChain(const ParsedCertificateList& certs,
|
| + const CertificateTrust& last_cert_trust,
|
| + const SignaturePolicy* signature_policy,
|
| + const der::GeneralizedTime& time,
|
| + KeyPurpose required_key_purpose,
|
| + CertPathErrors* errors) {
|
| DCHECK(signature_policy);
|
| DCHECK(errors);
|
|
|
| @@ -517,6 +555,13 @@ void VerifyCertificateChainNoReturnValue(
|
| return;
|
| }
|
|
|
| + // TODO(eroman): Verifying a trusted leaf certificate is not currently
|
| + // permitted.
|
| + if (certs.size() == 1) {
|
| + errors->GetOtherErrors()->AddError(kChainIsLength1);
|
| + return;
|
| + }
|
| +
|
| // 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.
|
| @@ -536,15 +581,14 @@ void VerifyCertificateChainNoReturnValue(
|
| //
|
| // working_public_key: the public key used to verify the
|
| // signature of a certificate.
|
| - der::Input working_spki = trust_anchor->spki();
|
| + der::Input working_spki;
|
|
|
| // |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 =
|
| - trust_anchor->normalized_subject();
|
| + der::Input working_normalized_issuer_name;
|
|
|
| // |max_path_length| corresponds with the same named variable in RFC 5280
|
| // section 6.1.2:
|
| @@ -556,23 +600,12 @@ void VerifyCertificateChainNoReturnValue(
|
| // certificate.
|
| size_t max_path_length = certs.size();
|
|
|
| - // Apply any trust anchor constraints per RFC 5937.
|
| - //
|
| - // TODO(eroman): Errors on the trust anchor are put into a certificate bucket
|
| - // GetErrorsForCert(certs.size()). This is a bit magical, and
|
| - // has some integration issues.
|
| - ProcessTrustAnchorConstraints(*trust_anchor, required_key_purpose,
|
| - &max_path_length, &name_constraints_list,
|
| - errors->GetErrorsForCert(certs.size()));
|
| -
|
| // Iterate over all the certificates in the reverse direction: starting from
|
| - // the certificate signed by trust anchor and progressing towards the target
|
| - // certificate.
|
| - //
|
| - // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based.
|
| + // the root certificate and progressing towards the target certificate.
|
| //
|
| - // * i=0 : Certificated signed by trust anchor.
|
| - // * i=N-1 : Target certificate.
|
| + // * i=0 : Root certificate (i.e. trust anchor)
|
| + // * i=1 : Certificated signed by the root certificate
|
| + // * i=certs.size()-1 : Target certificate.
|
| for (size_t i = 0; i < certs.size(); ++i) {
|
| const size_t index_into_certs = certs.size() - i - 1;
|
|
|
| @@ -580,6 +613,7 @@ void VerifyCertificateChainNoReturnValue(
|
| // 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_root_cert = i == 0;
|
|
|
| const ParsedCertificate& cert = *certs[index_into_certs];
|
|
|
| @@ -587,6 +621,16 @@ void VerifyCertificateChainNoReturnValue(
|
| // associated with that certificate.
|
| CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs);
|
|
|
| + if (is_root_cert) {
|
| + ProcessRootCertificate(cert, last_cert_trust, required_key_purpose,
|
| + &max_path_length, &name_constraints_list,
|
| + &working_spki, &working_normalized_issuer_name,
|
| + cert_errors);
|
| +
|
| + // Don't do any other checks for root certificates.
|
| + continue;
|
| + }
|
| +
|
| // Per RFC 5280 section 6.1:
|
| // * Do basic processing for each certificate
|
| // * If it is the last certificate in the path (target certificate)
|
| @@ -617,20 +661,4 @@ void VerifyCertificateChainNoReturnValue(
|
| // certification path.
|
| }
|
|
|
| -} // namespace
|
| -
|
| -bool VerifyCertificateChain(const ParsedCertificateList& certs,
|
| - const TrustAnchor* trust_anchor,
|
| - const SignaturePolicy* signature_policy,
|
| - const der::GeneralizedTime& time,
|
| - KeyPurpose required_key_purpose,
|
| - CertPathErrors* errors) {
|
| - // TODO(eroman): This function requires that |errors| is empty upon entry,
|
| - // which is not part of the API contract.
|
| - DCHECK(!errors->ContainsHighSeverityErrors());
|
| - VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy,
|
| - time, required_key_purpose, errors);
|
| - return !errors->ContainsHighSeverityErrors();
|
| -}
|
| -
|
| } // namespace net
|
|
|