Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Unified Diff: net/cert/internal/verify_certificate_chain.cc

Issue 2832703002: Allow the TrustStore interface to return matching intermediates, and identify distrusted certs. (Closed)
Patch Set: fix cert_verify_tool Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698