Bug 1838739 - Initialize result of SetAsGPUOutOfMemoryError. r=webgpu-reviewers,nical
[gecko.git] / security / certverifier / CertVerifier.cpp
blobf279560fbdcba9fd67651bb904b03afbc861181e
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 "AppTrustDomain.h"
12 #include "CTDiversityPolicy.h"
13 #include "CTKnownLogs.h"
14 #include "CTLogVerifier.h"
15 #include "ExtendedValidation.h"
16 #include "MultiLogCTVerifier.h"
17 #include "NSSCertDBTrustDomain.h"
18 #include "NSSErrorsService.h"
19 #include "cert.h"
20 #include "mozilla/Assertions.h"
21 #include "mozilla/Casting.h"
22 #include "mozilla/IntegerPrintfMacros.h"
23 #include "mozilla/Logging.h"
24 #include "nsNSSComponent.h"
25 #include "mozilla/SyncRunnable.h"
26 #include "nsPromiseFlatString.h"
27 #include "nsServiceManagerUtils.h"
28 #include "pk11pub.h"
29 #include "mozpkix/pkix.h"
30 #include "mozpkix/pkixcheck.h"
31 #include "mozpkix/pkixnss.h"
32 #include "mozpkix/pkixutil.h"
33 #include "secmod.h"
34 #include "nsNetCID.h"
36 using namespace mozilla::ct;
37 using namespace mozilla::pkix;
38 using namespace mozilla::psm;
40 mozilla::LazyLogModule gCertVerifierLog("certverifier");
42 // Returns the certificate validity period in calendar months (rounded down).
43 // "extern" to allow unit tests in CTPolicyEnforcerTest.cpp.
44 extern mozilla::pkix::Result GetCertLifetimeInFullMonths(Time certNotBefore,
45 Time certNotAfter,
46 size_t& months) {
47 if (certNotBefore >= certNotAfter) {
48 MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter");
49 return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS;
51 uint64_t notBeforeSeconds;
52 Result rv = SecondsSinceEpochFromTime(certNotBefore, &notBeforeSeconds);
53 if (rv != Success) {
54 return rv;
56 uint64_t notAfterSeconds;
57 rv = SecondsSinceEpochFromTime(certNotAfter, &notAfterSeconds);
58 if (rv != Success) {
59 return rv;
61 // PRTime is microseconds
62 PRTime notBeforePR = static_cast<PRTime>(notBeforeSeconds) * 1000000;
63 PRTime notAfterPR = static_cast<PRTime>(notAfterSeconds) * 1000000;
65 PRExplodedTime explodedNotBefore;
66 PRExplodedTime explodedNotAfter;
68 PR_ExplodeTime(notBeforePR, PR_LocalTimeParameters, &explodedNotBefore);
69 PR_ExplodeTime(notAfterPR, PR_LocalTimeParameters, &explodedNotAfter);
71 PRInt32 signedMonths =
72 (explodedNotAfter.tm_year - explodedNotBefore.tm_year) * 12 +
73 (explodedNotAfter.tm_month - explodedNotBefore.tm_month);
74 if (explodedNotAfter.tm_mday < explodedNotBefore.tm_mday) {
75 --signedMonths;
78 // Can't use `mozilla::AssertedCast<size_t>(signedMonths)` below
79 // since it currently generates a warning on Win x64 debug.
80 if (signedMonths < 0) {
81 MOZ_ASSERT_UNREACHABLE("Expected explodedNotBefore < explodedNotAfter");
82 return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
84 months = static_cast<size_t>(signedMonths);
86 return Success;
89 namespace mozilla {
90 namespace psm {
92 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
93 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
94 const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4;
95 static const unsigned int MIN_RSA_BITS = 2048;
96 static const unsigned int MIN_RSA_BITS_WEAK = 1024;
98 void CertificateTransparencyInfo::Reset() {
99 enabled = false;
100 verifyResult.Reset();
101 policyCompliance = CTPolicyCompliance::Unknown;
104 CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
105 mozilla::TimeDuration ocspTimeoutSoft,
106 mozilla::TimeDuration ocspTimeoutHard,
107 uint32_t certShortLifetimeInDays,
108 NetscapeStepUpPolicy netscapeStepUpPolicy,
109 CertificateTransparencyMode ctMode,
110 CRLiteMode crliteMode,
111 const Vector<EnterpriseCert>& thirdPartyCerts)
112 : mOCSPDownloadConfig(odc),
113 mOCSPStrict(osc == ocspStrict),
114 mOCSPTimeoutSoft(ocspTimeoutSoft),
115 mOCSPTimeoutHard(ocspTimeoutHard),
116 mCertShortLifetimeInDays(certShortLifetimeInDays),
117 mNetscapeStepUpPolicy(netscapeStepUpPolicy),
118 mCTMode(ctMode),
119 mCRLiteMode(crliteMode) {
120 LoadKnownCTLogs();
121 for (const auto& root : thirdPartyCerts) {
122 EnterpriseCert rootCopy;
123 // Best-effort. If we run out of memory, users might see untrusted issuer
124 // errors, but the browser will probably crash before then.
125 if (NS_SUCCEEDED(rootCopy.Init(root))) {
126 Unused << mThirdPartyCerts.append(std::move(rootCopy));
129 for (const auto& root : mThirdPartyCerts) {
130 Input input;
131 if (root.GetInput(input) == Success) {
132 // mThirdPartyCerts consists of roots and intermediates.
133 if (root.GetIsRoot()) {
134 // Best effort again.
135 Unused << mThirdPartyRootInputs.append(input);
136 } else {
137 Unused << mThirdPartyIntermediateInputs.append(input);
143 CertVerifier::~CertVerifier() = default;
145 Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) {
146 bool isEcdsa = dcInfo.scheme == ssl_sig_ecdsa_secp256r1_sha256 ||
147 dcInfo.scheme == ssl_sig_ecdsa_secp384r1_sha384 ||
148 dcInfo.scheme == ssl_sig_ecdsa_secp521r1_sha512;
150 // Firefox currently does not advertise any RSA schemes for use
151 // with Delegated Credentials. As a secondary (on top of NSS)
152 // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_*
153 // schemes are supported, check the modulus size and allow RSA here.
154 if (!isEcdsa) {
155 return Result::ERROR_INVALID_KEY;
158 return Result::Success;
161 // The term "builtin root" traditionally refers to a root CA certificate that
162 // has been added to the NSS trust store, because it has been approved
163 // for inclusion according to the Mozilla CA policy, and might be accepted
164 // by Mozilla applications as an issuer for certificates seen on the public web.
165 Result IsCertBuiltInRoot(Input certInput, bool& result) {
166 result = false;
168 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
169 return Result::FATAL_ERROR_LIBRARY_FAILURE;
172 #ifdef DEBUG
173 nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
174 if (!component) {
175 return Result::FATAL_ERROR_LIBRARY_FAILURE;
177 nsTArray<uint8_t> certBytes;
178 certBytes.AppendElements(certInput.UnsafeGetData(), certInput.GetLength());
179 if (NS_FAILED(component->IsCertTestBuiltInRoot(certBytes, &result))) {
180 return Result::FATAL_ERROR_LIBRARY_FAILURE;
182 if (result) {
183 return Success;
185 #endif // DEBUG
186 SECItem certItem(UnsafeMapInputToSECItem(certInput));
187 AutoSECMODListReadLock lock;
188 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
189 list = list->next) {
190 for (int i = 0; i < list->module->slotCount; i++) {
191 PK11SlotInfo* slot = list->module->slots[i];
192 // We're searching for the "builtin root module", which is a module that
193 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST.
194 // We use PK11_HasRootCerts() to identify a module with that property.
195 // In the past, we exclusively used the PKCS#11 module named nssckbi,
196 // which is provided by the NSS library.
197 // Nowadays, some distributions use a replacement module, which contains
198 // the builtin roots, but which also contains additional CA certificates,
199 // such as CAs trusted in a local deployment.
200 // We want to be able to distinguish between these two categories,
201 // because a CA, which may issue certificates for the public web,
202 // is expected to comply with additional requirements.
203 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
204 // then we treat it as a "builtin root".
205 if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
206 continue;
208 CK_OBJECT_HANDLE handle =
209 PK11_FindEncodedCertInSlot(slot, &certItem, nullptr);
210 if (handle == CK_INVALID_HANDLE) {
211 continue;
213 if (PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
214 false)) {
215 // Attribute was found, and is set to true
216 result = true;
217 break;
221 return Success;
224 static Result BuildCertChainForOneKeyUsage(
225 NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1,
226 KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku,
227 const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse,
228 /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) {
229 trustDomain.ResetAccumulatedState();
230 Result rv =
231 BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
232 ku1, eku, requiredPolicy, stapledOCSPResponse);
233 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
234 trustDomain.ResetAccumulatedState();
235 rv = BuildCertChain(trustDomain, certDER, time,
236 EndEntityOrCA::MustBeEndEntity, ku2, eku,
237 requiredPolicy, stapledOCSPResponse);
238 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
239 trustDomain.ResetAccumulatedState();
240 rv = BuildCertChain(trustDomain, certDER, time,
241 EndEntityOrCA::MustBeEndEntity, ku3, eku,
242 requiredPolicy, stapledOCSPResponse);
243 if (rv != Success) {
244 rv = Result::ERROR_INADEQUATE_KEY_USAGE;
248 if (ocspStaplingStatus) {
249 *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
251 return rv;
254 void CertVerifier::LoadKnownCTLogs() {
255 if (mCTMode == CertificateTransparencyMode::Disabled) {
256 return;
258 mCTVerifier = MakeUnique<MultiLogCTVerifier>();
259 for (const CTLogInfo& log : kCTLogList) {
260 Input publicKey;
261 Result rv = publicKey.Init(
262 BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength);
263 if (rv != Success) {
264 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
265 continue;
268 CTLogVerifier logVerifier;
269 const CTLogOperatorInfo& logOperator =
270 kCTLogOperatorList[log.operatorIndex];
271 rv = logVerifier.Init(publicKey, logOperator.id, log.status,
272 log.disqualificationTime);
273 if (rv != Success) {
274 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
275 continue;
278 mCTVerifier->AddLog(std::move(logVerifier));
280 // TBD: Initialize mCTDiversityPolicy with the CA dependency map
281 // of the known CT logs operators.
282 mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>();
285 Result CertVerifier::VerifyCertificateTransparencyPolicy(
286 NSSCertDBTrustDomain& trustDomain,
287 const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time,
288 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
289 if (ctInfo) {
290 ctInfo->Reset();
292 if (mCTMode == CertificateTransparencyMode::Disabled) {
293 return Success;
295 if (ctInfo) {
296 ctInfo->enabled = true;
299 if (builtChain.IsEmpty()) {
300 return Result::FATAL_ERROR_INVALID_ARGS;
303 Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
304 if (embeddedSCTs.GetLength() > 0) {
305 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
306 ("Got embedded SCT data of length %zu\n",
307 static_cast<size_t>(embeddedSCTs.GetLength())));
309 Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
310 if (sctsFromOCSP.GetLength() > 0) {
311 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
312 ("Got OCSP SCT data of length %zu\n",
313 static_cast<size_t>(sctsFromOCSP.GetLength())));
315 if (sctsFromTLS.GetLength() > 0) {
316 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
317 ("Got TLS SCT data of length %zu\n",
318 static_cast<size_t>(sctsFromTLS.GetLength())));
321 if (builtChain.Length() == 1) {
322 // Issuer certificate is required for SCT verification.
323 // If we've arrived here, we probably have a "trust chain" with only one
324 // certificate (i.e. a self-signed end-entity that has been set as a trust
325 // anchor either by a third party modifying our trust DB or via the
326 // enterprise roots feature). If this is the case, certificate transparency
327 // information will probably not be present, and it certainly won't verify
328 // correctly. To simplify things, we return an empty CTVerifyResult and a
329 // "not enough SCTs" CTPolicyCompliance result.
330 if (ctInfo) {
331 CTVerifyResult emptyResult;
332 ctInfo->verifyResult = std::move(emptyResult);
333 ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts;
335 return Success;
338 const nsTArray<uint8_t>& endEntityBytes = builtChain.ElementAt(0);
339 Input endEntityInput;
340 Result rv =
341 endEntityInput.Init(endEntityBytes.Elements(), endEntityBytes.Length());
342 if (rv != Success) {
343 return rv;
346 const nsTArray<uint8_t>& issuerBytes = builtChain.ElementAt(1);
347 Input issuerInput;
348 rv = issuerInput.Init(issuerBytes.Elements(), issuerBytes.Length());
349 if (rv != Success) {
350 return rv;
353 BackCert issuerBackCert(issuerInput, EndEntityOrCA::MustBeCA, nullptr);
354 rv = issuerBackCert.Init();
355 if (rv != Success) {
356 return rv;
358 Input issuerPublicKeyInput = issuerBackCert.GetSubjectPublicKeyInfo();
360 CTVerifyResult result;
361 rv = mCTVerifier->Verify(endEntityInput, issuerPublicKeyInput, embeddedSCTs,
362 sctsFromOCSP, sctsFromTLS, time, result);
363 if (rv != Success) {
364 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
365 ("SCT verification failed with fatal error %" PRId32 "\n",
366 static_cast<uint32_t>(rv)));
367 return rv;
370 if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
371 size_t validCount = 0;
372 size_t unknownLogCount = 0;
373 size_t disqualifiedLogCount = 0;
374 size_t invalidSignatureCount = 0;
375 size_t invalidTimestampCount = 0;
376 for (const VerifiedSCT& verifiedSct : result.verifiedScts) {
377 switch (verifiedSct.status) {
378 case VerifiedSCT::Status::Valid:
379 validCount++;
380 break;
381 case VerifiedSCT::Status::ValidFromDisqualifiedLog:
382 disqualifiedLogCount++;
383 break;
384 case VerifiedSCT::Status::UnknownLog:
385 unknownLogCount++;
386 break;
387 case VerifiedSCT::Status::InvalidSignature:
388 invalidSignatureCount++;
389 break;
390 case VerifiedSCT::Status::InvalidTimestamp:
391 invalidTimestampCount++;
392 break;
393 case VerifiedSCT::Status::None:
394 default:
395 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
398 MOZ_LOG(
399 gCertVerifierLog, LogLevel::Debug,
400 ("SCT verification result: "
401 "valid=%zu unknownLog=%zu disqualifiedLog=%zu "
402 "invalidSignature=%zu invalidTimestamp=%zu "
403 "decodingErrors=%zu\n",
404 validCount, unknownLogCount, disqualifiedLogCount,
405 invalidSignatureCount, invalidTimestampCount, result.decodingErrors));
408 BackCert endEntityBackCert(endEntityInput, EndEntityOrCA::MustBeEndEntity,
409 nullptr);
410 rv = endEntityBackCert.Init();
411 if (rv != Success) {
412 return rv;
414 Time notBefore(Time::uninitialized);
415 Time notAfter(Time::uninitialized);
416 rv = ParseValidity(endEntityBackCert.GetValidity(), &notBefore, &notAfter);
417 if (rv != Success) {
418 return rv;
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, 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 Result CertVerifier::VerifyCert(
449 const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage, Time time,
450 void* pinArg, const char* hostname,
451 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
452 /*optional*/ const Flags flags,
453 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
454 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
455 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
456 /*optional*/ const OriginAttributes& originAttributes,
457 /*optional out*/ EVStatus* evStatus,
458 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
459 /*optional out*/ KeySizeStatus* keySizeStatus,
460 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
461 /*optional out*/ CertificateTransparencyInfo* ctInfo,
462 /*optional out*/ bool* isBuiltChainRootBuiltInRoot,
463 /*optional out*/ bool* madeOCSPRequests) {
464 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
466 MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
467 MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
469 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
470 return Result::FATAL_ERROR_LIBRARY_FAILURE;
472 if (NS_FAILED(CheckForSmartCardChanges())) {
473 return Result::FATAL_ERROR_LIBRARY_FAILURE;
476 if (evStatus) {
477 *evStatus = EVStatus::NotEV;
479 if (ocspStaplingStatus) {
480 if (usage != certificateUsageSSLServer) {
481 return Result::FATAL_ERROR_INVALID_ARGS;
483 *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
486 if (keySizeStatus) {
487 if (usage != certificateUsageSSLServer) {
488 return Result::FATAL_ERROR_INVALID_ARGS;
490 *keySizeStatus = KeySizeStatus::NeverChecked;
493 if (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV)) {
494 return Result::FATAL_ERROR_INVALID_ARGS;
497 if (isBuiltChainRootBuiltInRoot) {
498 *isBuiltChainRootBuiltInRoot = false;
501 if (madeOCSPRequests) {
502 *madeOCSPRequests = false;
505 Input certDER;
506 Result rv = certDER.Init(certBytes.Elements(), certBytes.Length());
507 if (rv != Success) {
508 return rv;
511 // We configure the OCSP fetching modes separately for EV and non-EV
512 // verifications.
513 NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching =
514 (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) ||
515 (flags & FLAG_LOCAL_ONLY)
516 ? NSSCertDBTrustDomain::NeverFetchOCSP
517 : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
518 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
520 Input stapledOCSPResponseInput;
521 const Input* stapledOCSPResponse = nullptr;
522 if (stapledOCSPResponseArg) {
523 rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(),
524 stapledOCSPResponseArg->Length());
525 if (rv != Success) {
526 // The stapled OCSP response was too big.
527 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
529 stapledOCSPResponse = &stapledOCSPResponseInput;
532 Input sctsFromTLSInput;
533 if (sctsFromTLS) {
534 rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length());
535 if (rv != Success && sctsFromTLSInput.GetLength() != 0) {
536 return Result::FATAL_ERROR_LIBRARY_FAILURE;
540 switch (usage) {
541 case certificateUsageSSLClient: {
542 // XXX: We don't really have a trust bit for SSL client authentication so
543 // just use trustEmail as it is the closest alternative.
544 NSSCertDBTrustDomain trustDomain(
545 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
546 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
547 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
548 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
549 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
550 nullptr);
551 rv = BuildCertChain(
552 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
553 KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
554 CertPolicyId::anyPolicy, stapledOCSPResponse);
555 if (madeOCSPRequests) {
556 *madeOCSPRequests |=
557 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
559 break;
562 case certificateUsageSSLServer: {
563 // TODO: When verifying a certificate in an SSL handshake, we should
564 // restrict the acceptable key usage based on the key exchange method
565 // chosen by the server.
567 // Try to validate for EV first.
568 NSSCertDBTrustDomain::OCSPFetching evOCSPFetching =
569 (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY)
570 ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
571 : NSSCertDBTrustDomain::FetchOCSPForEV;
573 nsTArray<CertPolicyId> evPolicies;
574 GetKnownEVPolicies(certBytes, evPolicies);
575 rv = Result::ERROR_UNKNOWN_ERROR;
576 for (const auto& evPolicy : evPolicies) {
577 NSSCertDBTrustDomain trustDomain(
578 trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
579 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS,
580 ValidityCheckingMode::CheckForEV, mNetscapeStepUpPolicy,
581 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
582 mThirdPartyIntermediateInputs, extraCertificates, builtChain,
583 pinningTelemetryInfo, hostname);
584 rv = BuildCertChainForOneKeyUsage(
585 trustDomain, certDER, time,
586 KeyUsage::digitalSignature, // (EC)DHE
587 KeyUsage::keyEncipherment, // RSA
588 KeyUsage::keyAgreement, // (EC)DH
589 KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse,
590 ocspStaplingStatus);
591 if (madeOCSPRequests) {
592 *madeOCSPRequests |=
593 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
595 if (rv == Success) {
596 rv = VerifyCertificateTransparencyPolicy(
597 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
599 if (rv == Success) {
600 if (evStatus) {
601 *evStatus = EVStatus::EV;
603 if (isBuiltChainRootBuiltInRoot) {
604 *isBuiltChainRootBuiltInRoot =
605 trustDomain.GetIsBuiltChainRootBuiltInRoot();
607 break;
610 if (rv == Success) {
611 break;
613 if (flags & FLAG_MUST_BE_EV) {
614 rv = Result::ERROR_POLICY_VALIDATION_FAILED;
615 break;
618 // Now try non-EV.
619 unsigned int keySizeOptions[] = {MIN_RSA_BITS, MIN_RSA_BITS_WEAK};
621 KeySizeStatus keySizeStatuses[] = {KeySizeStatus::LargeMinimumSucceeded,
622 KeySizeStatus::CompatibilityRisk};
624 static_assert(
625 MOZ_ARRAY_LENGTH(keySizeOptions) == MOZ_ARRAY_LENGTH(keySizeStatuses),
626 "keySize array lengths differ");
628 size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses);
630 for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) {
631 // invalidate any telemetry info relating to failed chains
632 if (pinningTelemetryInfo) {
633 pinningTelemetryInfo->Reset();
636 NSSCertDBTrustDomain trustDomain(
637 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
638 mOCSPTimeoutHard, mCertShortLifetimeInDays, keySizeOptions[i],
639 ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy,
640 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
641 mThirdPartyIntermediateInputs, extraCertificates, builtChain,
642 pinningTelemetryInfo, hostname);
643 rv = BuildCertChainForOneKeyUsage(
644 trustDomain, certDER, time,
645 KeyUsage::digitalSignature, //(EC)DHE
646 KeyUsage::keyEncipherment, // RSA
647 KeyUsage::keyAgreement, //(EC)DH
648 KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy,
649 stapledOCSPResponse, ocspStaplingStatus);
650 if (madeOCSPRequests) {
651 *madeOCSPRequests |=
652 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
654 if (rv != Success && !IsFatalError(rv) &&
655 rv != Result::ERROR_REVOKED_CERTIFICATE &&
656 trustDomain.GetIsErrorDueToDistrustedCAPolicy()) {
657 // Bug 1444440 - If there are multiple paths, at least one to a CA
658 // distrusted-by-policy, and none of them ending in a trusted root,
659 // then we might show a different error (UNKNOWN_ISSUER) than we
660 // intend, confusing users.
661 rv = Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
663 if (rv == Success) {
664 rv = VerifyCertificateTransparencyPolicy(
665 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
667 if (rv == Success) {
668 if (keySizeStatus) {
669 *keySizeStatus = keySizeStatuses[i];
671 if (isBuiltChainRootBuiltInRoot) {
672 *isBuiltChainRootBuiltInRoot =
673 trustDomain.GetIsBuiltChainRootBuiltInRoot();
675 break;
679 if (rv != Success && keySizeStatus) {
680 *keySizeStatus = KeySizeStatus::AlreadyBad;
683 break;
686 case certificateUsageSSLCA: {
687 NSSCertDBTrustDomain trustDomain(
688 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
689 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
690 ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy, mCRLiteMode,
691 originAttributes, mThirdPartyRootInputs,
692 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
693 nullptr);
694 rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
695 KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
696 CertPolicyId::anyPolicy, stapledOCSPResponse);
697 if (madeOCSPRequests) {
698 *madeOCSPRequests |=
699 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
701 break;
704 case certificateUsageEmailSigner: {
705 NSSCertDBTrustDomain trustDomain(
706 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
707 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
708 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
709 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
710 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
711 nullptr);
712 rv = BuildCertChain(
713 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
714 KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
715 CertPolicyId::anyPolicy, stapledOCSPResponse);
716 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
717 rv = BuildCertChain(
718 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
719 KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection,
720 CertPolicyId::anyPolicy, stapledOCSPResponse);
722 if (madeOCSPRequests) {
723 *madeOCSPRequests |=
724 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
726 break;
729 case certificateUsageEmailRecipient: {
730 // TODO: The higher level S/MIME processing should pass in which key
731 // usage it is trying to verify for, and base its algorithm choices
732 // based on the result of the verification(s).
733 NSSCertDBTrustDomain trustDomain(
734 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
735 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
736 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
737 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
738 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
739 nullptr);
740 rv = BuildCertChain(trustDomain, certDER, time,
741 EndEntityOrCA::MustBeEndEntity,
742 KeyUsage::keyEncipherment, // RSA
743 KeyPurposeId::id_kp_emailProtection,
744 CertPolicyId::anyPolicy, stapledOCSPResponse);
745 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
746 rv = BuildCertChain(trustDomain, certDER, time,
747 EndEntityOrCA::MustBeEndEntity,
748 KeyUsage::keyAgreement, // ECDH/DH
749 KeyPurposeId::id_kp_emailProtection,
750 CertPolicyId::anyPolicy, stapledOCSPResponse);
752 if (madeOCSPRequests) {
753 *madeOCSPRequests |=
754 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
756 break;
759 default:
760 rv = Result::FATAL_ERROR_INVALID_ARGS;
763 if (rv != Success) {
764 return rv;
767 return Success;
770 static bool CertIsSelfSigned(const BackCert& backCert, void* pinarg) {
771 if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) {
772 return false;
775 nsTArray<Span<const uint8_t>> emptyCertList;
776 // AppTrustDomain is only used for its signature verification callbacks
777 // (AppTrustDomain::Verify{ECDSA,RSAPKCS1,RSAPSS}SignedData).
778 mozilla::psm::AppTrustDomain trustDomain(std::move(emptyCertList));
779 Result rv = VerifySignedData(trustDomain, backCert.GetSignedData(),
780 backCert.GetSubjectPublicKeyInfo());
781 return rv == Success;
784 static Result CheckCertHostnameHelper(Input peerCertInput,
785 const nsACString& hostname) {
786 Input hostnameInput;
787 Result rv = hostnameInput.Init(
788 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
789 hostname.Length());
790 if (rv != Success) {
791 return Result::FATAL_ERROR_INVALID_ARGS;
794 rv = CheckCertHostname(peerCertInput, hostnameInput);
795 // Treat malformed name information as a domain mismatch.
796 if (rv == Result::ERROR_BAD_DER) {
797 return Result::ERROR_BAD_CERT_DOMAIN;
799 return rv;
802 Result CertVerifier::VerifySSLServerCert(
803 const nsTArray<uint8_t>& peerCertBytes, Time time,
804 /*optional*/ void* pinarg, const nsACString& hostname,
805 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
806 /*optional*/ Flags flags,
807 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
808 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
809 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
810 /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo,
811 /*optional*/ const OriginAttributes& originAttributes,
812 /*optional out*/ EVStatus* evStatus,
813 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
814 /*optional out*/ KeySizeStatus* keySizeStatus,
815 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
816 /*optional out*/ CertificateTransparencyInfo* ctInfo,
817 /*optional out*/ bool* isBuiltChainRootBuiltInRoot,
818 /*optional out*/ bool* madeOCSPRequests) {
819 // XXX: MOZ_ASSERT(pinarg);
820 MOZ_ASSERT(!hostname.IsEmpty());
822 if (isBuiltChainRootBuiltInRoot) {
823 *isBuiltChainRootBuiltInRoot = false;
826 if (evStatus) {
827 *evStatus = EVStatus::NotEV;
830 if (hostname.IsEmpty()) {
831 return Result::ERROR_BAD_CERT_DOMAIN;
834 // CreateCertErrorRunnable assumes that CheckCertHostname is only called
835 // if VerifyCert succeeded.
836 Input peerCertInput;
837 Result rv =
838 peerCertInput.Init(peerCertBytes.Elements(), peerCertBytes.Length());
839 if (rv != Success) {
840 return rv;
842 bool isBuiltChainRootBuiltInRootLocal;
843 rv = VerifyCert(peerCertBytes, certificateUsageSSLServer, time, pinarg,
844 PromiseFlatCString(hostname).get(), builtChain, flags,
845 extraCertificates, stapledOCSPResponse, sctsFromTLS,
846 originAttributes, evStatus, ocspStaplingStatus, keySizeStatus,
847 pinningTelemetryInfo, ctInfo,
848 &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests);
849 if (rv != Success) {
850 // we don't use the certificate for path building, so this parameter doesn't
851 // matter
852 EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity;
853 BackCert peerBackCert(peerCertInput, notUsedForPaths, nullptr);
854 if (peerBackCert.Init() != Success) {
855 return rv;
857 if (rv == Result::ERROR_UNKNOWN_ISSUER &&
858 CertIsSelfSigned(peerBackCert, pinarg)) {
859 // In this case we didn't find any issuer for the certificate and the
860 // certificate is self-signed.
861 return Result::ERROR_SELF_SIGNED_CERT;
863 if (rv == Result::ERROR_UNKNOWN_ISSUER) {
864 // In this case we didn't get any valid path for the cert. Let's see if
865 // the issuer is the same as the issuer for our canary probe. If yes, this
866 // connection is connecting via a misconfigured proxy.
867 // Note: The MitM canary might not be set. In this case we consider this
868 // an unknown issuer error.
869 nsCOMPtr<nsINSSComponent> component(
870 do_GetService(PSM_COMPONENT_CONTRACTID));
871 if (!component) {
872 return Result::FATAL_ERROR_LIBRARY_FAILURE;
874 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
875 // the feature is enabled.
876 Input issuerNameInput = peerBackCert.GetIssuer();
877 SECItem issuerNameItem = UnsafeMapInputToSECItem(issuerNameInput);
878 UniquePORTString issuerName(CERT_DerNameToAscii(&issuerNameItem));
879 if (!issuerName) {
880 return Result::ERROR_BAD_DER;
882 nsresult rv = component->IssuerMatchesMitmCanary(issuerName.get());
883 if (NS_SUCCEEDED(rv)) {
884 return Result::ERROR_MITM_DETECTED;
887 // If the certificate is expired or not yet valid, first check whether or
888 // not it is valid for the indicated hostname, because that would be a more
889 // serious error.
890 if (rv == Result::ERROR_EXPIRED_CERTIFICATE ||
891 rv == Result::ERROR_NOT_YET_VALID_CERTIFICATE ||
892 rv == Result::ERROR_INVALID_DER_TIME) {
893 Result hostnameResult = CheckCertHostnameHelper(peerCertInput, hostname);
894 if (hostnameResult != Success) {
895 return hostnameResult;
898 return rv;
901 if (dcInfo) {
902 rv = IsDelegatedCredentialAcceptable(*dcInfo);
903 if (rv != Success) {
904 return rv;
908 Input stapledOCSPResponseInput;
909 Input* responseInputPtr = nullptr;
910 if (stapledOCSPResponse) {
911 rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(),
912 stapledOCSPResponse->Length());
913 if (rv != Success) {
914 // The stapled OCSP response was too big.
915 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
917 responseInputPtr = &stapledOCSPResponseInput;
920 if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
921 rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
922 if (rv != Success) {
923 return rv;
927 rv = CheckCertHostnameHelper(peerCertInput, hostname);
928 if (rv != Success) {
929 return rv;
932 if (isBuiltChainRootBuiltInRoot) {
933 *isBuiltChainRootBuiltInRoot = isBuiltChainRootBuiltInRootLocal;
936 return Success;
939 } // namespace psm
940 } // namespace mozilla