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

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

Issue 2800993002: Add a key purpose parameter to Certificate PathBuilder. (Closed)
Patch Set: More cast comments 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::KEY_PURPOSE_ANY:
181 return;
182 case KeyPurpose::KEY_PURPOSE_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 }
196 case KeyPurpose::KEY_PURPOSE_CLIENT_AUTH: {
197 // TODO(eroman): Is it OK for the target certificate to omit the EKU?
198 if (!cert.has_extended_key_usage())
199 return;
200
201 for (const auto& key_purpose_oid : cert.extended_key_usage()) {
202 if (key_purpose_oid == AnyEKU())
203 return;
204 if (key_purpose_oid == ClientAuth())
205 return;
206 }
207
208 errors->AddError(kEkuLacksClientAuth);
209 }
210 }
211 }
212
168 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate 213 // This function corresponds to RFC 5280 section 6.1.3's "Basic Certificate
169 // Processing" procedure. 214 // Processing" procedure.
170 void BasicCertificateProcessing( 215 void BasicCertificateProcessing(
171 const ParsedCertificate& cert, 216 const ParsedCertificate& cert,
172 bool is_target_cert, 217 bool is_target_cert,
173 const SignaturePolicy* signature_policy, 218 const SignaturePolicy* signature_policy,
174 const der::GeneralizedTime& time, 219 const der::GeneralizedTime& time,
175 const der::Input& working_spki, 220 const der::Input& working_spki,
176 const der::Input& working_normalized_issuer_name, 221 const der::Input& working_normalized_issuer_name,
177 const std::vector<const NameConstraints*>& name_constraints_list, 222 const std::vector<const NameConstraints*>& name_constraints_list,
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 // follows the description in RFC 5937 440 // follows the description in RFC 5937
396 void ProcessTrustAnchorConstraints( 441 void ProcessTrustAnchorConstraints(
397 const TrustAnchor& trust_anchor, 442 const TrustAnchor& trust_anchor,
398 size_t* max_path_length_ptr, 443 size_t* max_path_length_ptr,
399 std::vector<const NameConstraints*>* name_constraints_list, 444 std::vector<const NameConstraints*>* name_constraints_list,
400 CertErrors* errors) { 445 CertErrors* errors) {
401 // In RFC 5937 the enforcement of anchor constraints is governed by the input 446 // In RFC 5937 the enforcement of anchor constraints is governed by the input
402 // enforceTrustAnchorConstraints to path validation. In our implementation 447 // enforceTrustAnchorConstraints to path validation. In our implementation
403 // this is always on, and enforcement is controlled solely by whether or not 448 // this is always on, and enforcement is controlled solely by whether or not
404 // the trust anchor specified constraints. 449 // the trust anchor specified constraints.
405 if (!trust_anchor.enforces_constraints()) 450 if (!trust_anchor.enforces_constraints())
mattm 2017/04/06 22:16:03 Should it be checked on the trust anchor if it has
eroman 2017/04/07 00:39:40 Good point! Yes it should. I will update for this.
eroman 2017/04/07 22:13:08 Done.
406 return; 451 return;
407 452
408 // Anchor constraints are encoded via the attached certificate. 453 // Anchor constraints are encoded via the attached certificate.
409 const ParsedCertificate& cert = *trust_anchor.cert(); 454 const ParsedCertificate& cert = *trust_anchor.cert();
410 455
411 // The following enforcements follow from RFC 5937 (primarily section 3.2): 456 // The following enforcements follow from RFC 5937 (primarily section 3.2):
412 457
413 // Initialize name constraints initial-permitted/excluded-subtrees. 458 // Initialize name constraints initial-permitted/excluded-subtrees.
414 if (cert.has_name_constraints()) 459 if (cert.has_name_constraints())
415 name_constraints_list->push_back(&cert.name_constraints()); 460 name_constraints_list->push_back(&cert.name_constraints());
(...skipping 29 matching lines...) Expand all
445 VerifyNoUnconsumedCriticalExtensions(cert, errors); 490 VerifyNoUnconsumedCriticalExtensions(cert, errors);
446 } 491 }
447 492
448 // This implementation is structured to mimic the description of certificate 493 // This implementation is structured to mimic the description of certificate
449 // path verification given by RFC 5280 section 6.1. 494 // path verification given by RFC 5280 section 6.1.
450 void VerifyCertificateChainNoReturnValue( 495 void VerifyCertificateChainNoReturnValue(
451 const ParsedCertificateList& certs, 496 const ParsedCertificateList& certs,
452 const TrustAnchor* trust_anchor, 497 const TrustAnchor* trust_anchor,
453 const SignaturePolicy* signature_policy, 498 const SignaturePolicy* signature_policy,
454 const der::GeneralizedTime& time, 499 const der::GeneralizedTime& time,
500 KeyPurpose required_key_purpose,
455 CertPathErrors* errors) { 501 CertPathErrors* errors) {
456 DCHECK(trust_anchor); 502 DCHECK(trust_anchor);
457 DCHECK(signature_policy); 503 DCHECK(signature_policy);
458 DCHECK(errors); 504 DCHECK(errors);
459 505
460 // An empty chain is necessarily invalid. 506 // An empty chain is necessarily invalid.
461 if (certs.empty()) { 507 if (certs.empty()) {
462 errors->GetOtherErrors()->AddError(kChainIsEmpty); 508 errors->GetOtherErrors()->AddError(kChainIsEmpty);
463 return; 509 return;
464 } 510 }
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
534 CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs); 580 CertErrors* cert_errors = errors->GetErrorsForCert(index_into_certs);
535 581
536 // Per RFC 5280 section 6.1: 582 // Per RFC 5280 section 6.1:
537 // * Do basic processing for each certificate 583 // * Do basic processing for each certificate
538 // * If it is the last certificate in the path (target certificate) 584 // * If it is the last certificate in the path (target certificate)
539 // - Then run "Wrap up" 585 // - Then run "Wrap up"
540 // - Otherwise run "Prepare for Next cert" 586 // - Otherwise run "Prepare for Next cert"
541 BasicCertificateProcessing(cert, is_target_cert, signature_policy, time, 587 BasicCertificateProcessing(cert, is_target_cert, signature_policy, time,
542 working_spki, working_normalized_issuer_name, 588 working_spki, working_normalized_issuer_name,
543 name_constraints_list, cert_errors); 589 name_constraints_list, cert_errors);
590
591 // The key purpose is checked not just for the end-entity certificate, but
592 // also interpreted as a constraint when it appears in intermediates. This
593 // goes beyond what RFC 5280 describes, but is the de-facto standard. See
594 // https://wiki.mozilla.org/CA:CertificatePolicyV2.1#Frequently_Asked_Questi ons
595 VerifyExtendedKeyUsage(cert, required_key_purpose, cert_errors);
596
544 if (!is_target_cert) { 597 if (!is_target_cert) {
545 PrepareForNextCertificate(cert, &max_path_length, &working_spki, 598 PrepareForNextCertificate(cert, &max_path_length, &working_spki,
546 &working_normalized_issuer_name, 599 &working_normalized_issuer_name,
547 &name_constraints_list, cert_errors); 600 &name_constraints_list, cert_errors);
548 } else { 601 } else {
549 WrapUp(cert, cert_errors); 602 WrapUp(cert, cert_errors);
603 // TODO(eroman): Verify the Key Usage on target is consistent with
604 // key_purpose.
550 } 605 }
551 } 606 }
552 607
553 // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1: 608 // TODO(eroman): RFC 5280 forbids duplicate certificates per section 6.1:
554 // 609 //
555 // A certificate MUST NOT appear more than once in a prospective 610 // A certificate MUST NOT appear more than once in a prospective
556 // certification path. 611 // certification path.
557 } 612 }
558 613
559 } // namespace 614 } // namespace
560 615
561 bool VerifyCertificateChain(const ParsedCertificateList& certs, 616 bool VerifyCertificateChain(const ParsedCertificateList& certs,
562 const TrustAnchor* trust_anchor, 617 const TrustAnchor* trust_anchor,
563 const SignaturePolicy* signature_policy, 618 const SignaturePolicy* signature_policy,
564 const der::GeneralizedTime& time, 619 const der::GeneralizedTime& time,
620 KeyPurpose required_key_purpose,
565 CertPathErrors* errors) { 621 CertPathErrors* errors) {
566 // TODO(eroman): This function requires that |errors| is empty upon entry, 622 // TODO(eroman): This function requires that |errors| is empty upon entry,
567 // which is not part of the API contract. 623 // which is not part of the API contract.
568 DCHECK(!errors->ContainsHighSeverityErrors()); 624 DCHECK(!errors->ContainsHighSeverityErrors());
569 VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy, 625 VerifyCertificateChainNoReturnValue(certs, trust_anchor, signature_policy,
570 time, errors); 626 time, required_key_purpose, errors);
571 return !errors->ContainsHighSeverityErrors(); 627 return !errors->ContainsHighSeverityErrors();
572 } 628 }
573 629
574 } // namespace net 630 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698