Bug 1883912: Enable Intl.ListFormat test for "unit" style. r=spidermonkey-reviewers...
[gecko.git] / security / certverifier / CertVerifier.cpp
blobf349bdc52674ebba408c31a9f0cb734329283e0d
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 nsTArray<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 mThirdPartyCerts = thirdPartyCerts.Clone();
122 for (const auto& root : mThirdPartyCerts) {
123 Input input;
124 if (root.GetInput(input) == Success) {
125 // mThirdPartyCerts consists of roots and intermediates.
126 if (root.GetIsRoot()) {
127 mThirdPartyRootInputs.AppendElement(input);
128 } else {
129 mThirdPartyIntermediateInputs.AppendElement(input);
135 CertVerifier::~CertVerifier() = default;
137 Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) {
138 bool isEcdsa = dcInfo.scheme == ssl_sig_ecdsa_secp256r1_sha256 ||
139 dcInfo.scheme == ssl_sig_ecdsa_secp384r1_sha384 ||
140 dcInfo.scheme == ssl_sig_ecdsa_secp521r1_sha512;
142 // Firefox currently does not advertise any RSA schemes for use
143 // with Delegated Credentials. As a secondary (on top of NSS)
144 // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_*
145 // schemes are supported, check the modulus size and allow RSA here.
146 if (!isEcdsa) {
147 return Result::ERROR_INVALID_KEY;
150 return Result::Success;
153 // The term "builtin root" traditionally refers to a root CA certificate that
154 // has been added to the NSS trust store, because it has been approved
155 // for inclusion according to the Mozilla CA policy, and might be accepted
156 // by Mozilla applications as an issuer for certificates seen on the public web.
157 Result IsCertBuiltInRoot(Input certInput, bool& result) {
158 result = false;
160 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
161 return Result::FATAL_ERROR_LIBRARY_FAILURE;
164 #ifdef DEBUG
165 nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
166 if (!component) {
167 return Result::FATAL_ERROR_LIBRARY_FAILURE;
169 nsTArray<uint8_t> certBytes;
170 certBytes.AppendElements(certInput.UnsafeGetData(), certInput.GetLength());
171 if (NS_FAILED(component->IsCertTestBuiltInRoot(certBytes, &result))) {
172 return Result::FATAL_ERROR_LIBRARY_FAILURE;
174 if (result) {
175 return Success;
177 #endif // DEBUG
178 SECItem certItem(UnsafeMapInputToSECItem(certInput));
179 AutoSECMODListReadLock lock;
180 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
181 list = list->next) {
182 for (int i = 0; i < list->module->slotCount; i++) {
183 PK11SlotInfo* slot = list->module->slots[i];
184 // We're searching for the "builtin root module", which is a module that
185 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST.
186 // We use PK11_HasRootCerts() to identify a module with that property.
187 // In the past, we exclusively used the PKCS#11 module named nssckbi,
188 // which is provided by the NSS library.
189 // Nowadays, some distributions use a replacement module, which contains
190 // the builtin roots, but which also contains additional CA certificates,
191 // such as CAs trusted in a local deployment.
192 // We want to be able to distinguish between these two categories,
193 // because a CA, which may issue certificates for the public web,
194 // is expected to comply with additional requirements.
195 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true,
196 // then we treat it as a "builtin root".
197 if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
198 continue;
200 CK_OBJECT_HANDLE handle =
201 PK11_FindEncodedCertInSlot(slot, &certItem, nullptr);
202 if (handle == CK_INVALID_HANDLE) {
203 continue;
205 if (PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
206 false)) {
207 // Attribute was found, and is set to true
208 result = true;
209 break;
213 return Success;
216 static Result BuildCertChainForOneKeyUsage(
217 NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1,
218 KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku,
219 const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse,
220 /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) {
221 trustDomain.ResetAccumulatedState();
222 Result rv =
223 BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
224 ku1, eku, requiredPolicy, stapledOCSPResponse);
225 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
226 trustDomain.ResetAccumulatedState();
227 rv = BuildCertChain(trustDomain, certDER, time,
228 EndEntityOrCA::MustBeEndEntity, ku2, eku,
229 requiredPolicy, stapledOCSPResponse);
230 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
231 trustDomain.ResetAccumulatedState();
232 rv = BuildCertChain(trustDomain, certDER, time,
233 EndEntityOrCA::MustBeEndEntity, ku3, eku,
234 requiredPolicy, stapledOCSPResponse);
235 if (rv != Success) {
236 rv = Result::ERROR_INADEQUATE_KEY_USAGE;
240 if (ocspStaplingStatus) {
241 *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
243 return rv;
246 void CertVerifier::LoadKnownCTLogs() {
247 if (mCTMode == CertificateTransparencyMode::Disabled) {
248 return;
250 mCTVerifier = MakeUnique<MultiLogCTVerifier>();
251 for (const CTLogInfo& log : kCTLogList) {
252 Input publicKey;
253 Result rv = publicKey.Init(
254 BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength);
255 if (rv != Success) {
256 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
257 continue;
260 CTLogVerifier logVerifier;
261 const CTLogOperatorInfo& logOperator =
262 kCTLogOperatorList[log.operatorIndex];
263 rv = logVerifier.Init(publicKey, logOperator.id, log.status,
264 log.disqualificationTime);
265 if (rv != Success) {
266 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
267 continue;
270 mCTVerifier->AddLog(std::move(logVerifier));
272 // TBD: Initialize mCTDiversityPolicy with the CA dependency map
273 // of the known CT logs operators.
274 mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>();
277 Result CertVerifier::VerifyCertificateTransparencyPolicy(
278 NSSCertDBTrustDomain& trustDomain,
279 const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time,
280 /*optional out*/ CertificateTransparencyInfo* ctInfo) {
281 if (ctInfo) {
282 ctInfo->Reset();
284 if (mCTMode == CertificateTransparencyMode::Disabled) {
285 return Success;
287 if (ctInfo) {
288 ctInfo->enabled = true;
291 if (builtChain.IsEmpty()) {
292 return Result::FATAL_ERROR_INVALID_ARGS;
295 Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
296 if (embeddedSCTs.GetLength() > 0) {
297 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
298 ("Got embedded SCT data of length %zu\n",
299 static_cast<size_t>(embeddedSCTs.GetLength())));
301 Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
302 if (sctsFromOCSP.GetLength() > 0) {
303 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
304 ("Got OCSP SCT data of length %zu\n",
305 static_cast<size_t>(sctsFromOCSP.GetLength())));
307 if (sctsFromTLS.GetLength() > 0) {
308 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
309 ("Got TLS SCT data of length %zu\n",
310 static_cast<size_t>(sctsFromTLS.GetLength())));
313 if (builtChain.Length() == 1) {
314 // Issuer certificate is required for SCT verification.
315 // If we've arrived here, we probably have a "trust chain" with only one
316 // certificate (i.e. a self-signed end-entity that has been set as a trust
317 // anchor either by a third party modifying our trust DB or via the
318 // enterprise roots feature). If this is the case, certificate transparency
319 // information will probably not be present, and it certainly won't verify
320 // correctly. To simplify things, we return an empty CTVerifyResult and a
321 // "not enough SCTs" CTPolicyCompliance result.
322 if (ctInfo) {
323 CTVerifyResult emptyResult;
324 ctInfo->verifyResult = std::move(emptyResult);
325 ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts;
327 return Success;
330 const nsTArray<uint8_t>& endEntityBytes = builtChain.ElementAt(0);
331 Input endEntityInput;
332 Result rv =
333 endEntityInput.Init(endEntityBytes.Elements(), endEntityBytes.Length());
334 if (rv != Success) {
335 return rv;
338 const nsTArray<uint8_t>& issuerBytes = builtChain.ElementAt(1);
339 Input issuerInput;
340 rv = issuerInput.Init(issuerBytes.Elements(), issuerBytes.Length());
341 if (rv != Success) {
342 return rv;
345 BackCert issuerBackCert(issuerInput, EndEntityOrCA::MustBeCA, nullptr);
346 rv = issuerBackCert.Init();
347 if (rv != Success) {
348 return rv;
350 Input issuerPublicKeyInput = issuerBackCert.GetSubjectPublicKeyInfo();
352 CTVerifyResult result;
353 rv = mCTVerifier->Verify(endEntityInput, issuerPublicKeyInput, embeddedSCTs,
354 sctsFromOCSP, sctsFromTLS, time, result);
355 if (rv != Success) {
356 MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
357 ("SCT verification failed with fatal error %" PRId32 "\n",
358 static_cast<uint32_t>(rv)));
359 return rv;
362 if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
363 size_t validCount = 0;
364 size_t unknownLogCount = 0;
365 size_t disqualifiedLogCount = 0;
366 size_t invalidSignatureCount = 0;
367 size_t invalidTimestampCount = 0;
368 for (const VerifiedSCT& verifiedSct : result.verifiedScts) {
369 switch (verifiedSct.status) {
370 case VerifiedSCT::Status::Valid:
371 validCount++;
372 break;
373 case VerifiedSCT::Status::ValidFromDisqualifiedLog:
374 disqualifiedLogCount++;
375 break;
376 case VerifiedSCT::Status::UnknownLog:
377 unknownLogCount++;
378 break;
379 case VerifiedSCT::Status::InvalidSignature:
380 invalidSignatureCount++;
381 break;
382 case VerifiedSCT::Status::InvalidTimestamp:
383 invalidTimestampCount++;
384 break;
385 case VerifiedSCT::Status::None:
386 default:
387 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
390 MOZ_LOG(
391 gCertVerifierLog, LogLevel::Debug,
392 ("SCT verification result: "
393 "valid=%zu unknownLog=%zu disqualifiedLog=%zu "
394 "invalidSignature=%zu invalidTimestamp=%zu "
395 "decodingErrors=%zu\n",
396 validCount, unknownLogCount, disqualifiedLogCount,
397 invalidSignatureCount, invalidTimestampCount, result.decodingErrors));
400 BackCert endEntityBackCert(endEntityInput, EndEntityOrCA::MustBeEndEntity,
401 nullptr);
402 rv = endEntityBackCert.Init();
403 if (rv != Success) {
404 return rv;
406 Time notBefore(Time::uninitialized);
407 Time notAfter(Time::uninitialized);
408 rv = ParseValidity(endEntityBackCert.GetValidity(), &notBefore, &notAfter);
409 if (rv != Success) {
410 return rv;
412 size_t lifetimeInMonths;
413 rv = GetCertLifetimeInFullMonths(notBefore, notAfter, lifetimeInMonths);
414 if (rv != Success) {
415 return rv;
418 CTLogOperatorList allOperators;
419 GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators);
421 CTLogOperatorList dependentOperators;
422 rv = mCTDiversityPolicy->GetDependentOperators(builtChain, allOperators,
423 dependentOperators);
424 if (rv != Success) {
425 return rv;
428 CTPolicyEnforcer ctPolicyEnforcer;
429 CTPolicyCompliance ctPolicyCompliance;
430 ctPolicyEnforcer.CheckCompliance(result.verifiedScts, lifetimeInMonths,
431 dependentOperators, ctPolicyCompliance);
433 if (ctInfo) {
434 ctInfo->verifyResult = std::move(result);
435 ctInfo->policyCompliance = ctPolicyCompliance;
437 return Success;
440 Result CertVerifier::VerifyCert(
441 const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage, Time time,
442 void* pinArg, const char* hostname,
443 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
444 /*optional*/ const Flags flags,
445 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
446 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg,
447 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
448 /*optional*/ const OriginAttributes& originAttributes,
449 /*optional out*/ EVStatus* evStatus,
450 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
451 /*optional out*/ KeySizeStatus* keySizeStatus,
452 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
453 /*optional out*/ CertificateTransparencyInfo* ctInfo,
454 /*optional out*/ bool* isBuiltChainRootBuiltInRoot,
455 /*optional out*/ bool* madeOCSPRequests,
456 /*optional out*/ IssuerSources* issuerSources) {
457 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
459 MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
460 MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
462 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
463 return Result::FATAL_ERROR_LIBRARY_FAILURE;
465 if (NS_FAILED(CheckForSmartCardChanges())) {
466 return Result::FATAL_ERROR_LIBRARY_FAILURE;
469 if (evStatus) {
470 *evStatus = EVStatus::NotEV;
472 if (ocspStaplingStatus) {
473 if (usage != certificateUsageSSLServer) {
474 return Result::FATAL_ERROR_INVALID_ARGS;
476 *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED;
479 if (keySizeStatus) {
480 if (usage != certificateUsageSSLServer) {
481 return Result::FATAL_ERROR_INVALID_ARGS;
483 *keySizeStatus = KeySizeStatus::NeverChecked;
486 if (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV)) {
487 return Result::FATAL_ERROR_INVALID_ARGS;
490 if (isBuiltChainRootBuiltInRoot) {
491 *isBuiltChainRootBuiltInRoot = false;
494 if (madeOCSPRequests) {
495 *madeOCSPRequests = false;
498 if (issuerSources) {
499 issuerSources->clear();
502 Input certDER;
503 Result rv = certDER.Init(certBytes.Elements(), certBytes.Length());
504 if (rv != Success) {
505 return rv;
508 // We configure the OCSP fetching modes separately for EV and non-EV
509 // verifications.
510 NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching =
511 (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) ||
512 (flags & FLAG_LOCAL_ONLY)
513 ? NSSCertDBTrustDomain::NeverFetchOCSP
514 : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
515 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
517 Input stapledOCSPResponseInput;
518 const Input* stapledOCSPResponse = nullptr;
519 if (stapledOCSPResponseArg) {
520 rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(),
521 stapledOCSPResponseArg->Length());
522 if (rv != Success) {
523 // The stapled OCSP response was too big.
524 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
526 stapledOCSPResponse = &stapledOCSPResponseInput;
529 Input sctsFromTLSInput;
530 if (sctsFromTLS) {
531 rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length());
532 if (rv != Success && sctsFromTLSInput.GetLength() != 0) {
533 return Result::FATAL_ERROR_LIBRARY_FAILURE;
537 switch (usage) {
538 case certificateUsageSSLClient: {
539 // XXX: We don't really have a trust bit for SSL client authentication so
540 // just use trustEmail as it is the closest alternative.
541 NSSCertDBTrustDomain trustDomain(
542 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
543 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
544 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
545 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
546 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
547 nullptr);
548 rv = BuildCertChain(
549 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
550 KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
551 CertPolicyId::anyPolicy, stapledOCSPResponse);
552 if (madeOCSPRequests) {
553 *madeOCSPRequests |=
554 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
556 break;
559 case certificateUsageSSLServer: {
560 // TODO: When verifying a certificate in an SSL handshake, we should
561 // restrict the acceptable key usage based on the key exchange method
562 // chosen by the server.
564 // Try to validate for EV first.
565 NSSCertDBTrustDomain::OCSPFetching evOCSPFetching =
566 (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY)
567 ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
568 : NSSCertDBTrustDomain::FetchOCSPForEV;
570 nsTArray<CertPolicyId> evPolicies;
571 GetKnownEVPolicies(certBytes, evPolicies);
572 rv = Result::ERROR_UNKNOWN_ERROR;
573 for (const auto& evPolicy : evPolicies) {
574 NSSCertDBTrustDomain trustDomain(
575 trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
576 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS,
577 ValidityCheckingMode::CheckForEV, mNetscapeStepUpPolicy,
578 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
579 mThirdPartyIntermediateInputs, extraCertificates, builtChain,
580 pinningTelemetryInfo, hostname);
581 rv = BuildCertChainForOneKeyUsage(
582 trustDomain, certDER, time,
583 KeyUsage::digitalSignature, // (EC)DHE
584 KeyUsage::keyEncipherment, // RSA
585 KeyUsage::keyAgreement, // (EC)DH
586 KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse,
587 ocspStaplingStatus);
588 if (madeOCSPRequests) {
589 *madeOCSPRequests |=
590 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
592 if (issuerSources) {
593 *issuerSources = trustDomain.GetIssuerSources();
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 (issuerSources) {
655 *issuerSources = trustDomain.GetIssuerSources();
657 if (rv != Success && !IsFatalError(rv) &&
658 rv != Result::ERROR_REVOKED_CERTIFICATE &&
659 trustDomain.GetIsErrorDueToDistrustedCAPolicy()) {
660 // Bug 1444440 - If there are multiple paths, at least one to a CA
661 // distrusted-by-policy, and none of them ending in a trusted root,
662 // then we might show a different error (UNKNOWN_ISSUER) than we
663 // intend, confusing users.
664 rv = Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
666 if (rv == Success) {
667 rv = VerifyCertificateTransparencyPolicy(
668 trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
670 if (rv == Success) {
671 if (keySizeStatus) {
672 *keySizeStatus = keySizeStatuses[i];
674 if (isBuiltChainRootBuiltInRoot) {
675 *isBuiltChainRootBuiltInRoot =
676 trustDomain.GetIsBuiltChainRootBuiltInRoot();
678 break;
682 if (rv != Success && keySizeStatus) {
683 *keySizeStatus = KeySizeStatus::AlreadyBad;
686 break;
689 case certificateUsageSSLCA: {
690 NSSCertDBTrustDomain trustDomain(
691 trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
692 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
693 ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy, mCRLiteMode,
694 originAttributes, mThirdPartyRootInputs,
695 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
696 nullptr);
697 rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
698 KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
699 CertPolicyId::anyPolicy, stapledOCSPResponse);
700 if (madeOCSPRequests) {
701 *madeOCSPRequests |=
702 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
704 break;
707 case certificateUsageEmailSigner: {
708 NSSCertDBTrustDomain trustDomain(
709 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
710 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
711 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
712 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
713 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
714 nullptr);
715 rv = BuildCertChain(
716 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
717 KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
718 CertPolicyId::anyPolicy, stapledOCSPResponse);
719 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
720 rv = BuildCertChain(
721 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
722 KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection,
723 CertPolicyId::anyPolicy, stapledOCSPResponse);
725 if (madeOCSPRequests) {
726 *madeOCSPRequests |=
727 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
729 break;
732 case certificateUsageEmailRecipient: {
733 // TODO: The higher level S/MIME processing should pass in which key
734 // usage it is trying to verify for, and base its algorithm choices
735 // based on the result of the verification(s).
736 NSSCertDBTrustDomain trustDomain(
737 trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
738 mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK,
739 ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch,
740 mCRLiteMode, originAttributes, mThirdPartyRootInputs,
741 mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
742 nullptr);
743 rv = BuildCertChain(trustDomain, certDER, time,
744 EndEntityOrCA::MustBeEndEntity,
745 KeyUsage::keyEncipherment, // RSA
746 KeyPurposeId::id_kp_emailProtection,
747 CertPolicyId::anyPolicy, stapledOCSPResponse);
748 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
749 rv = BuildCertChain(trustDomain, certDER, time,
750 EndEntityOrCA::MustBeEndEntity,
751 KeyUsage::keyAgreement, // ECDH/DH
752 KeyPurposeId::id_kp_emailProtection,
753 CertPolicyId::anyPolicy, stapledOCSPResponse);
755 if (madeOCSPRequests) {
756 *madeOCSPRequests |=
757 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched;
759 break;
762 default:
763 rv = Result::FATAL_ERROR_INVALID_ARGS;
766 if (rv != Success) {
767 return rv;
770 return Success;
773 static bool CertIsSelfSigned(const BackCert& backCert, void* pinarg) {
774 if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) {
775 return false;
778 nsTArray<Span<const uint8_t>> emptyCertList;
779 // AppTrustDomain is only used for its signature verification callbacks
780 // (AppTrustDomain::Verify{ECDSA,RSAPKCS1,RSAPSS}SignedData).
781 mozilla::psm::AppTrustDomain trustDomain(std::move(emptyCertList));
782 Result rv = VerifySignedData(trustDomain, backCert.GetSignedData(),
783 backCert.GetSubjectPublicKeyInfo());
784 return rv == Success;
787 class SkipInvalidSANsForNonBuiltInRootsPolicy : public NameMatchingPolicy {
788 public:
789 explicit SkipInvalidSANsForNonBuiltInRootsPolicy(bool rootIsBuiltIn)
790 : mRootIsBuiltIn(rootIsBuiltIn) {}
792 virtual Result FallBackToCommonName(
793 Time,
794 /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override {
795 fallBackToCommonName = FallBackToSearchWithinSubject::No;
796 return Success;
799 virtual HandleInvalidSubjectAlternativeNamesBy
800 HandleInvalidSubjectAlternativeNames() override {
801 return mRootIsBuiltIn ? HandleInvalidSubjectAlternativeNamesBy::Halting
802 : HandleInvalidSubjectAlternativeNamesBy::Skipping;
805 private:
806 bool mRootIsBuiltIn;
809 static Result CheckCertHostnameHelper(Input peerCertInput,
810 const nsACString& hostname,
811 bool rootIsBuiltIn) {
812 Input hostnameInput;
813 Result rv = hostnameInput.Init(
814 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
815 hostname.Length());
816 if (rv != Success) {
817 return Result::FATAL_ERROR_INVALID_ARGS;
820 SkipInvalidSANsForNonBuiltInRootsPolicy nameMatchingPolicy(rootIsBuiltIn);
821 rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
822 // Treat malformed name information as a domain mismatch.
823 if (rv == Result::ERROR_BAD_DER) {
824 return Result::ERROR_BAD_CERT_DOMAIN;
826 return rv;
829 Result CertVerifier::VerifySSLServerCert(
830 const nsTArray<uint8_t>& peerCertBytes, Time time,
831 /*optional*/ void* pinarg, const nsACString& hostname,
832 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain,
833 /*optional*/ Flags flags,
834 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
835 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse,
836 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS,
837 /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo,
838 /*optional*/ const OriginAttributes& originAttributes,
839 /*optional out*/ EVStatus* evStatus,
840 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
841 /*optional out*/ KeySizeStatus* keySizeStatus,
842 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
843 /*optional out*/ CertificateTransparencyInfo* ctInfo,
844 /*optional out*/ bool* isBuiltChainRootBuiltInRoot,
845 /*optional out*/ bool* madeOCSPRequests,
846 /*optional out*/ IssuerSources* issuerSources) {
847 // XXX: MOZ_ASSERT(pinarg);
848 MOZ_ASSERT(!hostname.IsEmpty());
850 if (isBuiltChainRootBuiltInRoot) {
851 *isBuiltChainRootBuiltInRoot = false;
854 if (evStatus) {
855 *evStatus = EVStatus::NotEV;
858 if (hostname.IsEmpty()) {
859 return Result::ERROR_BAD_CERT_DOMAIN;
862 // CreateCertErrorRunnable assumes that CheckCertHostname is only called
863 // if VerifyCert succeeded.
864 Input peerCertInput;
865 Result rv =
866 peerCertInput.Init(peerCertBytes.Elements(), peerCertBytes.Length());
867 if (rv != Success) {
868 return rv;
870 bool isBuiltChainRootBuiltInRootLocal;
871 rv = VerifyCert(
872 peerCertBytes, certificateUsageSSLServer, time, pinarg,
873 PromiseFlatCString(hostname).get(), builtChain, flags, extraCertificates,
874 stapledOCSPResponse, sctsFromTLS, originAttributes, evStatus,
875 ocspStaplingStatus, keySizeStatus, pinningTelemetryInfo, ctInfo,
876 &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests, issuerSources);
877 if (rv != Success) {
878 // we don't use the certificate for path building, so this parameter doesn't
879 // matter
880 EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity;
881 BackCert peerBackCert(peerCertInput, notUsedForPaths, nullptr);
882 if (peerBackCert.Init() != Success) {
883 return rv;
885 if ((rv == Result::ERROR_UNKNOWN_ISSUER ||
886 rv == Result::ERROR_BAD_SIGNATURE ||
887 rv == Result::ERROR_INADEQUATE_KEY_USAGE) &&
888 CertIsSelfSigned(peerBackCert, pinarg)) {
889 // In this case we didn't find any issuer for the certificate, or we did
890 // find other certificates with the same subject but different keys, and
891 // the certificate is self-signed.
892 return Result::ERROR_SELF_SIGNED_CERT;
894 if (rv == Result::ERROR_UNKNOWN_ISSUER) {
895 // In this case we didn't get any valid path for the cert. Let's see if
896 // the issuer is the same as the issuer for our canary probe. If yes, this
897 // connection is connecting via a misconfigured proxy.
898 // Note: The MitM canary might not be set. In this case we consider this
899 // an unknown issuer error.
900 nsCOMPtr<nsINSSComponent> component(
901 do_GetService(PSM_COMPONENT_CONTRACTID));
902 if (!component) {
903 return Result::FATAL_ERROR_LIBRARY_FAILURE;
905 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and
906 // the feature is enabled.
907 Input issuerNameInput = peerBackCert.GetIssuer();
908 SECItem issuerNameItem = UnsafeMapInputToSECItem(issuerNameInput);
909 UniquePORTString issuerName(CERT_DerNameToAscii(&issuerNameItem));
910 if (!issuerName) {
911 return Result::ERROR_BAD_DER;
913 nsresult rv = component->IssuerMatchesMitmCanary(issuerName.get());
914 if (NS_SUCCEEDED(rv)) {
915 return Result::ERROR_MITM_DETECTED;
918 // If the certificate is expired or not yet valid, first check whether or
919 // not it is valid for the indicated hostname, because that would be a more
920 // serious error.
921 if (rv == Result::ERROR_EXPIRED_CERTIFICATE ||
922 rv == Result::ERROR_NOT_YET_VALID_CERTIFICATE ||
923 rv == Result::ERROR_INVALID_DER_TIME) {
924 Result hostnameResult =
925 CheckCertHostnameHelper(peerCertInput, hostname, false);
926 if (hostnameResult != Success) {
927 return hostnameResult;
930 return rv;
933 if (dcInfo) {
934 rv = IsDelegatedCredentialAcceptable(*dcInfo);
935 if (rv != Success) {
936 return rv;
940 Input stapledOCSPResponseInput;
941 Input* responseInputPtr = nullptr;
942 if (stapledOCSPResponse) {
943 rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(),
944 stapledOCSPResponse->Length());
945 if (rv != Success) {
946 // The stapled OCSP response was too big.
947 return Result::ERROR_OCSP_MALFORMED_RESPONSE;
949 responseInputPtr = &stapledOCSPResponseInput;
952 if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) {
953 rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr);
954 if (rv != Success) {
955 return rv;
959 rv = CheckCertHostnameHelper(peerCertInput, hostname,
960 isBuiltChainRootBuiltInRootLocal);
961 if (rv != Success) {
962 return rv;
965 if (isBuiltChainRootBuiltInRoot) {
966 *isBuiltChainRootBuiltInRoot = isBuiltChainRootBuiltInRootLocal;
969 return Success;
972 } // namespace psm
973 } // namespace mozilla