Chromium Code Reviews| 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..baff4206f796283696ff93d683c6373c1a0e8d41 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,53 @@ 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) { |
| + switch (trust.type) { |
| + case CertificateTrustType::UNSPECIFIED: |
| + // Doesn't chain to a trust anchor - implicitly distrusted |
| + errors->AddError(kCertIsNotTrustAnchor); |
| + return; |
|
mattm
2017/04/28 20:26:47
working_spki and working_normalized_issuer_name ar
eroman
2017/04/28 21:48:04
Good point.
I don't currently have tests for thos
|
| + case CertificateTrustType::DISTRUSTED: |
| + // Chains to an actively distrusted certificate. |
| + errors->AddError(kCertIsDistrusted); |
| + return; |
| + case CertificateTrustType::TRUSTED_ANCHOR: |
| + case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS: |
| + // Chains to a trust anchor - use its SPKI and subject when verifying the |
| + // next certificate. |
| + *working_spki = cert.tbs().spki_tlv; |
| + *working_normalized_issuer_name = cert.normalized_subject(); |
| + |
| + // 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); |
| + } |
| + return; |
| + } |
| +} |
| + |
| +} // 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 +551,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 +577,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 +596,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. |
| + // the root certificate and progressing towards the target certificate. |
| // |
| - // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based. |
| - // |
| - // * 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 +609,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 +617,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 +657,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 |