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"
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"
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"
29 #include "mozpkix/pkix.h"
30 #include "mozpkix/pkixcheck.h"
31 #include "mozpkix/pkixnss.h"
32 #include "mozpkix/pkixutil.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
,
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
, ¬BeforeSeconds
);
56 uint64_t notAfterSeconds
;
57 rv
= SecondsSinceEpochFromTime(certNotAfter
, ¬AfterSeconds
);
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
) {
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
);
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() {
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
),
119 mCRLiteMode(crliteMode
) {
121 mThirdPartyCerts
= thirdPartyCerts
.Clone();
122 for (const auto& root
: mThirdPartyCerts
) {
124 if (root
.GetInput(input
) == Success
) {
125 // mThirdPartyCerts consists of roots and intermediates.
126 if (root
.GetIsRoot()) {
127 mThirdPartyRootInputs
.AppendElement(input
);
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.
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
) {
160 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
161 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
165 nsCOMPtr
<nsINSSComponent
> component(do_GetService(PSM_COMPONENT_CONTRACTID
));
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
;
178 SECItem
certItem(UnsafeMapInputToSECItem(certInput
));
179 AutoSECMODListReadLock lock
;
180 for (SECMODModuleList
* list
= SECMOD_GetDefaultModuleList(); list
;
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
)) {
200 CK_OBJECT_HANDLE handle
=
201 PK11_FindEncodedCertInSlot(slot
, &certItem
, nullptr);
202 if (handle
== CK_INVALID_HANDLE
) {
205 if (PK11_HasAttributeSet(slot
, handle
, CKA_NSS_MOZILLA_CA_POLICY
,
207 // Attribute was found, and is set to true
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();
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
);
236 rv
= Result::ERROR_INADEQUATE_KEY_USAGE
;
240 if (ocspStaplingStatus
) {
241 *ocspStaplingStatus
= trustDomain
.GetOCSPStaplingStatus();
246 void CertVerifier::LoadKnownCTLogs() {
247 if (mCTMode
== CertificateTransparencyMode::Disabled
) {
250 mCTVerifier
= MakeUnique
<MultiLogCTVerifier
>();
251 for (const CTLogInfo
& log
: kCTLogList
) {
253 Result rv
= publicKey
.Init(
254 BitwiseCast
<const uint8_t*, const char*>(log
.key
), log
.keyLength
);
256 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
260 CTLogVerifier logVerifier
;
261 const CTLogOperatorInfo
& logOperator
=
262 kCTLogOperatorList
[log
.operatorIndex
];
263 rv
= logVerifier
.Init(publicKey
, logOperator
.id
, log
.status
,
264 log
.disqualificationTime
);
266 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
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
) {
284 if (mCTMode
== CertificateTransparencyMode::Disabled
) {
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.
323 CTVerifyResult emptyResult
;
324 ctInfo
->verifyResult
= std::move(emptyResult
);
325 ctInfo
->policyCompliance
= CTPolicyCompliance::NotEnoughScts
;
330 const nsTArray
<uint8_t>& endEntityBytes
= builtChain
.ElementAt(0);
331 Input endEntityInput
;
333 endEntityInput
.Init(endEntityBytes
.Elements(), endEntityBytes
.Length());
338 const nsTArray
<uint8_t>& issuerBytes
= builtChain
.ElementAt(1);
340 rv
= issuerInput
.Init(issuerBytes
.Elements(), issuerBytes
.Length());
345 BackCert
issuerBackCert(issuerInput
, EndEntityOrCA::MustBeCA
, nullptr);
346 rv
= issuerBackCert
.Init();
350 Input issuerPublicKeyInput
= issuerBackCert
.GetSubjectPublicKeyInfo();
352 CTVerifyResult result
;
353 rv
= mCTVerifier
->Verify(endEntityInput
, issuerPublicKeyInput
, embeddedSCTs
,
354 sctsFromOCSP
, sctsFromTLS
, time
, result
);
356 MOZ_LOG(gCertVerifierLog
, LogLevel::Debug
,
357 ("SCT verification failed with fatal error %" PRId32
"\n",
358 static_cast<uint32_t>(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
:
373 case VerifiedSCT::Status::ValidFromDisqualifiedLog
:
374 disqualifiedLogCount
++;
376 case VerifiedSCT::Status::UnknownLog
:
379 case VerifiedSCT::Status::InvalidSignature
:
380 invalidSignatureCount
++;
382 case VerifiedSCT::Status::InvalidTimestamp
:
383 invalidTimestampCount
++;
385 case VerifiedSCT::Status::None
:
387 MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status");
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
,
402 rv
= endEntityBackCert
.Init();
406 Time
notBefore(Time::uninitialized
);
407 Time
notAfter(Time::uninitialized
);
408 rv
= ParseValidity(endEntityBackCert
.GetValidity(), ¬Before
, ¬After
);
412 size_t lifetimeInMonths
;
413 rv
= GetCertLifetimeInFullMonths(notBefore
, notAfter
, lifetimeInMonths
);
418 CTLogOperatorList allOperators
;
419 GetCTLogOperatorsFromVerifiedSCTList(result
.verifiedScts
, allOperators
);
421 CTLogOperatorList dependentOperators
;
422 rv
= mCTDiversityPolicy
->GetDependentOperators(builtChain
, allOperators
,
428 CTPolicyEnforcer ctPolicyEnforcer
;
429 CTPolicyCompliance ctPolicyCompliance
;
430 ctPolicyEnforcer
.CheckCompliance(result
.verifiedScts
, lifetimeInMonths
,
431 dependentOperators
, ctPolicyCompliance
);
434 ctInfo
->verifyResult
= std::move(result
);
435 ctInfo
->policyCompliance
= ctPolicyCompliance
;
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
;
470 *evStatus
= EVStatus::NotEV
;
472 if (ocspStaplingStatus
) {
473 if (usage
!= certificateUsageSSLServer
) {
474 return Result::FATAL_ERROR_INVALID_ARGS
;
476 *ocspStaplingStatus
= OCSP_STAPLING_NEVER_CHECKED
;
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;
499 issuerSources
->clear();
503 Result rv
= certDER
.Init(certBytes
.Elements(), certBytes
.Length());
508 // We configure the OCSP fetching modes separately for EV and non-EV
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());
523 // The stapled OCSP response was too big.
524 return Result::ERROR_OCSP_MALFORMED_RESPONSE
;
526 stapledOCSPResponse
= &stapledOCSPResponseInput
;
529 Input sctsFromTLSInput
;
531 rv
= sctsFromTLSInput
.Init(sctsFromTLS
->Elements(), sctsFromTLS
->Length());
532 if (rv
!= Success
&& sctsFromTLSInput
.GetLength() != 0) {
533 return Result::FATAL_ERROR_LIBRARY_FAILURE
;
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,
549 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
550 KeyUsage::digitalSignature
, KeyPurposeId::id_kp_clientAuth
,
551 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
552 if (madeOCSPRequests
) {
554 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
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
,
588 if (madeOCSPRequests
) {
590 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
593 *issuerSources
= trustDomain
.GetIssuerSources();
596 rv
= VerifyCertificateTransparencyPolicy(
597 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
601 *evStatus
= EVStatus::EV
;
603 if (isBuiltChainRootBuiltInRoot
) {
604 *isBuiltChainRootBuiltInRoot
=
605 trustDomain
.GetIsBuiltChainRootBuiltInRoot();
613 if (flags
& FLAG_MUST_BE_EV
) {
614 rv
= Result::ERROR_POLICY_VALIDATION_FAILED
;
619 unsigned int keySizeOptions
[] = {MIN_RSA_BITS
, MIN_RSA_BITS_WEAK
};
621 KeySizeStatus keySizeStatuses
[] = {KeySizeStatus::LargeMinimumSucceeded
,
622 KeySizeStatus::CompatibilityRisk
};
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
) {
652 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
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
;
667 rv
= VerifyCertificateTransparencyPolicy(
668 trustDomain
, builtChain
, sctsFromTLSInput
, time
, ctInfo
);
672 *keySizeStatus
= keySizeStatuses
[i
];
674 if (isBuiltChainRootBuiltInRoot
) {
675 *isBuiltChainRootBuiltInRoot
=
676 trustDomain
.GetIsBuiltChainRootBuiltInRoot();
682 if (rv
!= Success
&& keySizeStatus
) {
683 *keySizeStatus
= KeySizeStatus::AlreadyBad
;
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,
697 rv
= BuildCertChain(trustDomain
, certDER
, time
, EndEntityOrCA::MustBeCA
,
698 KeyUsage::keyCertSign
, KeyPurposeId::id_kp_serverAuth
,
699 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
700 if (madeOCSPRequests
) {
702 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
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,
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
) {
721 trustDomain
, certDER
, time
, EndEntityOrCA::MustBeEndEntity
,
722 KeyUsage::nonRepudiation
, KeyPurposeId::id_kp_emailProtection
,
723 CertPolicyId::anyPolicy
, stapledOCSPResponse
);
725 if (madeOCSPRequests
) {
727 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
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,
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
) {
757 trustDomain
.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched
;
763 rv
= Result::FATAL_ERROR_INVALID_ARGS
;
773 static bool CertIsSelfSigned(const BackCert
& backCert
, void* pinarg
) {
774 if (!InputsAreEqual(backCert
.GetIssuer(), backCert
.GetSubject())) {
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
{
789 explicit SkipInvalidSANsForNonBuiltInRootsPolicy(bool rootIsBuiltIn
)
790 : mRootIsBuiltIn(rootIsBuiltIn
) {}
792 virtual Result
FallBackToCommonName(
794 /*out*/ FallBackToSearchWithinSubject
& fallBackToCommonName
) override
{
795 fallBackToCommonName
= FallBackToSearchWithinSubject::No
;
799 virtual HandleInvalidSubjectAlternativeNamesBy
800 HandleInvalidSubjectAlternativeNames() override
{
801 return mRootIsBuiltIn
? HandleInvalidSubjectAlternativeNamesBy::Halting
802 : HandleInvalidSubjectAlternativeNamesBy::Skipping
;
809 static Result
CheckCertHostnameHelper(Input peerCertInput
,
810 const nsACString
& hostname
,
811 bool rootIsBuiltIn
) {
813 Result rv
= hostnameInput
.Init(
814 BitwiseCast
<const uint8_t*, const char*>(hostname
.BeginReading()),
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
;
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;
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.
866 peerCertInput
.Init(peerCertBytes
.Elements(), peerCertBytes
.Length());
870 bool isBuiltChainRootBuiltInRootLocal
;
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
);
878 // we don't use the certificate for path building, so this parameter doesn't
880 EndEntityOrCA notUsedForPaths
= EndEntityOrCA::MustBeEndEntity
;
881 BackCert
peerBackCert(peerCertInput
, notUsedForPaths
, nullptr);
882 if (peerBackCert
.Init() != Success
) {
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
));
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
));
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
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
;
934 rv
= IsDelegatedCredentialAcceptable(*dcInfo
);
940 Input stapledOCSPResponseInput
;
941 Input
* responseInputPtr
= nullptr;
942 if (stapledOCSPResponse
) {
943 rv
= stapledOCSPResponseInput
.Init(stapledOCSPResponse
->Elements(),
944 stapledOCSPResponse
->Length());
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
);
959 rv
= CheckCertHostnameHelper(peerCertInput
, hostname
,
960 isBuiltChainRootBuiltInRootLocal
);
965 if (isBuiltChainRootBuiltInRoot
) {
966 *isBuiltChainRootBuiltInRoot
= isBuiltChainRootBuiltInRootLocal
;
973 } // namespace mozilla