Bug 1641886 [wpt PR 23851] - Support interpolating contain-intrinsic-size, a=testonly
[gecko.git] / security / certverifier / CertVerifier.cpp
blob260c9d2ef760c38e00dfd6a3f0b48a7153bf5d53
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CertVerifier.h"
9 #include <stdint.h>
11 #include "CTDiversityPolicy.h"
12 #include "CTKnownLogs.h"
13 #include "CTLogVerifier.h"
14 #include "ExtendedValidation.h"
15 #include "MultiLogCTVerifier.h"
16 #include "NSSCertDBTrustDomain.h"
17 #include "NSSErrorsService.h"
18 #include "cert.h"
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Casting.h"
21 #include "mozilla/IntegerPrintfMacros.h"
22 #include "nsNSSComponent.h"
23 #include "nsPromiseFlatString.h"
24 #include "nsServiceManagerUtils.h"
25 #include "pk11pub.h"
26 #include "mozpkix/pkix.h"
27 #include "mozpkix/pkixnss.h"
28 #include "secmod.h"
30 using namespace mozilla::ct;
31 using namespace mozilla::pkix;
32 using namespace mozilla::psm;
34 mozilla::LazyLogModule gCertVerifierLog("certverifier");
36 // Returns the certificate validity period in calendar months (rounded down).
37 // "extern" to allow unit tests in CTPolicyEnforcerTest.cpp.
38 extern mozilla::pkix::Result GetCertLifetimeInFullMonths(PRTime certNotBefore,
39 PRTime certNotAfter,
40 size_t& months) {
41 if (certNotBefore >= certNotAfter) {
42 MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter");
43 return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS;
46 PRExplodedTime explodedNotBefore;
47 PRExplodedTime explodedNotAfter;
49 PR_ExplodeTime(certNotBefore, PR_LocalTimeParameters, &explodedNotBefore);
50 PR_ExplodeTime(certNotAfter, PR_LocalTimeParameters, &explodedNotAfter);
52 PRInt32 signedMonths =
53 (explodedNotAfter.tm_year - explodedNotBefore.tm_year) * 12 +
54 (explodedNotAfter.tm_month - explodedNotBefore.tm_month);
55 if (explodedNotAfter.tm_mday < explodedNotBefore.tm_mday) {
56 --signedMonths;
59 // Can't use `mozilla::AssertedCast<size_t>(signedMonths)` below
60 // since it currently generates a warning on Win x64 debug.
61 if (signedMonths < 0) {
62 MOZ_ASSERT_UNREACHABLE("Expected explodedNotBefore < explodedNotAfter");
63 return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
65 months = static_cast<size_t>(signedMonths);
67 return Success;
70 namespace mozilla {
71 namespace psm {
73 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
74 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
75 const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4;
76 static const unsigned int MIN_RSA_BITS = 2048;
77 static const unsigned int MIN_RSA_BITS_WEAK = 1024;
79 void CertificateTransparencyInfo::Reset() {
80 enabled = false;
81 verifyResult.Reset();
82 policyCompliance = CTPolicyCompliance::Unknown;
85 CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
86 mozilla::TimeDuration ocspTimeoutSoft,
87 mozilla::TimeDuration ocspTimeoutHard,
88 uint32_t certShortLifetimeInDays,
89 PinningMode pinningMode, SHA1Mode sha1Mode,
90 BRNameMatchingPolicy::Mode nameMatchingMode,
91 NetscapeStepUpPolicy netscapeStepUpPolicy,
92 CertificateTransparencyMode ctMode,
93 DistrustedCAPolicy distrustedCAPolicy,
94 CRLiteMode crliteMode,
95 const Vector<EnterpriseCert>& thirdPartyCerts)
96 : mOCSPDownloadConfig(odc),
97 mOCSPStrict(osc == ocspStrict),
98 mOCSPTimeoutSoft(ocspTimeoutSoft),
99 mOCSPTimeoutHard(ocspTimeoutHard),
100 mCertShortLifetimeInDays(certShortLifetimeInDays),
101 mPinningMode(pinningMode),
102 mSHA1Mode(sha1Mode),
103 mNameMatchingMode(nameMatchingMode),
104 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
105 mCTMode(ctMode),
106 mDistrustedCAPolicy(distrustedCAPolicy),
107 mCRLiteMode(crliteMode) {
108 LoadKnownCTLogs();
109 for (const auto& root : thirdPartyCerts) {
110 EnterpriseCert rootCopy;
111 // Best-effort. If we run out of memory, users might see untrusted issuer
112 // errors, but the browser will probably crash before then.
113 if (NS_SUCCEEDED(rootCopy.Init(root))) {
114 Unused << mThirdPartyCerts.append(std::move(rootCopy));
117 for (const auto& root : mThirdPartyCerts) {
118 Input input;
119 if (root.GetInput(input) == Success) {
120 // mThirdPartyCerts consists of roots and intermediates.
121 if (root.GetIsRoot()) {
122 // Best effort again.
123 Unused << mThirdPartyRootInputs.append(input);
124 } else {
125 Unused << mThirdPartyIntermediateInputs.append(input);
131 CertVerifier::~CertVerifier() = default;
133 Result IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain,
134 bool& result) {
135 if (!chain || CERT_LIST_EMPTY(chain)) {
136 return Result::FATAL_ERROR_LIBRARY_FAILURE;
138 CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
139 if (!rootNode) {
140 return Result::FATAL_ERROR_LIBRARY_FAILURE;
142 CERTCertificate* root = rootNode->cert;
143 if (!root) {
144 return Result::FATAL_ERROR_LIBRARY_FAILURE;
146 return IsCertBuiltInRoot(root, result);
149 Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo,
150 SECOidTag evOidPolicyTag) {
151 bool isEcdsa = dcInfo.scheme == ssl_sig_ecdsa_secp256r1_sha256 ||
152 dcInfo.scheme == ssl_sig_ecdsa_secp384r1_sha384 ||
153 dcInfo.scheme == ssl_sig_ecdsa_secp521r1_sha512;
155 // Firefox currently does not advertise any RSA schemes for use
156 // with Delegated Credentials. As a secondary (on top of NSS)
157 // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_*
158 // schemes are supported, check the modulus size and allow RSA here.
159 if (!isEcdsa) {
160 return Result::ERROR_INVALID_KEY;
163 return Result::Success;
166 // The term "builtin root" traditionally refers to a root CA certificate that
167 // has been added to the NSS trust store, because it has been approved
168 // for inclusion according to the Mozilla CA policy, and might be accepted
169 // by Mozilla applications as an issuer for certificates seen on the public web.
170 Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
171 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
172 return Result::FATAL_ERROR_LIBRARY_FAILURE;
175 result = false;
176 #ifdef DEBUG
177 nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
178 if (!component) {
179 return Result::FATAL_ERROR_LIBRARY_FAILURE;
181 nsresult rv = component->IsCertTestBuiltInRoot(cert, &result);
182 if (NS_FAILED(rv)) {
183 return Result::FATAL_ERROR_LIBRARY_FAILURE;
185 if (result) {
186 return Success;
188 #endif // DEBUG
189 AutoSECMODListReadLock lock;
190 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
191 list = list->next) {
192 for (int i = 0; i < list->module->slotCount; i++) {
193 PK11SlotInfo* slot = list->module->slots[i];
194 // We're searching for the "builtin root module", which is a module that
195 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST.
196 // We use PK11_HasRootCerts() to identify a module with that property.
197 // In the past, we exclusively used the PKCS#11 module named nssckbi,
198 // which is provided by the NSS library.
199 // Nowadays, some distributions use a replacement module, which contains
200 // the builtin roots, but which also contains additional CA certificates,
201 // such as CAs trusted in a local deployment.
202 // We want to be able to distinguish between these two categories,
203 // because a CA, which may issue certificates for the public web,
204 // is expected to comply with additional requirements.
205 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
206 // then we treat it as a "builtin root".
207 if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) {
208 CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, cert, nullptr);
209 if (handle != CK_INVALID_HANDLE &&
210 PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
211 false)) {
212 // Attribute was found, and is set to true
213 result = true;
214 break;
219 return Success;
222 static Result BuildCertChainForOneKeyUsage(
223 NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1,
224 KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku,
225 const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse,
226 /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) {
227 trustDomain.ResetAccumulatedState();
228 Result rv =
229 BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
230 ku1, eku, requiredPolicy, stapledOCSPResponse);
231 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
232 trustDomain.ResetAccumulatedState();
233 rv = BuildCertChain(trustDomain, certDER, time,
234 EndEntityOrCA::MustBeEndEntity, ku2, eku,
235 requiredPolicy, stapledOCSPResponse);
236 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
237 trustDomain.ResetAccumulatedState();
238 rv = BuildCertChain(trustDomain, certDER, time,
239 EndEntityOrCA::MustBeEndEntity, ku3, eku,
240 requiredPolicy, stapledOCSPResponse);
241 if (rv != Success) {
242 rv = Result::ERROR_INADEQUATE_KEY_USAGE;
246 if (ocspStaplingStatus) {
247 *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
249 return rv;
252 void CertVerifier::LoadKnownCTLogs() {
253 if (mCTMode == CertificateTransparencyMode::Disabled) {
254 return;
256 mCTVerifier = MakeUnique<MultiLogCTVerifier>();
257 for (const CTLogInfo& log : kCTLogList) {
258 Input publicKey;
259 Result rv = publicKey.Init(
260 BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength);
261 if (rv != Success) {
262 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
263 continue;
266 CTLogVerifier logVerifier;
267 const CTLogOperatorInfo& logOperator =
268 kCTLogOperatorList[log.operatorIndex];
269 rv = logVerifier.Init(publicKey, logOperator.id, log.status,
270 log.disqualificationTime);
271 if (rv != Success) {
272 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
273 continue;
276 mCTVerifier->AddLog(std::move(logVerifier));
278 // TBD: Initialize mCTDiversityPolicy with the CA dependency map
279 // of the known CT logs operators.
280 mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>();
283 Result CertVerifier::VerifyCertificateTransparencyPolicy(
284 NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
285 Input sctsFromTLS, Time time,
286 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
287 if (ctInfo) {
288 ctInfo->Reset();
290 if (mCTMode == CertificateTransparencyMode::Disabled) {
291 return Success;
293 if (ctInfo) {
294 ctInfo->enabled = true;
297 if (!builtChain || CERT_LIST_EMPTY(builtChain)) {
298 return Result::FATAL_ERROR_INVALID_ARGS;
301 Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
302 if (embeddedSCTs.GetLength() > 0) {
303 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
304 ("Got embedded SCT data of length %zu\n",
305 static_cast<size_t>(embeddedSCTs.GetLength())));
307 Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
308 if (sctsFromOCSP.GetLength() > 0) {
309 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
310 ("Got OCSP SCT data of length %zu\n",
311 static_cast<size_t>(sctsFromOCSP.GetLength())));
313 if (sctsFromTLS.GetLength() > 0) {
314 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
315 ("Got TLS SCT data of length %zu\n",
316 static_cast<size_t>(sctsFromTLS.GetLength())));
319 CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
320 if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) {
321 return Result::FATAL_ERROR_INVALID_ARGS;
323 CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
324 if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) {
325 // Issuer certificate is required for SCT verification.
326 // If we've arrived here, we probably have a "trust chain" with only one
327 // certificate (i.e. a self-signed end-entity that has been set as a trust
328 // anchor either by a third party modifying our trust DB or via the
329 // enterprise roots feature). If this is the case, certificate transparency
330 // information will probably not be present, and it certainly won't verify
331 // correctly. To simplify things, we return an empty CTVerifyResult and a
332 // "not enough SCTs" CTPolicyCompliance result.
333 if (ctInfo) {
334 CTVerifyResult emptyResult;
335 ctInfo->verifyResult = std::move(emptyResult);
336 ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts;
338 return Success;
341 CERTCertificate* endEntity = endEntityNode->cert;
342 CERTCertificate* issuer = issuerNode->cert;
343 if (!endEntity || !issuer) {
344 return Result::FATAL_ERROR_INVALID_ARGS;
347 if (endEntity->subjectName) {
348 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
349 ("Verifying CT Policy compliance of subject %s\n",
350 endEntity->subjectName));
353 Input endEntityDER;
354 Result rv =
355 endEntityDER.Init(endEntity->derCert.data, endEntity->derCert.len);
356 if (rv != Success) {
357 return rv;
360 Input issuerPublicKeyDER;
361 rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
362 issuer->derPublicKey.len);
363 if (rv != Success) {
364 return rv;
367 CTVerifyResult result;
368 rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER, embeddedSCTs,
369 sctsFromOCSP, sctsFromTLS, time, result);
370 if (rv != Success) {
371 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
372 ("SCT verification failed with fatal error %" PRId32 "\n",
373 static_cast<uint32_t>(rv)));
374 return rv;
377 if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
378 size_t validCount = 0;
379 size_t unknownLogCount = 0;
380 size_t disqualifiedLogCount = 0;
381 size_t invalidSignatureCount = 0;
382 size_t invalidTimestampCount = 0;
383 for (const VerifiedSCT& verifiedSct : result.verifiedScts) {
384 switch (verifiedSct.status) {
385 case VerifiedSCT::Status::Valid:
386 validCount++;
387 break;
388 case VerifiedSCT::Status::ValidFromDisqualifiedLog:
389 disqualifiedLogCount++;
390 break;
391 case VerifiedSCT::Status::UnknownLog:
392 unknownLogCount++;
393 break;
394 case VerifiedSCT::Status::InvalidSignature:
395 invalidSignatureCount++;
396 break;
397 case VerifiedSCT::Status::InvalidTimestamp:
398 invalidTimestampCount++;
399 break;
400 case VerifiedSCT::Status::None:
401 default:
402 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
405 MOZ_LOG(
406 gCertVerifierLog, LogLevel::Debug,
407 ("SCT verification result: "
408 "valid=%zu unknownLog=%zu disqualifiedLog=%zu "
409 "invalidSignature=%zu invalidTimestamp=%zu "
410 "decodingErrors=%zu\n",
411 validCount, unknownLogCount, disqualifiedLogCount,
412 invalidSignatureCount, invalidTimestampCount, result.decodingErrors));
415 PRTime notBefore;
416 PRTime notAfter;
417 if (CERT_GetCertTimes(endEntity, &notBefore, &notAfter) != SECSuccess) {
418 return Result::FATAL_ERROR_LIBRARY_FAILURE;
420 size_t lifetimeInMonths;
421 rv = GetCertLifetimeInFullMonths(notBefore, notAfter, lifetimeInMonths);
422 if (rv != Success) {
423 return rv;
426 CTLogOperatorList allOperators;
427 GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators);
429 CTLogOperatorList dependentOperators;
430 rv = mCTDiversityPolicy->GetDependentOperators(builtChain.get(), allOperators,
431 dependentOperators);
432 if (rv != Success) {
433 return rv;
436 CTPolicyEnforcer ctPolicyEnforcer;
437 CTPolicyCompliance ctPolicyCompliance;
438 ctPolicyEnforcer.CheckCompliance(result.verifiedScts, lifetimeInMonths,
439 dependentOperators, ctPolicyCompliance);
441 if (ctInfo) {
442 ctInfo->verifyResult = std::move(result);
443 ctInfo->policyCompliance = ctPolicyCompliance;
445 return Success;
448 bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode) {
449 switch (mSHA1Mode) {
450 case SHA1Mode::Forbidden:
451 return mode != SHA1Mode::Forbidden;
452 case SHA1Mode::ImportedRoot:
453 return mode != SHA1Mode::Forbidden && mode != SHA1Mode::ImportedRoot;
454 case SHA1Mode::ImportedRootOrBefore2016:
455 return mode == SHA1Mode::Allowed;
456 case SHA1Mode::Allowed:
457 return false;
458 // MSVC warns unless we explicitly handle this now-unused option.
459 case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
460 default:
461 MOZ_ASSERT(false, "unexpected SHA1Mode type");
462 return true;
466 Result CertVerifier::VerifyCert(
467 CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg,
468 const char* hostname,
469 /*out*/ UniqueCERTCertList& builtChain,
470 /*optional*/ const Flags flags,
471 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
472 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
473 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
474 /*optional*/ const OriginAttributes& originAttributes,
475 /*optional out*/ SECOidTag* evOidPolicy,
476 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
477 /*optional out*/ KeySizeStatus* keySizeStatus,
478 /*optional out*/ SHA1ModeResult* sha1ModeResult,
479 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
480 /*optional out*/ CertificateTransparencyInfo* ctInfo,
481 /*optional out*/ CRLiteTelemetryInfo* crliteInfo) {
482 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
484 MOZ_ASSERT(cert);
485 MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
486 MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
487 MOZ_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
489 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
490 return Result::FATAL_ERROR_LIBRARY_FAILURE;
492 if (NS_FAILED(CheckForSmartCardChanges())) {
493 return Result::FATAL_ERROR_LIBRARY_FAILURE;
496 if (evOidPolicy) {
497 *evOidPolicy = SEC_OID_UNKNOWN;
499 if (ocspStaplingStatus) {
500 if (usage != certificateUsageSSLServer) {
501 return Result::FATAL_ERROR_INVALID_ARGS;
503 *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
506 if (keySizeStatus) {
507 if (usage != certificateUsageSSLServer) {
508 return Result::FATAL_ERROR_INVALID_ARGS;
510 *keySizeStatus = KeySizeStatus::NeverChecked;
513 if (sha1ModeResult) {
514 if (usage != certificateUsageSSLServer) {
515 return Result::FATAL_ERROR_INVALID_ARGS;
517 *sha1ModeResult = SHA1ModeResult::NeverChecked;
520 if (!cert ||
521 (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
522 return Result::FATAL_ERROR_INVALID_ARGS;
525 Input certDER;
526 Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
527 if (rv != Success) {
528 return rv;
531 // We configure the OCSP fetching modes separately for EV and non-EV
532 // verifications.
533 NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching =
534 (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) ||
535 (flags & FLAG_LOCAL_ONLY)
536 ? NSSCertDBTrustDomain::NeverFetchOCSP
537 : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
538 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
540 Input stapledOCSPResponseInput;
541 const Input* stapledOCSPResponse = nullptr;
542 if (stapledOCSPResponseArg) {
543 rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(),
544 stapledOCSPResponseArg->Length());
545 if (rv != Success) {
546 // The stapled OCSP response was too big.
547 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
549 stapledOCSPResponse = &stapledOCSPResponseInput;
552 Input sctsFromTLSInput;
553 if (sctsFromTLS) {
554 rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length());
555 if (rv != Success && sctsFromTLSInput.GetLength() != 0) {
556 return Result::FATAL_ERROR_LIBRARY_FAILURE;
560 switch (usage) {
561 case certificateUsageSSLClient: {
562 // XXX: We don't really have a trust bit for SSL client authentication so
563 // just use trustEmail as it is the closest alternative.
564 NSSCertDBTrustDomain trustDomain(
565 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
566 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
567 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
568 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
569 mDistrustedCAPolicy, mCRLiteMode, originAttributes,
570 mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
571 extraCertificates, builtChain, nullptr, nullptr);
572 rv = BuildCertChain(
573 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
574 KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
575 CertPolicyId::anyPolicy, stapledOCSPResponse);
576 break;
579 case certificateUsageSSLServer: {
580 // TODO: When verifying a certificate in an SSL handshake, we should
581 // restrict the acceptable key usage based on the key exchange method
582 // chosen by the server.
584 // These configurations are in order of most restrictive to least
585 // restrictive. This enables us to gather telemetry on the expected
586 // results of setting the default policy to a particular configuration.
587 SHA1Mode sha1ModeConfigurations[] = {
588 SHA1Mode::Forbidden,
589 SHA1Mode::ImportedRoot,
590 SHA1Mode::ImportedRootOrBefore2016,
591 SHA1Mode::Allowed,
594 SHA1ModeResult sha1ModeResults[] = {
595 SHA1ModeResult::SucceededWithoutSHA1,
596 SHA1ModeResult::SucceededWithImportedRoot,
597 SHA1ModeResult::SucceededWithImportedRootOrSHA1Before2016,
598 SHA1ModeResult::SucceededWithSHA1,
601 size_t sha1ModeConfigurationsCount =
602 MOZ_ARRAY_LENGTH(sha1ModeConfigurations);
604 static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) ==
605 MOZ_ARRAY_LENGTH(sha1ModeResults),
606 "digestAlgorithm array lengths differ");
608 rv = Result::ERROR_UNKNOWN_ERROR;
610 // Try to validate for EV first.
611 NSSCertDBTrustDomain::OCSPFetching evOCSPFetching =
612 (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY)
613 ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
614 : NSSCertDBTrustDomain::FetchOCSPForEV;
616 CertPolicyId evPolicy;
617 SECOidTag evPolicyOidTag;
618 bool foundEVPolicy = GetFirstEVPolicy(*cert, evPolicy, evPolicyOidTag);
619 for (size_t i = 0;
620 i < sha1ModeConfigurationsCount && rv != Success && foundEVPolicy;
621 i++) {
622 // Don't attempt verification if the SHA1 mode set by preferences
623 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
624 // (To put it another way, only attempt verification if the SHA1 mode
625 // option we're on is as restrictive or more restrictive than
626 // mSHA1Mode.) This allows us to gather telemetry information while
627 // still enforcing the mode set by preferences.
628 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) {
629 continue;
632 // Because of the try-strict and fallback approach, we have to clear any
633 // previously noted telemetry information.
634 if (pinningTelemetryInfo) {
635 pinningTelemetryInfo->Reset();
637 if (crliteInfo) {
638 crliteInfo->Reset();
641 NSSCertDBTrustDomain trustDomain(
642 trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
643 mOCSPTimeoutHard, mCertShortLifetimeInDays, mPinningMode,
644 MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
645 sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
646 mDistrustedCAPolicy, mCRLiteMode, originAttributes,
647 mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
648 extraCertificates, builtChain, pinningTelemetryInfo, crliteInfo,
649 hostname);
650 rv = BuildCertChainForOneKeyUsage(
651 trustDomain, certDER, time,
652 KeyUsage::digitalSignature, // (EC)DHE
653 KeyUsage::keyEncipherment, // RSA
654 KeyUsage::keyAgreement, // (EC)DH
655 KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse,
656 ocspStaplingStatus);
657 if (rv == Success &&
658 sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) {
659 bool isBuiltInRoot = false;
660 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
661 if (rv != Success) {
662 break;
664 if (isBuiltInRoot) {
665 rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
668 if (rv == Success) {
669 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
670 ("cert is EV with status %i\n",
671 static_cast<int>(sha1ModeResults[i])));
672 if (evOidPolicy) {
673 *evOidPolicy = evPolicyOidTag;
675 if (sha1ModeResult) {
676 *sha1ModeResult = sha1ModeResults[i];
678 rv = VerifyCertificateTransparencyPolicy(
679 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
680 if (rv != Success) {
681 break;
685 if (rv == Success) {
686 break;
689 if (flags & FLAG_MUST_BE_EV) {
690 rv = Result::ERROR_POLICY_VALIDATION_FAILED;
691 break;
694 // Now try non-EV.
695 unsigned int keySizeOptions[] = {MIN_RSA_BITS, MIN_RSA_BITS_WEAK};
697 KeySizeStatus keySizeStatuses[] = {KeySizeStatus::LargeMinimumSucceeded,
698 KeySizeStatus::CompatibilityRisk};
700 static_assert(
701 MOZ_ARRAY_LENGTH(keySizeOptions) == MOZ_ARRAY_LENGTH(keySizeStatuses),
702 "keySize array lengths differ");
704 size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses);
706 for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) {
707 for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success;
708 j++) {
709 // Don't attempt verification if the SHA1 mode set by preferences
710 // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
711 // (To put it another way, only attempt verification if the SHA1 mode
712 // option we're on is as restrictive or more restrictive than
713 // mSHA1Mode.) This allows us to gather telemetry information while
714 // still enforcing the mode set by preferences.
715 if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) {
716 continue;
719 // invalidate any telemetry info relating to failed chains
720 if (pinningTelemetryInfo) {
721 pinningTelemetryInfo->Reset();
723 if (crliteInfo) {
724 crliteInfo->Reset();
727 NSSCertDBTrustDomain trustDomain(
728 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg,
729 mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
730 mPinningMode, keySizeOptions[i],
731 ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j],
732 mNetscapeStepUpPolicy, mDistrustedCAPolicy, mCRLiteMode,
733 originAttributes, mThirdPartyRootInputs,
734 mThirdPartyIntermediateInputs, extraCertificates, builtChain,
735 pinningTelemetryInfo, crliteInfo, hostname);
736 rv = BuildCertChainForOneKeyUsage(
737 trustDomain, certDER, time,
738 KeyUsage::digitalSignature, //(EC)DHE
739 KeyUsage::keyEncipherment, // RSA
740 KeyUsage::keyAgreement, //(EC)DH
741 KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy,
742 stapledOCSPResponse, ocspStaplingStatus);
743 if (rv != Success && !IsFatalError(rv) &&
744 rv != Result::ERROR_REVOKED_CERTIFICATE &&
745 trustDomain.GetIsErrorDueToDistrustedCAPolicy()) {
746 // Bug 1444440 - If there are multiple paths, at least one to a CA
747 // distrusted-by-policy, and none of them ending in a trusted root,
748 // then we might show a different error (UNKNOWN_ISSUER) than we
749 // intend, confusing users.
750 rv = Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
752 if (rv == Success &&
753 sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) {
754 bool isBuiltInRoot = false;
755 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
756 if (rv != Success) {
757 break;
759 if (isBuiltInRoot) {
760 rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
763 if (rv == Success) {
764 if (keySizeStatus) {
765 *keySizeStatus = keySizeStatuses[i];
767 if (sha1ModeResult) {
768 *sha1ModeResult = sha1ModeResults[j];
770 rv = VerifyCertificateTransparencyPolicy(
771 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
772 if (rv != Success) {
773 break;
779 if (rv == Success) {
780 break;
783 if (keySizeStatus) {
784 *keySizeStatus = KeySizeStatus::AlreadyBad;
786 // The telemetry probe CERT_CHAIN_SHA1_POLICY_STATUS gives us feedback on
787 // the result of setting a specific policy. However, we don't want noise
788 // from users who have manually set the policy to something other than the
789 // default, so we only collect for ImportedRoot (which is the default).
790 if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) {
791 *sha1ModeResult = SHA1ModeResult::Failed;
794 break;
797 case certificateUsageSSLCA: {
798 NSSCertDBTrustDomain trustDomain(
799 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
800 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
801 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
802 SHA1Mode::Allowed, mNetscapeStepUpPolicy, mDistrustedCAPolicy,
803 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
804 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
805 nullptr);
806 rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
807 KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
808 CertPolicyId::anyPolicy, stapledOCSPResponse);
809 break;
812 case certificateUsageEmailSigner: {
813 NSSCertDBTrustDomain trustDomain(
814 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
815 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
816 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
817 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
818 mDistrustedCAPolicy, mCRLiteMode, originAttributes,
819 mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
820 extraCertificates, builtChain, nullptr, nullptr);
821 rv = BuildCertChain(
822 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
823 KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
824 CertPolicyId::anyPolicy, stapledOCSPResponse);
825 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
826 rv = BuildCertChain(
827 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
828 KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection,
829 CertPolicyId::anyPolicy, stapledOCSPResponse);
831 break;
834 case certificateUsageEmailRecipient: {
835 // TODO: The higher level S/MIME processing should pass in which key
836 // usage it is trying to verify for, and base its algorithm choices
837 // based on the result of the verification(s).
838 NSSCertDBTrustDomain trustDomain(
839 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
840 mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
841 MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
842 SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
843 mDistrustedCAPolicy, mCRLiteMode, originAttributes,
844 mThirdPartyRootInputs, mThirdPartyIntermediateInputs,
845 extraCertificates, builtChain, nullptr, nullptr);
846 rv = BuildCertChain(trustDomain, certDER, time,
847 EndEntityOrCA::MustBeEndEntity,
848 KeyUsage::keyEncipherment, // RSA
849 KeyPurposeId::id_kp_emailProtection,
850 CertPolicyId::anyPolicy, stapledOCSPResponse);
851 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
852 rv = BuildCertChain(trustDomain, certDER, time,
853 EndEntityOrCA::MustBeEndEntity,
854 KeyUsage::keyAgreement, // ECDH/DH
855 KeyPurposeId::id_kp_emailProtection,
856 CertPolicyId::anyPolicy, stapledOCSPResponse);
858 break;
861 default:
862 rv = Result::FATAL_ERROR_INVALID_ARGS;
865 if (rv != Success) {
866 return rv;
869 return Success;
872 static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
873 if (!SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) {
874 return false;
877 // Check that the certificate is signed with the cert's spki.
878 SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
879 const_cast<CERTSignedData*>(&cert->signatureWrap),
880 const_cast<CERTSubjectPublicKeyInfo*>(&cert->subjectPublicKeyInfo),
881 pinarg);
882 if (rv != SECSuccess) {
883 return false;
886 return true;
889 Result CertVerifier::VerifySSLServerCert(
890 const UniqueCERTCertificate& peerCert, Time time,
891 /*optional*/ void* pinarg, const nsACString& hostname,
892 /*out*/ UniqueCERTCertList& builtChain,
893 /*optional*/ Flags flags,
894 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
895 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
896 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
897 /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo,
898 /*optional*/ const OriginAttributes& originAttributes,
899 /*optional*/ bool saveIntermediatesInPermanentDatabase,
900 /*optional out*/ SECOidTag* evOidPolicy,
901 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
902 /*optional out*/ KeySizeStatus* keySizeStatus,
903 /*optional out*/ SHA1ModeResult* sha1ModeResult,
904 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
905 /*optional out*/ CertificateTransparencyInfo* ctInfo,
906 /*optional out*/ CRLiteTelemetryInfo* crliteInfo,
907 /*optional out*/ bool* isBuiltCertChainRootBuiltInRoot) {
908 MOZ_ASSERT(peerCert);
909 // XXX: MOZ_ASSERT(pinarg);
910 MOZ_ASSERT(!hostname.IsEmpty());
912 SECOidTag evPolicyOidTag = SEC_OID_UNKNOWN;
914 if (isBuiltCertChainRootBuiltInRoot) {
915 *isBuiltCertChainRootBuiltInRoot = false;
918 if (evOidPolicy) {
919 *evOidPolicy = evPolicyOidTag;
922 if (hostname.IsEmpty()) {
923 return Result::ERROR_BAD_CERT_DOMAIN;
926 // CreateCertErrorRunnable assumes that CheckCertHostname is only called
927 // if VerifyCert succeeded.
928 Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
929 pinarg, PromiseFlatCString(hostname).get(), builtChain,
930 flags, extraCertificates, stapledOCSPResponse,
931 sctsFromTLS, originAttributes, &evPolicyOidTag,
932 ocspStaplingStatus, keySizeStatus, sha1ModeResult,
933 pinningTelemetryInfo, ctInfo, crliteInfo);
934 if (rv != Success) {
935 if (rv == Result::ERROR_UNKNOWN_ISSUER &&
936 CertIsSelfSigned(peerCert, pinarg)) {
937 // In this case we didn't find any issuer for the certificate and the
938 // certificate is self-signed.
939 return Result::ERROR_SELF_SIGNED_CERT;
941 if (rv == Result::ERROR_UNKNOWN_ISSUER) {
942 // In this case we didn't get any valid path for the cert. Let's see if
943 // the issuer is the same as the issuer for our canary probe. If yes, this
944 // connection is connecting via a misconfigured proxy.
945 // Note: The MitM canary might not be set. In this case we consider this
946 // an unknown issuer error.
947 nsCOMPtr<nsINSSComponent> component(
948 do_GetService(PSM_COMPONENT_CONTRACTID));
949 if (!component) {
950 return Result::FATAL_ERROR_LIBRARY_FAILURE;
952 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
953 // the feature is enabled.
954 nsresult rv = component->IssuerMatchesMitmCanary(peerCert->issuerName);
955 if (NS_SUCCEEDED(rv)) {
956 return Result::ERROR_MITM_DETECTED;
959 return rv;
962 if (dcInfo) {
963 rv = IsDelegatedCredentialAcceptable(*dcInfo, evPolicyOidTag);
964 if (rv != Success) {
965 return rv;
969 Input peerCertInput;
970 rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
971 if (rv != Success) {
972 return rv;
975 Input stapledOCSPResponseInput;
976 Input* responseInputPtr = nullptr;
977 if (stapledOCSPResponse) {
978 rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(),
979 stapledOCSPResponse->Length());
980 if (rv != Success) {
981 // The stapled OCSP response was too big.
982 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
984 responseInputPtr = &stapledOCSPResponseInput;
987 if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
988 rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
989 if (rv != Success) {
990 return rv;
994 Input hostnameInput;
995 rv = hostnameInput.Init(
996 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
997 hostname.Length());
998 if (rv != Success) {
999 return Result::FATAL_ERROR_INVALID_ARGS;
1001 bool isBuiltInRoot;
1002 rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
1003 if (rv != Success) {
1004 return rv;
1007 if (isBuiltCertChainRootBuiltInRoot) {
1008 *isBuiltCertChainRootBuiltInRoot = isBuiltInRoot;
1011 BRNameMatchingPolicy nameMatchingPolicy(
1012 isBuiltInRoot ? mNameMatchingMode
1013 : BRNameMatchingPolicy::Mode::DoNotEnforce);
1014 rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
1015 if (rv != Success) {
1016 // Treat malformed name information as a domain mismatch.
1017 if (rv == Result::ERROR_BAD_DER) {
1018 return Result::ERROR_BAD_CERT_DOMAIN;
1021 return rv;
1024 if (saveIntermediatesInPermanentDatabase) {
1025 SaveIntermediateCerts(builtChain);
1028 if (evOidPolicy) {
1029 *evOidPolicy = evPolicyOidTag;
1032 return Success;
1035 } // namespace psm
1036 } // namespace mozilla