1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/cert/cert_verify_proc_nss.h"
17 #include "base/logging.h"
18 #include "crypto/nss_util.h"
19 #include "crypto/scoped_nss_types.h"
20 #include "crypto/sha2.h"
21 #include "net/base/net_errors.h"
22 #include "net/cert/asn1_util.h"
23 #include "net/cert/cert_status_flags.h"
24 #include "net/cert/cert_verifier.h"
25 #include "net/cert/cert_verify_result.h"
26 #include "net/cert/crl_set.h"
27 #include "net/cert/ev_root_ca_metadata.h"
28 #include "net/cert/x509_certificate.h"
29 #include "net/cert/x509_util_nss.h"
32 #include <CommonCrypto/CommonDigest.h>
33 #include "net/cert/x509_util_ios.h"
34 #endif // defined(OS_IOS)
36 #if defined(USE_NSS_CERTS)
45 CERTCertificatePolicies
,
46 crypto::NSSDestroyer
<CERTCertificatePolicies
,
47 CERT_DestroyCertificatePoliciesExtension
> >
48 ScopedCERTCertificatePolicies
;
52 crypto::NSSDestroyer
<CERTCertList
, CERT_DestroyCertList
> >
55 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam
56 // array that cvout points to. cvout must be initialized as passed to
57 // CERT_PKIXVerifyCert, so that the array must be terminated with
59 // When it goes out of scope, it destroys values of cert_po_trustAnchor
60 // and cert_po_certList types, but doesn't release the array itself.
61 class ScopedCERTValOutParam
{
63 explicit ScopedCERTValOutParam(CERTValOutParam
* cvout
) : cvout_(cvout
) {}
65 ~ScopedCERTValOutParam() {
69 // Free the internal resources, but do not release the array itself.
73 for (CERTValOutParam
*p
= cvout_
; p
->type
!= cert_po_end
; p
++) {
75 case cert_po_trustAnchor
:
76 if (p
->value
.pointer
.cert
) {
77 CERT_DestroyCertificate(p
->value
.pointer
.cert
);
78 p
->value
.pointer
.cert
= NULL
;
81 case cert_po_certList
:
82 if (p
->value
.pointer
.chain
) {
83 CERT_DestroyCertList(p
->value
.pointer
.chain
);
84 p
->value
.pointer
.chain
= NULL
;
94 CERTValOutParam
* cvout_
;
96 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam
);
99 // Map PORT_GetError() return values to our network error codes.
100 int MapSecurityError(int err
) {
102 case PR_DIRECTORY_LOOKUP_ERROR
: // DNS lookup error.
103 return ERR_NAME_NOT_RESOLVED
;
104 case SEC_ERROR_INVALID_ARGS
:
105 return ERR_INVALID_ARGUMENT
;
106 case SSL_ERROR_BAD_CERT_DOMAIN
:
107 return ERR_CERT_COMMON_NAME_INVALID
;
108 case SEC_ERROR_INVALID_TIME
:
109 case SEC_ERROR_EXPIRED_CERTIFICATE
:
110 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
:
111 return ERR_CERT_DATE_INVALID
;
112 case SEC_ERROR_UNKNOWN_ISSUER
:
113 case SEC_ERROR_UNTRUSTED_ISSUER
:
114 case SEC_ERROR_CA_CERT_INVALID
:
115 case SEC_ERROR_APPLICATION_CALLBACK_ERROR
: // Rejected by
116 // chain_verify_callback.
117 return ERR_CERT_AUTHORITY_INVALID
;
118 // TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
119 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
:
120 case SEC_ERROR_OCSP_SERVER_ERROR
:
121 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION
;
122 case SEC_ERROR_REVOKED_CERTIFICATE
:
123 case SEC_ERROR_UNTRUSTED_CERT
: // Treat as revoked.
124 return ERR_CERT_REVOKED
;
125 case SEC_ERROR_CERT_NOT_IN_NAME_SPACE
:
126 return ERR_CERT_NAME_CONSTRAINT_VIOLATION
;
127 case SEC_ERROR_BAD_DER
:
128 case SEC_ERROR_BAD_SIGNATURE
:
129 case SEC_ERROR_CERT_NOT_VALID
:
130 // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
131 case SEC_ERROR_CERT_USAGES_INVALID
:
132 case SEC_ERROR_INADEQUATE_KEY_USAGE
: // Key usage.
133 case SEC_ERROR_INADEQUATE_CERT_TYPE
: // Extended key usage and whether
134 // the certificate is a CA.
135 case SEC_ERROR_POLICY_VALIDATION_FAILED
:
136 case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID
:
137 case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
:
138 case SEC_ERROR_EXTENSION_VALUE_INVALID
:
139 return ERR_CERT_INVALID
;
140 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
:
141 return ERR_CERT_WEAK_SIGNATURE_ALGORITHM
;
143 LOG(WARNING
) << "Unknown error " << err
<< " mapped to net::ERR_FAILED";
148 // Map PORT_GetError() return values to our cert status flags.
149 CertStatus
MapCertErrorToCertStatus(int err
) {
150 int net_error
= MapSecurityError(err
);
151 return MapNetErrorToCertStatus(net_error
);
154 // Saves some information about the certificate chain cert_list in
155 // *verify_result. The caller MUST initialize *verify_result before calling
157 // Note that cert_list[0] is the end entity certificate.
158 void GetCertChainInfo(CERTCertList
* cert_list
,
159 CERTCertificate
* root_cert
,
160 CertVerifyResult
* verify_result
) {
163 CERTCertificate
* verified_cert
= NULL
;
164 std::vector
<CERTCertificate
*> verified_chain
;
166 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
167 !CERT_LIST_END(node
, cert_list
);
168 node
= CERT_LIST_NEXT(node
), ++i
) {
170 verified_cert
= node
->cert
;
172 // Because of an NSS bug, CERT_PKIXVerifyCert may chain a self-signed
173 // certificate of a root CA to another certificate of the same root CA
174 // key. Detect that error and ignore the root CA certificate.
175 // See https://bugzilla.mozilla.org/show_bug.cgi?id=721288.
176 if (node
->cert
->isRoot
) {
177 // NOTE: isRoot doesn't mean the certificate is a trust anchor. It
178 // means the certificate is self-signed. Here we assume isRoot only
179 // implies the certificate is self-issued.
180 CERTCertListNode
* next_node
= CERT_LIST_NEXT(node
);
181 CERTCertificate
* next_cert
;
182 if (!CERT_LIST_END(next_node
, cert_list
)) {
183 next_cert
= next_node
->cert
;
185 next_cert
= root_cert
;
187 // Test that |node->cert| is actually a self-signed certificate
188 // whose key is equal to |next_cert|, and not a self-issued
189 // certificate signed by another key of the same CA.
190 if (next_cert
&& SECITEM_ItemsAreEqual(&node
->cert
->derPublicKey
,
191 &next_cert
->derPublicKey
)) {
195 verified_chain
.push_back(node
->cert
);
198 SECAlgorithmID
& signature
= node
->cert
->signature
;
199 SECOidTag oid_tag
= SECOID_FindOIDTag(&signature
.algorithm
);
201 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
:
202 verify_result
->has_md5
= true;
204 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION
:
205 verify_result
->has_md2
= true;
207 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION
:
208 verify_result
->has_md4
= true;
210 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
:
211 case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE
:
212 case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST
:
213 case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE
:
214 verify_result
->has_sha1
= true;
222 verified_chain
.push_back(root_cert
);
224 verify_result
->verified_cert
=
225 x509_util_ios::CreateCertFromNSSHandles(verified_cert
, verified_chain
);
227 verify_result
->verified_cert
=
228 X509Certificate::CreateFromHandle(verified_cert
, verified_chain
);
229 #endif // defined(OS_IOS)
232 // IsKnownRoot returns true if the given certificate is one that we believe
233 // is a standard (as opposed to user-installed) root.
234 bool IsKnownRoot(CERTCertificate
* root
) {
235 if (!root
|| !root
->slot
)
238 // This magic name is taken from
239 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
240 return 0 == strcmp(PK11_GetSlotName(root
->slot
),
241 "NSS Builtin Objects");
244 // Returns true if the given certificate is one of the additional trust anchors.
245 bool IsAdditionalTrustAnchor(CERTCertList
* additional_trust_anchors
,
246 CERTCertificate
* root
) {
247 if (!additional_trust_anchors
|| !root
)
249 for (CERTCertListNode
* node
= CERT_LIST_HEAD(additional_trust_anchors
);
250 !CERT_LIST_END(node
, additional_trust_anchors
);
251 node
= CERT_LIST_NEXT(node
)) {
252 if (CERT_CompareCerts(node
->cert
, root
))
264 // CheckRevocationWithCRLSet attempts to check each element of |cert_list|
265 // against |crl_set|. It returns:
266 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
267 // kCRLSetUnknown: if there is no fresh information about the leaf
268 // certificate in the chain or if the CRLSet has expired.
270 // Only the leaf certificate is considered for coverage because some
271 // intermediates have CRLs with no revocations (after filtering) and
272 // those CRLs are pruned from the CRLSet at generation time. This means
273 // that some EV sites would otherwise take the hit of an OCSP lookup for
275 // kCRLSetOk: otherwise.
276 CRLSetResult
CheckRevocationWithCRLSet(CERTCertList
* cert_list
,
277 CERTCertificate
* root
,
279 std::vector
<CERTCertificate
*> certs
;
282 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
283 !CERT_LIST_END(node
, cert_list
);
284 node
= CERT_LIST_NEXT(node
)) {
285 certs
.push_back(node
->cert
);
289 certs
.push_back(root
);
291 // error is set to true if any errors are found. It causes such chains to be
292 // considered as not covered.
294 // last_covered is set to the coverage state of the previous certificate. The
295 // certificates are iterated over backwards thus, after the iteration,
296 // |last_covered| contains the coverage state of the leaf certificate.
297 bool last_covered
= false;
299 // We iterate from the root certificate down to the leaf, keeping track of
300 // the issuer's SPKI at each step.
301 std::string issuer_spki_hash
;
302 for (std::vector
<CERTCertificate
*>::reverse_iterator i
= certs
.rbegin();
303 i
!= certs
.rend(); ++i
) {
304 CERTCertificate
* cert
= *i
;
306 base::StringPiece
der(reinterpret_cast<char*>(cert
->derCert
.data
),
309 base::StringPiece spki
;
310 if (!asn1::ExtractSPKIFromDERCert(der
, &spki
)) {
315 const std::string spki_hash
= crypto::SHA256HashString(spki
);
317 base::StringPiece serial_number
= base::StringPiece(
318 reinterpret_cast<char*>(cert
->serialNumber
.data
),
319 cert
->serialNumber
.len
);
321 CRLSet::Result result
= crl_set
->CheckSPKI(spki_hash
);
323 if (result
!= CRLSet::REVOKED
&& !issuer_spki_hash
.empty())
324 result
= crl_set
->CheckSerial(serial_number
, issuer_spki_hash
);
326 issuer_spki_hash
= spki_hash
;
329 case CRLSet::REVOKED
:
330 return kCRLSetRevoked
;
331 case CRLSet::UNKNOWN
:
332 last_covered
= false;
344 if (error
|| !last_covered
|| crl_set
->IsExpired())
345 return kCRLSetUnknown
;
349 // Forward declarations.
350 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
351 CERTCertificate
* cert_handle
, int num_policy_oids
,
352 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
353 CERTValOutParam
* cvout
);
354 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
);
356 // Call CERT_PKIXVerifyCert for the cert_handle.
357 // Verification results are stored in an array of CERTValOutParam.
358 // If |hard_fail| is true, and no policy_oids are supplied (eg: EV is NOT being
359 // checked), then the failure to obtain valid CRL/OCSP information for all
360 // certificates that contain CRL/OCSP URLs will cause the certificate to be
361 // treated as if it was revoked. Since failures may be caused by transient
362 // network failures or by malicious attackers, in general, hard_fail should be
364 // If policy_oids is not NULL and num_policy_oids is positive, policies
366 // additional_trust_anchors is an optional list of certificates that can be
367 // trusted as anchors when building a certificate chain.
368 // Caller must initialize cvout before calling this function.
369 SECStatus
PKIXVerifyCert(CERTCertificate
* cert_handle
,
370 bool check_revocation
,
372 bool cert_io_enabled
,
373 const SECOidTag
* policy_oids
,
375 CERTCertList
* additional_trust_anchors
,
376 CERTChainVerifyCallback
* chain_verify_callback
,
377 CERTValOutParam
* cvout
) {
378 bool use_crl
= check_revocation
;
379 bool use_ocsp
= check_revocation
;
381 PRUint64 revocation_method_flags
=
382 CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD
|
383 CERT_REV_M_ALLOW_NETWORK_FETCHING
|
384 CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE
|
385 CERT_REV_M_IGNORE_MISSING_FRESH_INFO
|
386 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO
;
387 PRUint64 revocation_method_independent_flags
=
388 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
;
389 if (check_revocation
&& policy_oids
&& num_policy_oids
> 0) {
390 // EV verification requires revocation checking. Consider the certificate
391 // revoked if we don't have revocation info.
392 // TODO(wtc): Add a bool parameter to expressly specify we're doing EV
393 // verification or we want strict revocation flags.
394 revocation_method_flags
|= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
;
395 revocation_method_independent_flags
|=
396 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
;
397 } else if (check_revocation
&& hard_fail
) {
398 revocation_method_flags
|= CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO
;
399 revocation_method_independent_flags
|=
400 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE
;
402 revocation_method_flags
|= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE
;
403 revocation_method_independent_flags
|=
404 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT
;
406 PRUint64 method_flags
[2];
407 method_flags
[cert_revocation_method_crl
] = revocation_method_flags
;
408 method_flags
[cert_revocation_method_ocsp
] = revocation_method_flags
;
411 method_flags
[cert_revocation_method_crl
] |=
412 CERT_REV_M_TEST_USING_THIS_METHOD
;
415 method_flags
[cert_revocation_method_ocsp
] |=
416 CERT_REV_M_TEST_USING_THIS_METHOD
;
419 CERTRevocationMethodIndex preferred_revocation_methods
[1];
421 preferred_revocation_methods
[0] = cert_revocation_method_ocsp
;
423 preferred_revocation_methods
[0] = cert_revocation_method_crl
;
426 CERTRevocationFlags revocation_flags
;
427 revocation_flags
.leafTests
.number_of_defined_methods
=
428 arraysize(method_flags
);
429 revocation_flags
.leafTests
.cert_rev_flags_per_method
= method_flags
;
430 revocation_flags
.leafTests
.number_of_preferred_methods
=
431 arraysize(preferred_revocation_methods
);
432 revocation_flags
.leafTests
.preferred_methods
= preferred_revocation_methods
;
433 revocation_flags
.leafTests
.cert_rev_method_independent_flags
=
434 revocation_method_independent_flags
;
436 revocation_flags
.chainTests
.number_of_defined_methods
=
437 arraysize(method_flags
);
438 revocation_flags
.chainTests
.cert_rev_flags_per_method
= method_flags
;
439 revocation_flags
.chainTests
.number_of_preferred_methods
=
440 arraysize(preferred_revocation_methods
);
441 revocation_flags
.chainTests
.preferred_methods
= preferred_revocation_methods
;
442 revocation_flags
.chainTests
.cert_rev_method_independent_flags
=
443 revocation_method_independent_flags
;
446 std::vector
<CERTValInParam
> cvin
;
448 CERTValInParam in_param
;
449 in_param
.type
= cert_pi_revocationFlags
;
450 in_param
.value
.pointer
.revocation
= &revocation_flags
;
451 cvin
.push_back(in_param
);
452 if (policy_oids
&& num_policy_oids
> 0) {
453 in_param
.type
= cert_pi_policyOID
;
454 in_param
.value
.arraySize
= num_policy_oids
;
455 in_param
.value
.array
.oids
= policy_oids
;
456 cvin
.push_back(in_param
);
458 if (additional_trust_anchors
) {
459 in_param
.type
= cert_pi_trustAnchors
;
460 in_param
.value
.pointer
.chain
= additional_trust_anchors
;
461 cvin
.push_back(in_param
);
462 in_param
.type
= cert_pi_useOnlyTrustAnchors
;
463 in_param
.value
.scalar
.b
= PR_FALSE
;
464 cvin
.push_back(in_param
);
466 if (chain_verify_callback
) {
467 in_param
.type
= cert_pi_chainVerifyCallback
;
468 in_param
.value
.pointer
.chainVerifyCallback
= chain_verify_callback
;
469 cvin
.push_back(in_param
);
471 in_param
.type
= cert_pi_end
;
472 cvin
.push_back(in_param
);
474 SECStatus rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
475 &cvin
[0], cvout
, NULL
);
476 if (rv
!= SECSuccess
) {
477 rv
= RetryPKIXVerifyCertWithWorkarounds(cert_handle
, num_policy_oids
,
478 cert_io_enabled
, &cvin
, cvout
);
483 // PKIXVerifyCert calls this function to work around some bugs in
484 // CERT_PKIXVerifyCert. All the arguments of this function are either the
485 // arguments or local variables of PKIXVerifyCert.
486 SECStatus
RetryPKIXVerifyCertWithWorkarounds(
487 CERTCertificate
* cert_handle
, int num_policy_oids
,
488 bool cert_io_enabled
, std::vector
<CERTValInParam
>* cvin
,
489 CERTValOutParam
* cvout
) {
490 // We call this function when the first CERT_PKIXVerifyCert call in
491 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure.
492 SECStatus rv
= SECFailure
;
493 int nss_error
= PORT_GetError();
494 CERTValInParam in_param
;
496 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
497 // CA certificate, so we retry with cert_pi_useAIACertFetch.
498 // cert_pi_useAIACertFetch has several bugs in its error handling and
499 // error reporting (NSS bug 528743), so we don't use it by default.
500 // Note: When building a certificate chain, CERT_PKIXVerifyCert may
501 // incorrectly pick a CA certificate with the same subject name as the
502 // missing intermediate CA certificate, and fail with the
503 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
504 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
505 if (cert_io_enabled
&&
506 (nss_error
== SEC_ERROR_UNKNOWN_ISSUER
||
507 nss_error
== SEC_ERROR_BAD_SIGNATURE
)) {
508 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
510 in_param
.type
= cert_pi_useAIACertFetch
;
511 in_param
.value
.scalar
.b
= PR_TRUE
;
512 cvin
->push_back(in_param
);
513 in_param
.type
= cert_pi_end
;
514 cvin
->push_back(in_param
);
515 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
516 &(*cvin
)[0], cvout
, NULL
);
517 if (rv
== SECSuccess
)
519 int new_nss_error
= PORT_GetError();
520 if (new_nss_error
== SEC_ERROR_INVALID_ARGS
||
521 new_nss_error
== SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE
||
522 new_nss_error
== SEC_ERROR_BAD_INFO_ACCESS_LOCATION
||
523 new_nss_error
== SEC_ERROR_BAD_HTTP_RESPONSE
||
524 new_nss_error
== SEC_ERROR_BAD_LDAP_RESPONSE
||
525 !IS_SEC_ERROR(new_nss_error
)) {
526 // Use the original error code because of cert_pi_useAIACertFetch's
527 // bad error reporting.
528 PORT_SetError(nss_error
);
531 nss_error
= new_nss_error
;
534 // If an intermediate CA certificate has requireExplicitPolicy in its
535 // policyConstraints extension, CERT_PKIXVerifyCert fails with
536 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
537 // certificate policy (NSS bug 552775). So we retry with the certificate
538 // policy found in the server certificate.
539 if (nss_error
== SEC_ERROR_POLICY_VALIDATION_FAILED
&&
540 num_policy_oids
== 0) {
541 SECOidTag policy
= GetFirstCertPolicy(cert_handle
);
542 if (policy
!= SEC_OID_UNKNOWN
) {
543 DCHECK_EQ(cvin
->back().type
, cert_pi_end
);
545 in_param
.type
= cert_pi_policyOID
;
546 in_param
.value
.arraySize
= 1;
547 in_param
.value
.array
.oids
= &policy
;
548 cvin
->push_back(in_param
);
549 in_param
.type
= cert_pi_end
;
550 cvin
->push_back(in_param
);
551 rv
= CERT_PKIXVerifyCert(cert_handle
, certificateUsageSSLServer
,
552 &(*cvin
)[0], cvout
, NULL
);
553 if (rv
!= SECSuccess
) {
554 // Use the original error code.
555 PORT_SetError(nss_error
);
563 // Decodes the certificatePolicies extension of the certificate. Returns
564 // NULL if the certificate doesn't have the extension or the extension can't
565 // be decoded. The returned value must be freed with a
566 // CERT_DestroyCertificatePoliciesExtension call.
567 CERTCertificatePolicies
* DecodeCertPolicies(
568 CERTCertificate
* cert_handle
) {
570 SECStatus rv
= CERT_FindCertExtension(cert_handle
,
571 SEC_OID_X509_CERTIFICATE_POLICIES
,
573 if (rv
!= SECSuccess
)
575 CERTCertificatePolicies
* policies
=
576 CERT_DecodeCertificatePoliciesExtension(&policy_ext
);
577 SECITEM_FreeItem(&policy_ext
, PR_FALSE
);
581 // Returns the OID tag for the first certificate policy in the certificate's
582 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate
583 // has no certificate policy.
584 SECOidTag
GetFirstCertPolicy(CERTCertificate
* cert_handle
) {
585 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
587 return SEC_OID_UNKNOWN
;
589 CERTPolicyInfo
* policy_info
= policies
->policyInfos
[0];
591 return SEC_OID_UNKNOWN
;
592 if (policy_info
->oid
!= SEC_OID_UNKNOWN
)
593 return policy_info
->oid
;
595 // The certificate policy is unknown to NSS. We need to create a dynamic
596 // OID tag for the policy.
598 od
.oid
.len
= policy_info
->policyID
.len
;
599 od
.oid
.data
= policy_info
->policyID
.data
;
600 od
.offset
= SEC_OID_UNKNOWN
;
601 // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
602 // default description here. The description doesn't need to be unique for
604 od
.desc
= "a certificate policy";
605 od
.mechanism
= CKM_INVALID_MECHANISM
;
606 od
.supportedExtension
= INVALID_CERT_EXTENSION
;
607 return SECOID_AddEntry(&od
);
610 HashValue
CertPublicKeyHashSHA1(CERTCertificate
* cert
) {
611 HashValue
hash(HASH_VALUE_SHA1
);
613 CC_SHA1(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
615 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA1
, hash
.data(),
616 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
617 DCHECK_EQ(SECSuccess
, rv
);
622 HashValue
CertPublicKeyHashSHA256(CERTCertificate
* cert
) {
623 HashValue
hash(HASH_VALUE_SHA256
);
625 CC_SHA256(cert
->derPublicKey
.data
, cert
->derPublicKey
.len
, hash
.data());
627 SECStatus rv
= HASH_HashBuf(HASH_AlgSHA256
, hash
.data(),
628 cert
->derPublicKey
.data
, cert
->derPublicKey
.len
);
629 DCHECK_EQ(rv
, SECSuccess
);
634 void AppendPublicKeyHashes(CERTCertList
* cert_list
,
635 CERTCertificate
* root_cert
,
636 HashValueVector
* hashes
) {
637 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
638 !CERT_LIST_END(node
, cert_list
);
639 node
= CERT_LIST_NEXT(node
)) {
640 hashes
->push_back(CertPublicKeyHashSHA1(node
->cert
));
641 hashes
->push_back(CertPublicKeyHashSHA256(node
->cert
));
644 hashes
->push_back(CertPublicKeyHashSHA1(root_cert
));
645 hashes
->push_back(CertPublicKeyHashSHA256(root_cert
));
649 // Returns true if |cert_handle| contains a policy OID that is an EV policy
650 // OID according to |metadata|, storing the resulting policy OID in
651 // |*ev_policy_oid|. A true return is not sufficient to establish that a
652 // certificate is EV, but a false return is sufficient to establish the
653 // certificate cannot be EV.
654 bool IsEVCandidate(EVRootCAMetadata
* metadata
,
655 CERTCertificate
* cert_handle
,
656 SECOidTag
* ev_policy_oid
) {
658 ScopedCERTCertificatePolicies
policies(DecodeCertPolicies(cert_handle
));
662 CERTPolicyInfo
** policy_infos
= policies
->policyInfos
;
663 while (*policy_infos
!= NULL
) {
664 CERTPolicyInfo
* policy_info
= *policy_infos
++;
665 // If the Policy OID is unknown, that implicitly means it has not been
666 // registered as an EV policy.
667 if (policy_info
->oid
== SEC_OID_UNKNOWN
)
669 if (metadata
->IsEVPolicyOID(policy_info
->oid
)) {
670 *ev_policy_oid
= policy_info
->oid
;
678 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp
679 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate.
680 // TODO(wtc): A possible optimization is that we get the trust anchor from
681 // the first PKIXVerifyCert call. We look up the EV policy for the trust
682 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV.
683 // Otherwise, we pass just that EV policy (as opposed to all the EV policies)
684 // to the second PKIXVerifyCert call.
685 bool VerifyEV(CERTCertificate
* cert_handle
,
688 bool rev_checking_enabled
,
689 EVRootCAMetadata
* metadata
,
690 SECOidTag ev_policy_oid
,
691 CERTCertList
* additional_trust_anchors
,
692 CERTChainVerifyCallback
* chain_verify_callback
) {
693 CERTValOutParam cvout
[3];
695 cvout
[cvout_index
].type
= cert_po_certList
;
696 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
697 int cvout_cert_list_index
= cvout_index
;
699 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
700 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
701 int cvout_trust_anchor_index
= cvout_index
;
703 cvout
[cvout_index
].type
= cert_po_end
;
704 ScopedCERTValOutParam
scoped_cvout(cvout
);
706 SECStatus status
= PKIXVerifyCert(
708 rev_checking_enabled
,
709 true, /* hard fail is implied in EV. */
710 flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
,
713 additional_trust_anchors
,
714 chain_verify_callback
,
716 if (status
!= SECSuccess
)
719 CERTCertificate
* root_ca
=
720 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
;
724 // This second PKIXVerifyCert call could have found a different certification
725 // path and one or more of the certificates on this new path, that weren't on
726 // the old path, might have been revoked.
728 CRLSetResult crl_set_result
= CheckRevocationWithCRLSet(
729 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
730 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
732 if (crl_set_result
== kCRLSetRevoked
)
737 SHA1HashValue fingerprint
= x509_util_ios::CalculateFingerprintNSS(root_ca
);
739 SHA1HashValue fingerprint
=
740 X509Certificate::CalculateFingerprint(root_ca
);
742 return metadata
->HasEVPolicyOID(fingerprint
, ev_policy_oid
);
745 CERTCertList
* CertificateListToCERTCertList(const CertificateList
& list
) {
746 CERTCertList
* result
= CERT_NewCertList();
747 for (size_t i
= 0; i
< list
.size(); ++i
) {
749 // X509Certificate::os_cert_handle() on iOS is a SecCertificateRef; convert
750 // it to an NSS CERTCertificate.
751 CERTCertificate
* cert
= x509_util_ios::CreateNSSCertHandleFromOSHandle(
752 list
[i
]->os_cert_handle());
754 CERTCertificate
* cert
= list
[i
]->os_cert_handle();
756 CERT_AddCertToListTail(result
, CERT_DupCertificate(cert
));
763 CertVerifyProcNSS::CertVerifyProcNSS()
764 #if defined(USE_NSS_CERTS)
765 : cache_ocsp_response_from_side_channel_(
766 reinterpret_cast<CacheOCSPResponseFromSideChannelFunction
>(
767 dlsym(RTLD_DEFAULT
, "CERT_CacheOCSPResponseFromSideChannel")))
772 CertVerifyProcNSS::~CertVerifyProcNSS() {}
774 bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
778 bool CertVerifyProcNSS::SupportsOCSPStapling() const {
779 #if defined(USE_NSS_CERTS)
780 return cache_ocsp_response_from_side_channel_
;
782 // TODO(davidben): Support OCSP stapling on iOS.
787 int CertVerifyProcNSS::VerifyInternalImpl(
788 X509Certificate
* cert
,
789 const std::string
& hostname
,
790 const std::string
& ocsp_response
,
793 const CertificateList
& additional_trust_anchors
,
794 CERTChainVerifyCallback
* chain_verify_callback
,
795 CertVerifyResult
* verify_result
) {
797 // For iOS, the entire chain must be loaded into NSS's in-memory certificate
799 x509_util_ios::NSSCertChain
scoped_chain(cert
);
800 CERTCertificate
* cert_handle
= scoped_chain
.cert_handle();
802 CERTCertificate
* cert_handle
= cert
->os_cert_handle();
803 #endif // defined(OS_IOS)
805 #if defined(USE_NSS_CERTS)
806 if (!ocsp_response
.empty() && cache_ocsp_response_from_side_channel_
) {
807 // Note: NSS uses a thread-safe global hash table, so this call will
808 // affect any concurrent verification operations on |cert| or copies of
809 // the same certificate. This is an unavoidable limitation of NSS's OCSP
811 SECItem ocsp_response_item
;
812 ocsp_response_item
.data
= reinterpret_cast<unsigned char*>(
813 const_cast<char*>(ocsp_response
.data()));
814 ocsp_response_item
.len
= ocsp_response
.size();
815 cache_ocsp_response_from_side_channel_(CERT_GetDefaultCertDB(), cert_handle
,
816 PR_Now(), &ocsp_response_item
, NULL
);
818 #endif // defined(USE_NSS_CERTS)
820 if (!cert
->VerifyNameMatch(hostname
,
821 &verify_result
->common_name_fallback_used
)) {
822 verify_result
->cert_status
|= CERT_STATUS_COMMON_NAME_INVALID
;
825 // Make sure that the cert is valid now.
826 SECCertTimeValidity validity
= CERT_CheckCertValidTimes(
827 cert_handle
, PR_Now(), PR_TRUE
);
828 if (validity
!= secCertTimeValid
)
829 verify_result
->cert_status
|= CERT_STATUS_DATE_INVALID
;
831 CERTValOutParam cvout
[3];
833 cvout
[cvout_index
].type
= cert_po_certList
;
834 cvout
[cvout_index
].value
.pointer
.chain
= NULL
;
835 int cvout_cert_list_index
= cvout_index
;
837 cvout
[cvout_index
].type
= cert_po_trustAnchor
;
838 cvout
[cvout_index
].value
.pointer
.cert
= NULL
;
839 int cvout_trust_anchor_index
= cvout_index
;
841 cvout
[cvout_index
].type
= cert_po_end
;
842 ScopedCERTValOutParam
scoped_cvout(cvout
);
844 EVRootCAMetadata
* metadata
= EVRootCAMetadata::GetInstance();
845 SECOidTag ev_policy_oid
= SEC_OID_UNKNOWN
;
846 bool is_ev_candidate
=
847 (flags
& CertVerifier::VERIFY_EV_CERT
) &&
848 IsEVCandidate(metadata
, cert_handle
, &ev_policy_oid
);
849 bool cert_io_enabled
= flags
& CertVerifier::VERIFY_CERT_IO_ENABLED
;
850 bool check_revocation
=
852 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED
);
853 if (check_revocation
)
854 verify_result
->cert_status
|= CERT_STATUS_REV_CHECKING_ENABLED
;
856 ScopedCERTCertList trust_anchors
;
857 if (!additional_trust_anchors
.empty()) {
859 CertificateListToCERTCertList(additional_trust_anchors
));
862 SECStatus status
= PKIXVerifyCert(cert_handle
,
869 chain_verify_callback
,
872 if (status
== SECSuccess
&&
873 (flags
& CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS
) &&
874 !IsKnownRoot(cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
)) {
875 // TODO(rsleevi): Optimize this by supplying the constructed chain to
876 // libpkix via cvin. Omitting for now, due to lack of coverage in upstream
877 // NSS tests for that feature.
878 scoped_cvout
.Clear();
879 verify_result
->cert_status
|= CERT_STATUS_REV_CHECKING_ENABLED
;
880 status
= PKIXVerifyCert(cert_handle
,
887 chain_verify_callback
,
891 if (status
== SECSuccess
) {
892 AppendPublicKeyHashes(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
893 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
894 &verify_result
->public_key_hashes
);
896 verify_result
->is_issued_by_known_root
=
897 IsKnownRoot(cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
);
898 verify_result
->is_issued_by_additional_trust_anchor
=
899 IsAdditionalTrustAnchor(
901 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
);
903 GetCertChainInfo(cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
904 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
908 CRLSetResult crl_set_result
= kCRLSetUnknown
;
910 crl_set_result
= CheckRevocationWithCRLSet(
911 cvout
[cvout_cert_list_index
].value
.pointer
.chain
,
912 cvout
[cvout_trust_anchor_index
].value
.pointer
.cert
,
914 if (crl_set_result
== kCRLSetRevoked
) {
915 PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE
);
920 if (status
!= SECSuccess
) {
921 int err
= PORT_GetError();
922 LOG(ERROR
) << "CERT_PKIXVerifyCert for " << hostname
923 << " failed err=" << err
;
924 // CERT_PKIXVerifyCert rerports the wrong error code for
925 // expired certificates (NSS bug 491174)
926 if (err
== SEC_ERROR_CERT_NOT_VALID
&&
927 (verify_result
->cert_status
& CERT_STATUS_DATE_INVALID
))
928 err
= SEC_ERROR_EXPIRED_CERTIFICATE
;
929 CertStatus cert_status
= MapCertErrorToCertStatus(err
);
931 verify_result
->cert_status
|= cert_status
;
932 return MapCertStatusToNetError(verify_result
->cert_status
);
934 // |err| is not a certificate error.
935 return MapSecurityError(err
);
938 if (IsCertStatusError(verify_result
->cert_status
))
939 return MapCertStatusToNetError(verify_result
->cert_status
);
941 if ((flags
& CertVerifier::VERIFY_EV_CERT
) && is_ev_candidate
) {
943 crl_set_result
!= kCRLSetOk
&&
945 (flags
& CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY
);
946 if (check_revocation
)
947 verify_result
->cert_status
|= CERT_STATUS_REV_CHECKING_ENABLED
;
949 if (VerifyEV(cert_handle
,
956 chain_verify_callback
)) {
957 verify_result
->cert_status
|= CERT_STATUS_IS_EV
;
964 int CertVerifyProcNSS::VerifyInternal(
965 X509Certificate
* cert
,
966 const std::string
& hostname
,
967 const std::string
& ocsp_response
,
970 const CertificateList
& additional_trust_anchors
,
971 CertVerifyResult
* verify_result
) {
972 return VerifyInternalImpl(cert
, hostname
, ocsp_response
, flags
, crl_set
,
973 additional_trust_anchors
,
974 NULL
, // chain_verify_callback