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

Side by Side Diff: net/cert/internal/verify_certificate_chain.cc

Issue 2800993002: Add a key purpose parameter to Certificate PathBuilder. (Closed)
Patch Set: rebase 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/internal/verify_certificate_chain.h" 5 #include "net/cert/internal/verify_certificate_chain.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
11 #include "net/cert/internal/cert_error_params.h" 11 #include "net/cert/internal/cert_error_params.h"
12 #include "net/cert/internal/cert_errors.h" 12 #include "net/cert/internal/cert_errors.h"
13 #include "net/cert/internal/extended_key_usage.h"
13 #include "net/cert/internal/name_constraints.h" 14 #include "net/cert/internal/name_constraints.h"
14 #include "net/cert/internal/parse_certificate.h" 15 #include "net/cert/internal/parse_certificate.h"
15 #include "net/cert/internal/signature_algorithm.h" 16 #include "net/cert/internal/signature_algorithm.h"
16 #include "net/cert/internal/signature_policy.h" 17 #include "net/cert/internal/signature_policy.h"
17 #include "net/cert/internal/trust_store.h" 18 #include "net/cert/internal/trust_store.h"
18 #include "net/cert/internal/verify_signed_data.h" 19 #include "net/cert/internal/verify_signed_data.h"
19 #include "net/der/input.h" 20 #include "net/der/input.h"
20 #include "net/der/parser.h" 21 #include "net/der/parser.h"
21 22
22 namespace net { 23 namespace net {
(...skipping 25 matching lines...) Expand all
48 DEFINE_CERT_ERROR_ID(kMissingBasicConstraints, 49 DEFINE_CERT_ERROR_ID(kMissingBasicConstraints,
49 "Does not have Basic Constraints"); 50 "Does not have Basic Constraints");
50 DEFINE_CERT_ERROR_ID(kNotPermittedByNameConstraints, 51 DEFINE_CERT_ERROR_ID(kNotPermittedByNameConstraints,
51 "Not permitted by name constraints"); 52 "Not permitted by name constraints");
52 DEFINE_CERT_ERROR_ID(kSubjectDoesNotMatchIssuer, 53 DEFINE_CERT_ERROR_ID(kSubjectDoesNotMatchIssuer,
53 "subject does not match issuer"); 54 "subject does not match issuer");
54 DEFINE_CERT_ERROR_ID(kVerifySignedDataFailed, "VerifySignedData failed"); 55 DEFINE_CERT_ERROR_ID(kVerifySignedDataFailed, "VerifySignedData failed");
55 DEFINE_CERT_ERROR_ID(kSignatureAlgorithmsDifferentEncoding, 56 DEFINE_CERT_ERROR_ID(kSignatureAlgorithmsDifferentEncoding,
56 "Certificate.signatureAlgorithm is encoded differently " 57 "Certificate.signatureAlgorithm is encoded differently "
57 "than TBSCertificate.signature"); 58 "than TBSCertificate.signature");
59 DEFINE_CERT_ERROR_ID(kEkuLacksServerAuth,
60 "The extended key usage does not include server auth");
61 DEFINE_CERT_ERROR_ID(kEkuLacksClientAuth,
62 "The extended key usage does not include client auth");
58 63
59 bool IsHandledCriticalExtensionOid(const der::Input& oid) { 64 bool IsHandledCriticalExtensionOid(const der::Input& oid) {
60 if (oid == BasicConstraintsOid()) 65 if (oid == BasicConstraintsOid())
61 return true; 66 return true;
62 if (oid == KeyUsageOid()) 67 if (oid == KeyUsageOid())
63 return true; 68 return true;
69 if (oid == ExtKeyUsageOid())
70 return true;
64 if (oid == NameConstraintsOid()) 71 if (oid == NameConstraintsOid())
65 return true; 72 return true;
66 // TODO(eroman): SubjectAltName isn't actually used here, but rather is being 73 // TODO(eroman): SubjectAltName isn't actually used here, but rather is being
67 // checked by a higher layer. 74 // checked by a higher layer.
68 if (oid == SubjectAltNameOid()) 75 if (oid == SubjectAltNameOid())
69 return true; 76 return true;
70 77
71 // TODO(eroman): Make this more complete. 78 // TODO(eroman): Make this more complete.
72 return false; 79 return false;
73 } 80 }
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 "TBSCertificate.signature", alg2_tlv)); 165 "TBSCertificate.signature", alg2_tlv));
159 return; 166 return;
160 } 167 }
161 168
162 errors->AddError( 169 errors->AddError(
163 kSignatureAlgorithmMismatch, 170 kSignatureAlgorithmMismatch,
164 CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv, 171 CreateCertErrorParams2Der("Certificate.algorithm", alg1_tlv,
165 "TBSCertificate.signature", alg2_tlv)); 172 "TBSCertificate.signature", alg2_tlv));
166 } 173 }
167 174
175 // Verify that |cert| can be used for |required_key_purpose|.
176 void VerifyExtendedKeyUsage(const ParsedCertificate& cert,
177 KeyPurpose required_key_purpose,
178 CertErrors* errors) {
179 switch (required_key_purpose) {
180 case KeyPurpose::ANY_EKU:
181 return;
182 case KeyPurpose::SERVER_AUTH: {
183 // TODO(eroman): Is it OK for the target certificate to omit the EKU?
184 if (!cert.has_extended_key_usage())
185 return;
186
187 for (const auto& key_purpose_oid : cert.extended_key_usage()) {
188 if (key_purpose_oid == AnyEKU())
189 return;
190 if (key_purpose_oid == ServerAuth())
191 return;
192 }
193
194 errors->AddError(kEkuLacksServerAuth);
195 break;
196 }
197 case KeyPurpose::CLIENT_AUTH: {
198 // TODO(eroman): Is it OK for the target certificate to omit the EKU?
199 if (!cert.has_extended_key_usage())
200 return;
201
202 for (const auto& key_purpose_oid : cert.extended_key_usage()) {
203 if (key_purpose_oid == AnyEKU())
204 return;
205 if (key_purpose_oid == ClientAuth())
206 return;
207 }
208
209 errors->AddError(kEkuLacksClientAuth);
210 break;
211 }
212 }
213 }
214
168 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate 215 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
169 // Processing" procedure. 216 // Processing" procedure.
170 void BasicCertificateProcessing( 217 void BasicCertificateProcessing(
171 const ParsedCertificate& cert, 218 const ParsedCertificate& cert,
172 bool is_target_cert, 219 bool is_target_cert,
173 const SignaturePolicy* signature_policy, 220 const SignaturePolicy* signature_policy,
174 const der::GeneralizedTime& time, 221 const der::GeneralizedTime& time,
175 const der::Input& working_spki, 222 const der::Input& working_spki,
176 const der::Input& working_normalized_issuer_name, 223 const der::Input& working_normalized_issuer_name,
177 const std::vector<const NameConstraints*>& name_constraints_list, 224 const std::vector<const NameConstraints*>& name_constraints_list,
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
388 435
389 // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure", 436 // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure",
390 // however is implied by RFC 5280 section 4.2.1.9. 437 // however is implied by RFC 5280 section 4.2.1.9.
391 VerifyTargetCertHasConsistentCaBits(cert, errors); 438 VerifyTargetCertHasConsistentCaBits(cert, errors);
392 } 439 }
393 440
394 // Initializes the path validation algorithm given anchor constraints. This 441 // Initializes the path validation algorithm given anchor constraints. This
395 // follows the description in RFC 5937 442 // follows the description in RFC 5937
396 void ProcessTrustAnchorConstraints( 443 void ProcessTrustAnchorConstraints(
397 const TrustAnchor& trust_anchor, 444 const TrustAnchor& trust_anchor,
445 KeyPurpose required_key_purpose,
398 size_t* max_path_length_ptr, 446 size_t* max_path_length_ptr,
399 std::vector<const NameConstraints*>* name_constraints_list, 447 std::vector<const NameConstraints*>* name_constraints_list,
400 CertErrors* errors) { 448 CertErrors* errors) {
401 // In RFC 5937 the enforcement of anchor constraints is governed by the input 449 // In RFC 5937 the enforcement of anchor constraints is governed by the input
402 // enforceTrustAnchorConstraints to path validation. In our implementation 450 // enforceTrustAnchorConstraints to path validation. In our implementation
403 // this is always on, and enforcement is controlled solely by whether or not 451 // this is always on, and enforcement is controlled solely by whether or not
404 // the trust anchor specified constraints. 452 // the trust anchor specified constraints.
405 if (!trust_anchor.enforces_constraints()) 453 if (!trust_anchor.enforces_constraints())
406 return; 454 return;
407 455
408 // Anchor constraints are encoded via the attached certificate. 456 // Anchor constraints are encoded via the attached certificate.
409 const ParsedCertificate& cert = *trust_anchor.cert(); 457 const ParsedCertificate& cert = *trust_anchor.cert();
410 458
459 // This is not part of RFC 5937 nor RFC 5280, but matches the EKU handling
460 // done for intermediates (described in Web PKI's Baseline Requirements).
461 VerifyExtendedKeyUsage(cert, required_key_purpose, errors);
462
411 // The following enforcements follow from RFC 5937 (primarily section 3.2): 463 // The following enforcements follow from RFC 5937 (primarily section 3.2):
412 464
413 // Initialize name constraints initial-permitted/excluded-subtrees. 465 // Initialize name constraints initial-permitted/excluded-subtrees.
414 if (cert.has_name_constraints()) 466 if (cert.has_name_constraints())
415 name_constraints_list->push_back(&cert.name_constraints()); 467 name_constraints_list->push_back(&cert.name_constraints());
416 468
417 // TODO(eroman): Initialize user-initial-policy-set based on anchor 469 // TODO(eroman): Initialize user-initial-policy-set based on anchor
418 // constraints. 470 // constraints.
419 471
420 // TODO(eroman): Initialize inhibit any policy based on anchor constraints. 472 // TODO(eroman): Initialize inhibit any policy based on anchor constraints.
(...skipping 24 matching lines...) Expand all
445 VerifyNoUnconsumedCriticalExtensions(cert, errors); 497 VerifyNoUnconsumedCriticalExtensions(cert, errors);
446 } 498 }
447 499
448 // This implementation is structured to mimic the description of certificate 500 // This implementation is structured to mimic the description of certificate
449 // path verification given by RFC 5280 section 6.1. 501 // path verification given by RFC 5280 section 6.1.
450 void VerifyCertificateChainNoReturnValue( 502 void VerifyCertificateChainNoReturnValue(
451 const ParsedCertificateList& certs, 503 const ParsedCertificateList& certs,
452 const TrustAnchor* trust_anchor, 504 const TrustAnchor* trust_anchor,
453 const SignaturePolicy* signature_policy, 505 const SignaturePolicy* signature_policy,
454 const der::GeneralizedTime& time, 506 const der::GeneralizedTime& time,
507 KeyPurpose required_key_purpose,
455 CertPathErrors* errors) { 508 CertPathErrors* errors) {
456 DCHECK(trust_anchor); 509 DCHECK(trust_anchor);
457 DCHECK(signature_policy); 510 DCHECK(signature_policy);
458 DCHECK(errors); 511 DCHECK(errors);
459 512
460 // An empty chain is necessarily invalid. 513 // An empty chain is necessarily invalid.
461 if (certs.empty()) { 514 if (certs.empty()) {
462 errors->GetOtherErrors()->AddError(kChainIsEmpty); 515 errors->GetOtherErrors()->AddError(kChainIsEmpty);
463 return; 516 return;
464 } 517 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 // and may be reduced to the value in the path length constraint 553 // and may be reduced to the value in the path length constraint
501 // field within the basic constraints extension of a CA 554 // field within the basic constraints extension of a CA
502 // certificate. 555 // certificate.
503 size_t max_path_length = certs.size(); 556 size_t max_path_length = certs.size();
504 557
505 // Apply any trust anchor constraints per RFC 5937. 558 // Apply any trust anchor constraints per RFC 5937.
506 // 559 //
507 // TODO(eroman): Errors on the trust anchor are put into a certificate bucket 560 // TODO(eroman): Errors on the trust anchor are put into a certificate bucket
508 // GetErrorsForCert(certs.size()). This is a bit magical, and 561 // GetErrorsForCert(certs.size()). This is a bit magical, and
509 // has some integration issues. 562 // has some integration issues.
510 ProcessTrustAnchorConstraints(*trust_anchor, &max_path_length, 563 ProcessTrustAnchorConstraints(*trust_anchor, required_key_purpose,
511 &name_constraints_list, 564 &max_path_length, &name_constraints_list,
512 errors->GetErrorsForCert(certs.size())); 565 errors->GetErrorsForCert(certs.size()));
513 566
514 // Iterate over all the certificates in the reverse direction: starting from 567 // Iterate over all the certificates in the reverse direction: starting from
515 // the certificate signed by trust anchor and progressing towards the target 568 // the certificate signed by trust anchor and progressing towards the target
516 // certificate. 569 // certificate.
517 // 570 //
518 // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based. 571 // Note that |i| uses 0-based indexing whereas in RFC 5280 it is 1-based.
519 // 572 //
520 // * i=0 : Certificated signed by trust anchor. 573 // * i=0 : Certificated signed by trust anchor.
521 // * i=N-1 : Target certificate. 574 // * i=N-1 : Target certificate.
(...skipping 12 matching lines...) Expand all
534 CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs); 587 CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs);
535 588
536 // Per RFC 5280 section 6.1: 589 // Per RFC 5280 section 6.1:
537 // * Do basic processing for each certificate 590 // * Do basic processing for each certificate
538 // * If it is the last certificate in the path (target certificate) 591 // * If it is the last certificate in the path (target certificate)
539 // - Then run "Wrap up" 592 // - Then run "Wrap up"
540 // - Otherwise run "Prepare for Next cert" 593 // - Otherwise run "Prepare for Next cert"
541 BasicCertificateProcessing(cert, is_target_cert, signature_policy, time, 594 BasicCertificateProcessing(cert, is_target_cert, signature_policy, time,
542 working_spki, working_normalized_issuer_name, 595 working_spki, working_normalized_issuer_name,
543 name_constraints_list, cert_errors); 596 name_constraints_list, cert_errors);
597
598 // The key purpose is checked not just for the end-entity certificate, but
599 // also interpreted as a constraint when it appears in intermediates. This
600 // goes beyond what RFC 5280 describes, but is the de-facto standard. See
601 // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questi ons
602 VerifyExtendedKeyUsage(cert, required_key_purpose, cert_errors);
603
544 if (!is_target_cert) { 604 if (!is_target_cert) {
545 PrepareForNextCertificate(cert, &max_path_length, &working_spki, 605 PrepareForNextCertificate(cert, &max_path_length, &working_spki,
546 &working_normalized_issuer_name, 606 &working_normalized_issuer_name,
547 &name_constraints_list, cert_errors); 607 &name_constraints_list, cert_errors);
548 } else { 608 } else {
549 WrapUp(cert, cert_errors); 609 WrapUp(cert, cert_errors);
610 // TODO(eroman): Verify the Key Usage on target is consistent with
611 // key_purpose.
550 } 612 }
551 } 613 }
552 614
553 // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1: 615 // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
554 // 616 //
555 // A certificate MUST NOT appear more than once in a prospective 617 // A certificate MUST NOT appear more than once in a prospective
556 // certification path. 618 // certification path.
557 } 619 }
558 620
559 } // namespace 621 } // namespace
560 622
561 bool VerifyCertificateChain(const ParsedCertificateList& certs, 623 bool VerifyCertificateChain(const ParsedCertificateList& certs,
562 const TrustAnchor* trust_anchor, 624 const TrustAnchor* trust_anchor,
563 const SignaturePolicy* signature_policy, 625 const SignaturePolicy* signature_policy,
564 const der::GeneralizedTime& time, 626 const der::GeneralizedTime& time,
627 KeyPurpose required_key_purpose,
565 CertPathErrors* errors) { 628 CertPathErrors* errors) {
566 // TODO(eroman): This function requires that |errors| is empty upon entry, 629 // TODO(eroman): This function requires that |errors| is empty upon entry,
567 // which is not part of the API contract. 630 // which is not part of the API contract.
568 DCHECK(!errors->ContainsHighSeverityErrors()); 631 DCHECK(!errors->ContainsHighSeverityErrors());
569 VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy, 632 VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy,
570 time, errors); 633 time, required_key_purpose, errors);
571 return !errors->ContainsHighSeverityErrors(); 634 return !errors->ContainsHighSeverityErrors();
572 } 635 }
573 636
574 } // namespace net 637 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/internal/verify_certificate_chain.h ('k') | net/cert/internal/verify_certificate_chain_pkits_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698