CertVerifier.cpp (43465B)
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/. */ 6 7 #include "CertVerifier.h" 8 9 #include <stdint.h> 10 11 #include "AppTrustDomain.h" 12 #include "CTKnownLogs.h" 13 #include "CTLogVerifier.h" 14 #include "ExtendedValidation.h" 15 #include "MultiLogCTVerifier.h" 16 #include "NSSCertDBTrustDomain.h" 17 #include "NSSErrorsService.h" 18 #include "cert.h" 19 #include "mozilla/Assertions.h" 20 #include "mozilla/Casting.h" 21 #include "mozilla/IntegerPrintfMacros.h" 22 #include "mozilla/Logging.h" 23 #include "mozilla/StaticPrefs_security.h" 24 #include "mozilla/SyncRunnable.h" 25 #include "mozpkix/pkix.h" 26 #include "mozpkix/pkixcheck.h" 27 #include "mozpkix/pkixnss.h" 28 #include "mozpkix/pkixutil.h" 29 #include "nsNSSComponent.h" 30 #include "nsNetCID.h" 31 #include "nsPromiseFlatString.h" 32 #include "nsServiceManagerUtils.h" 33 #include "pk11pub.h" 34 #include "secmod.h" 35 36 using namespace mozilla::ct; 37 using namespace mozilla::pkix; 38 using namespace mozilla::psm; 39 40 mozilla::LazyLogModule gCertVerifierLog("certverifier"); 41 42 namespace mozilla { 43 namespace psm { 44 45 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1; 46 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2; 47 const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4; 48 static const unsigned int MIN_RSA_BITS = 2048; 49 static const unsigned int MIN_RSA_BITS_WEAK = 1024; 50 51 void CertificateTransparencyInfo::Reset() { 52 enabled = false; 53 verifyResult.Reset(); 54 policyCompliance.reset(); 55 } 56 57 CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, 58 mozilla::TimeDuration ocspTimeoutSoft, 59 mozilla::TimeDuration ocspTimeoutHard, 60 uint32_t certShortLifetimeInDays, 61 CertificateTransparencyConfig&& ctConfig, 62 CRLiteMode crliteMode, 63 const nsTArray<EnterpriseCert>& thirdPartyCerts) 64 : mOCSPDownloadConfig(odc), 65 mOCSPStrict(osc == ocspStrict), 66 mOCSPTimeoutSoft(ocspTimeoutSoft), 67 mOCSPTimeoutHard(ocspTimeoutHard), 68 mCertShortLifetimeInDays(certShortLifetimeInDays), 69 mCTConfig(std::move(ctConfig)), 70 mCRLiteMode(crliteMode), 71 mSignatureCache( 72 signature_cache_new( 73 StaticPrefs::security_pki_cert_signature_cache_size()), 74 signature_cache_free), 75 mTrustCache( 76 trust_cache_new(StaticPrefs::security_pki_cert_trust_cache_size()), 77 trust_cache_free) { 78 LoadKnownCTLogs(); 79 mThirdPartyCerts = thirdPartyCerts.Clone(); 80 for (const auto& root : mThirdPartyCerts) { 81 Input input; 82 if (root.GetInput(input) == Success) { 83 // mThirdPartyCerts consists of roots and intermediates. 84 if (root.GetIsRoot()) { 85 mThirdPartyRootInputs.AppendElement(input); 86 } else { 87 mThirdPartyIntermediateInputs.AppendElement(input); 88 } 89 } 90 } 91 } 92 93 CertVerifier::~CertVerifier() = default; 94 95 Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) { 96 bool isEcdsa = dcInfo.scheme == ssl_sig_ecdsa_secp256r1_sha256 || 97 dcInfo.scheme == ssl_sig_ecdsa_secp384r1_sha384 || 98 dcInfo.scheme == ssl_sig_ecdsa_secp521r1_sha512; 99 100 // Firefox currently does not advertise any RSA schemes for use 101 // with Delegated Credentials. As a secondary (on top of NSS) 102 // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_* 103 // schemes are supported, check the modulus size and allow RSA here. 104 if (!isEcdsa) { 105 return Result::ERROR_INVALID_KEY; 106 } 107 108 return Result::Success; 109 } 110 111 // The term "builtin root" traditionally refers to a root CA certificate that 112 // has been added to the NSS trust store, because it has been approved 113 // for inclusion according to the Mozilla CA policy, and might be accepted 114 // by Mozilla applications as an issuer for certificates seen on the public web. 115 Result IsCertBuiltInRoot(Input certInput, bool& result) { 116 result = false; 117 118 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) { 119 return Result::FATAL_ERROR_LIBRARY_FAILURE; 120 } 121 122 #ifdef DEBUG 123 nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID)); 124 if (!component) { 125 return Result::FATAL_ERROR_LIBRARY_FAILURE; 126 } 127 nsTArray<uint8_t> certBytes; 128 certBytes.AppendElements(certInput.UnsafeGetData(), certInput.GetLength()); 129 if (NS_FAILED(component->IsCertTestBuiltInRoot(certBytes, &result))) { 130 return Result::FATAL_ERROR_LIBRARY_FAILURE; 131 } 132 if (result) { 133 return Success; 134 } 135 #endif // DEBUG 136 SECItem certItem(UnsafeMapInputToSECItem(certInput)); 137 AutoSECMODListReadLock lock; 138 for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list; 139 list = list->next) { 140 for (int i = 0; i < list->module->slotCount; i++) { 141 PK11SlotInfo* slot = list->module->slots[i]; 142 // We're searching for the "builtin root module", which is a module that 143 // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST. 144 // We use PK11_HasRootCerts() to identify a module with that property. 145 // In the past, we exclusively used the PKCS#11 module named nssckbi, 146 // which is provided by the NSS library. 147 // Nowadays, some distributions use a replacement module, which contains 148 // the builtin roots, but which also contains additional CA certificates, 149 // such as CAs trusted in a local deployment. 150 // We want to be able to distinguish between these two categories, 151 // because a CA, which may issue certificates for the public web, 152 // is expected to comply with additional requirements. 153 // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true, 154 // then we treat it as a "builtin root". 155 if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) { 156 continue; 157 } 158 CK_OBJECT_HANDLE handle = 159 PK11_FindEncodedCertInSlot(slot, &certItem, nullptr); 160 if (handle == CK_INVALID_HANDLE) { 161 continue; 162 } 163 if (PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY, 164 false)) { 165 // Attribute was found, and is set to true 166 result = true; 167 break; 168 } 169 } 170 } 171 return Success; 172 } 173 174 static Result BuildCertChainForOneKeyUsage( 175 NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1, 176 KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku, 177 const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse, 178 /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) { 179 trustDomain.ResetAccumulatedState(); 180 Result rv = 181 BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, 182 ku1, eku, requiredPolicy, stapledOCSPResponse); 183 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { 184 trustDomain.ResetAccumulatedState(); 185 rv = BuildCertChain(trustDomain, certDER, time, 186 EndEntityOrCA::MustBeEndEntity, ku2, eku, 187 requiredPolicy, stapledOCSPResponse); 188 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { 189 trustDomain.ResetAccumulatedState(); 190 rv = BuildCertChain(trustDomain, certDER, time, 191 EndEntityOrCA::MustBeEndEntity, ku3, eku, 192 requiredPolicy, stapledOCSPResponse); 193 if (rv != Success) { 194 rv = Result::ERROR_INADEQUATE_KEY_USAGE; 195 } 196 } 197 } 198 if (ocspStaplingStatus) { 199 *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus(); 200 } 201 return rv; 202 } 203 204 void CertVerifier::LoadKnownCTLogs() { 205 mCTVerifier = MakeUnique<MultiLogCTVerifier>(); 206 for (const CTLogInfo& log : kCTLogList) { 207 Input publicKey; 208 Result rv = publicKey.Init( 209 BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength); 210 if (rv != Success) { 211 MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log"); 212 continue; 213 } 214 215 const CTLogOperatorInfo& logOperator = 216 kCTLogOperatorList[log.operatorIndex]; 217 CTLogVerifier logVerifier(logOperator.id, log.state, log.format, 218 log.timestamp); 219 rv = logVerifier.Init(publicKey); 220 if (rv != Success) { 221 MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log"); 222 continue; 223 } 224 225 mCTVerifier->AddLog(std::move(logVerifier)); 226 } 227 } 228 229 bool HostnameMatchesPolicy(const char* hostname, const nsCString& policy) { 230 // Some contexts don't have a hostname (mostly tests), in which case the 231 // policy doesn't apply. 232 if (!hostname) { 233 return false; 234 } 235 nsDependentCString hostnameString(hostname); 236 // The policy is a comma-separated list of entries of the form 237 // '.example.com', 'example.com', or an IP address. 238 for (const auto& entry : policy.Split(',')) { 239 if (entry.IsEmpty()) { 240 continue; 241 } 242 // For '.example.com' entries, exact matches match the policy. 243 if (entry[0] == '.' && 244 Substring(entry, 1).EqualsIgnoreCase(hostnameString)) { 245 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 246 ("not enforcing CT for '%s' (matches policy '%s')", hostname, 247 policy.get())); 248 return true; 249 } 250 // For 'example.com' entries, exact matches or subdomains match the policy 251 // (IP addresses match here too). 252 if (StringEndsWith(hostnameString, entry) && 253 (hostnameString.Length() == entry.Length() || 254 hostnameString[hostnameString.Length() - entry.Length() - 1] == '.')) { 255 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 256 ("not enforcing CT for '%s' (matches policy '%s')", hostname, 257 policy.get())); 258 return true; 259 } 260 } 261 return false; 262 } 263 264 bool CertificateListHasSPKIHashIn( 265 const nsTArray<nsTArray<uint8_t>>& certificates, 266 const nsTArray<CopyableTArray<uint8_t>>& spkiHashes) { 267 if (spkiHashes.IsEmpty()) { 268 return false; 269 } 270 for (const auto& certificate : certificates) { 271 Input certificateInput; 272 if (certificateInput.Init(certificate.Elements(), certificate.Length()) != 273 Success) { 274 return false; 275 } 276 // No path building is happening here, so this parameter doesn't matter. 277 EndEntityOrCA notUsedForPathBuilding = EndEntityOrCA::MustBeEndEntity; 278 BackCert decodedCertificate(certificateInput, notUsedForPathBuilding, 279 nullptr); 280 if (decodedCertificate.Init() != Success) { 281 return false; 282 } 283 Input spki(decodedCertificate.GetSubjectPublicKeyInfo()); 284 uint8_t spkiHash[SHA256_LENGTH]; 285 if (DigestBufNSS(spki, DigestAlgorithm::sha256, spkiHash, 286 sizeof(spkiHash)) != Success) { 287 return false; 288 } 289 Span spkiHashSpan(reinterpret_cast<const uint8_t*>(spkiHash), 290 sizeof(spkiHash)); 291 for (const auto& candidateSPKIHash : spkiHashes) { 292 if (Span(candidateSPKIHash) == spkiHashSpan) { 293 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 294 ("found SPKI hash match - not enforcing CT")); 295 return true; 296 } 297 } 298 } 299 return false; 300 } 301 302 Result CertVerifier::VerifyCertificateTransparencyPolicy( 303 NSSCertDBTrustDomain& trustDomain, 304 const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time, 305 const char* hostname, 306 /*optional out*/ CertificateTransparencyInfo* ctInfo) { 307 if (builtChain.IsEmpty()) { 308 return Result::FATAL_ERROR_INVALID_ARGS; 309 } 310 if (ctInfo) { 311 ctInfo->Reset(); 312 } 313 if (mCTConfig.mMode == CertificateTransparencyMode::Disabled || 314 !trustDomain.GetIsBuiltChainRootBuiltInRoot()) { 315 return Success; 316 } 317 if (time > TimeFromEpochInSeconds(kCTExpirationTime / PR_USEC_PER_SEC)) { 318 MOZ_LOG(gCertVerifierLog, LogLevel::Warning, 319 ("skipping CT - built-in information has expired")); 320 return Success; 321 } 322 if (ctInfo) { 323 ctInfo->enabled = true; 324 } 325 326 Result rv = VerifyCertificateTransparencyPolicyInner( 327 trustDomain, builtChain, sctsFromTLS, time, ctInfo); 328 if (rv == Result::ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY && 329 (mCTConfig.mMode != CertificateTransparencyMode::Enforce || 330 HostnameMatchesPolicy(hostname, mCTConfig.mSkipForHosts) || 331 CertificateListHasSPKIHashIn(builtChain, 332 mCTConfig.mSkipForSPKIHashes))) { 333 return Success; 334 } 335 336 return rv; 337 } 338 339 Result CertVerifier::VerifyCertificateTransparencyPolicyInner( 340 NSSCertDBTrustDomain& trustDomain, 341 const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time, 342 /*optional out*/ CertificateTransparencyInfo* ctInfo) { 343 if (builtChain.Length() == 1) { 344 // Issuer certificate is required for SCT verification. 345 // If we've arrived here, we probably have a "trust chain" with only one 346 // certificate (i.e. a self-signed end-entity that has been set as a trust 347 // anchor either by a third party modifying our trust DB or via the 348 // enterprise roots feature). If this is the case, certificate transparency 349 // information will probably not be present, and it certainly won't verify 350 // correctly. To simplify things, we return an empty CTVerifyResult and a 351 // "not enough SCTs" CTPolicyCompliance result. 352 if (ctInfo) { 353 CTVerifyResult emptyResult; 354 ctInfo->verifyResult = std::move(emptyResult); 355 ctInfo->policyCompliance.emplace(CTPolicyCompliance::NotEnoughScts); 356 } 357 return Result::ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY; 358 } 359 360 const nsTArray<uint8_t>& endEntityBytes = builtChain.ElementAt(0); 361 Input endEntityInput; 362 Result rv = 363 endEntityInput.Init(endEntityBytes.Elements(), endEntityBytes.Length()); 364 if (rv != Success) { 365 return rv; 366 } 367 368 // We evaluated embedded SCTs and SCTs from the TLS handshake before we 369 // performed revocation checks, and we should have a cached CTVerifyResult in 370 // the trust domain. If we later received SCTs from OCSP (very rare), then 371 // we'll re-check all of the SCTs. Otherwise we'll use the cached 372 // CTVerifyResult. 373 Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling(); 374 if (sctsFromOCSP.GetLength() > 0) { 375 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 376 ("Got OCSP SCT data of length %zu", 377 static_cast<size_t>(sctsFromOCSP.GetLength()))); 378 } 379 380 CTVerifyResult result; 381 if (trustDomain.GetCachedCTVerifyResult().isSome() && 382 sctsFromOCSP.GetLength() == 0) { 383 result = trustDomain.GetCachedCTVerifyResult().extract(); 384 } else { 385 // invalidate the cached result if it exists 386 trustDomain.GetCachedCTVerifyResult().reset(); 387 388 Input embeddedSCTs = trustDomain.GetSCTListFromCertificate(); 389 if (embeddedSCTs.GetLength() > 0) { 390 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 391 ("Got embedded SCT data of length %zu", 392 static_cast<size_t>(embeddedSCTs.GetLength()))); 393 } 394 if (sctsFromTLS.GetLength() > 0) { 395 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 396 ("Got TLS SCT data of length %zu", 397 static_cast<size_t>(sctsFromTLS.GetLength()))); 398 } 399 400 const nsTArray<uint8_t>& issuerBytes = builtChain.ElementAt(1); 401 Input issuerInput; 402 rv = issuerInput.Init(issuerBytes.Elements(), issuerBytes.Length()); 403 if (rv != Success) { 404 return rv; 405 } 406 407 BackCert issuerBackCert(issuerInput, EndEntityOrCA::MustBeCA, nullptr); 408 rv = issuerBackCert.Init(); 409 if (rv != Success) { 410 return rv; 411 } 412 Input issuerPublicKeyInput = issuerBackCert.GetSubjectPublicKeyInfo(); 413 414 rv = mCTVerifier->Verify(endEntityInput, issuerPublicKeyInput, embeddedSCTs, 415 sctsFromOCSP, sctsFromTLS, time, 416 trustDomain.GetDistrustAfterTime(), result); 417 if (rv != Success) { 418 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 419 ("SCT verification failed with fatal error %" PRId32 "\n", 420 static_cast<uint32_t>(rv))); 421 return rv; 422 } 423 } 424 425 if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) { 426 size_t validCount = 0; 427 size_t retiredLogCount = 0; 428 for (const VerifiedSCT& verifiedSct : result.verifiedScts) { 429 switch (verifiedSct.logState) { 430 case CTLogState::Admissible: 431 validCount++; 432 break; 433 case CTLogState::Retired: 434 retiredLogCount++; 435 break; 436 } 437 } 438 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, 439 ("SCT verification result: " 440 "valid=%zu unknownLog=%zu retiredLog=%zu " 441 "invalidSignature=%zu invalidTimestamp=%zu " 442 "distrustedTimestamp=%zu decodingErrors=%zu\n", 443 validCount, result.sctsFromUnknownLogs, retiredLogCount, 444 result.sctsWithInvalidSignatures, result.sctsWithInvalidTimestamps, 445 result.sctsWithDistrustedTimestamps, result.decodingErrors)); 446 } 447 448 BackCert endEntityBackCert(endEntityInput, EndEntityOrCA::MustBeEndEntity, 449 nullptr); 450 rv = endEntityBackCert.Init(); 451 if (rv != Success) { 452 return rv; 453 } 454 Time notBefore(Time::uninitialized); 455 Time notAfter(Time::uninitialized); 456 rv = ParseValidity(endEntityBackCert.GetValidity(), ¬Before, ¬After); 457 if (rv != Success) { 458 return rv; 459 } 460 Duration certLifetime(notBefore, notAfter); 461 462 CTPolicyCompliance ctPolicyCompliance = 463 CheckCTPolicyCompliance(result.verifiedScts, certLifetime); 464 465 if (ctInfo) { 466 ctInfo->verifyResult = std::move(result); 467 ctInfo->policyCompliance.emplace(ctPolicyCompliance); 468 } 469 470 if (ctPolicyCompliance != CTPolicyCompliance::Compliant) { 471 return Result::ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY; 472 } 473 474 return Success; 475 } 476 477 Result CertVerifier::VerifyCert( 478 const nsTArray<uint8_t>& certBytes, VerifyUsage usage, Time time, 479 void* pinArg, const char* hostname, 480 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, 481 /*optional*/ const Flags flags, 482 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, 483 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg, 484 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, 485 /*optional*/ const OriginAttributes& originAttributes, 486 /*optional out*/ EVStatus* evStatus, 487 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, 488 /*optional out*/ KeySizeStatus* keySizeStatus, 489 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, 490 /*optional out*/ CertificateTransparencyInfo* ctInfo, 491 /*optional out*/ bool* isBuiltChainRootBuiltInRoot, 492 /*optional out*/ bool* madeOCSPRequests, 493 /*optional out*/ IssuerSources* issuerSources) { 494 MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n")); 495 496 MOZ_ASSERT(usage == VerifyUsage::TLSServer || !(flags & FLAG_MUST_BE_EV)); 497 MOZ_ASSERT(usage == VerifyUsage::TLSServer || !keySizeStatus); 498 499 if (NS_FAILED(BlockUntilLoadableCertsLoaded())) { 500 return Result::FATAL_ERROR_LIBRARY_FAILURE; 501 } 502 if (NS_FAILED(CheckForSmartCardChanges())) { 503 return Result::FATAL_ERROR_LIBRARY_FAILURE; 504 } 505 506 if (evStatus) { 507 *evStatus = EVStatus::NotEV; 508 } 509 if (ocspStaplingStatus) { 510 if (usage != VerifyUsage::TLSServer) { 511 return Result::FATAL_ERROR_INVALID_ARGS; 512 } 513 *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED; 514 } 515 516 if (keySizeStatus) { 517 if (usage != VerifyUsage::TLSServer) { 518 return Result::FATAL_ERROR_INVALID_ARGS; 519 } 520 *keySizeStatus = KeySizeStatus::NeverChecked; 521 } 522 523 if (usage != VerifyUsage::TLSServer && (flags & FLAG_MUST_BE_EV)) { 524 return Result::FATAL_ERROR_INVALID_ARGS; 525 } 526 527 if (isBuiltChainRootBuiltInRoot) { 528 *isBuiltChainRootBuiltInRoot = false; 529 } 530 531 if (madeOCSPRequests) { 532 *madeOCSPRequests = false; 533 } 534 535 if (issuerSources) { 536 issuerSources->clear(); 537 } 538 539 Input certDER; 540 Result rv = certDER.Init(certBytes.Elements(), certBytes.Length()); 541 if (rv != Success) { 542 return rv; 543 } 544 545 // We configure the OCSP fetching modes separately for EV and non-EV 546 // verifications. 547 NSSCertDBTrustDomain::RevocationCheckMode defaultRevCheckMode = 548 (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) || 549 (flags & FLAG_LOCAL_ONLY) 550 ? NSSCertDBTrustDomain::RevocationCheckLocalOnly 551 : !mOCSPStrict ? NSSCertDBTrustDomain::RevocationCheckMayFetch 552 : NSSCertDBTrustDomain::RevocationCheckRequired; 553 554 Input stapledOCSPResponseInput; 555 const Input* stapledOCSPResponse = nullptr; 556 if (stapledOCSPResponseArg) { 557 rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(), 558 stapledOCSPResponseArg->Length()); 559 if (rv != Success) { 560 // The stapled OCSP response was too big. 561 return Result::ERROR_OCSP_MALFORMED_RESPONSE; 562 } 563 stapledOCSPResponse = &stapledOCSPResponseInput; 564 } 565 566 Input sctsFromTLSInput; 567 if (sctsFromTLS) { 568 rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length()); 569 if (rv != Success && sctsFromTLSInput.GetLength() != 0) { 570 return Result::FATAL_ERROR_LIBRARY_FAILURE; 571 } 572 } 573 574 switch (usage) { 575 case VerifyUsage::TLSClient: { 576 // XXX: We don't really have a trust bit for SSL client authentication so 577 // just use trustEmail as it is the closest alternative. 578 NSSCertDBTrustDomain trustDomain( 579 trustEmail, defaultRevCheckMode, mOCSPCache, mSignatureCache.get(), 580 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 581 mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, mCRLiteMode, 582 originAttributes, mThirdPartyRootInputs, 583 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 584 mCTVerifier, builtChain, nullptr, nullptr); 585 rv = BuildCertChain( 586 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, 587 KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth, 588 CertPolicyId::anyPolicy, stapledOCSPResponse); 589 if (madeOCSPRequests) { 590 *madeOCSPRequests |= 591 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 592 } 593 break; 594 } 595 596 case VerifyUsage::TLSServer: { 597 // TODO: When verifying a certificate in an SSL handshake, we should 598 // restrict the acceptable key usage based on the key exchange method 599 // chosen by the server. 600 601 // Try to validate for EV first. 602 NSSCertDBTrustDomain::RevocationCheckMode evRevCheckMode = 603 (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY) 604 ? NSSCertDBTrustDomain::RevocationCheckLocalOnly 605 : !mOCSPStrict ? NSSCertDBTrustDomain::RevocationCheckMayFetch 606 : NSSCertDBTrustDomain::RevocationCheckRequired; 607 608 nsTArray<CertPolicyId> evPolicies; 609 GetKnownEVPolicies(certBytes, evPolicies); 610 rv = Result::ERROR_UNKNOWN_ERROR; 611 for (const auto& evPolicy : evPolicies) { 612 NSSCertDBTrustDomain trustDomain( 613 trustSSL, evRevCheckMode, mOCSPCache, mSignatureCache.get(), 614 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 615 mCertShortLifetimeInDays, MIN_RSA_BITS, mCRLiteMode, 616 originAttributes, mThirdPartyRootInputs, 617 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 618 mCTVerifier, builtChain, pinningTelemetryInfo, hostname); 619 rv = BuildCertChainForOneKeyUsage( 620 trustDomain, certDER, time, 621 KeyUsage::digitalSignature, // (EC)DHE 622 KeyUsage::keyEncipherment, // RSA 623 KeyUsage::keyAgreement, // (EC)DH 624 KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse, 625 ocspStaplingStatus); 626 if (madeOCSPRequests) { 627 *madeOCSPRequests |= 628 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 629 } 630 if (issuerSources) { 631 *issuerSources = trustDomain.GetIssuerSources(); 632 } 633 if (rv == Success) { 634 rv = VerifyCertificateTransparencyPolicy(trustDomain, builtChain, 635 sctsFromTLSInput, time, 636 hostname, ctInfo); 637 } 638 if (rv == Success) { 639 if (evStatus) { 640 *evStatus = EVStatus::EV; 641 } 642 if (isBuiltChainRootBuiltInRoot) { 643 *isBuiltChainRootBuiltInRoot = 644 trustDomain.GetIsBuiltChainRootBuiltInRoot(); 645 } 646 break; 647 } 648 } 649 if (rv == Success) { 650 break; 651 } 652 if (flags & FLAG_MUST_BE_EV) { 653 rv = Result::ERROR_POLICY_VALIDATION_FAILED; 654 break; 655 } 656 657 // Now try non-EV. 658 unsigned int keySizeOptions[] = {MIN_RSA_BITS, MIN_RSA_BITS_WEAK}; 659 660 KeySizeStatus keySizeStatuses[] = {KeySizeStatus::LargeMinimumSucceeded, 661 KeySizeStatus::CompatibilityRisk}; 662 663 static_assert(std::size(keySizeOptions) == std::size(keySizeStatuses), 664 "keySize array lengths differ"); 665 666 size_t keySizeOptionsCount = std::size(keySizeStatuses); 667 668 for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) { 669 // invalidate any telemetry info relating to failed chains 670 if (pinningTelemetryInfo) { 671 pinningTelemetryInfo->Reset(); 672 } 673 674 NSSCertDBTrustDomain trustDomain( 675 trustSSL, defaultRevCheckMode, mOCSPCache, mSignatureCache.get(), 676 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 677 mCertShortLifetimeInDays, keySizeOptions[i], mCRLiteMode, 678 originAttributes, mThirdPartyRootInputs, 679 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 680 mCTVerifier, builtChain, pinningTelemetryInfo, hostname); 681 rv = BuildCertChainForOneKeyUsage( 682 trustDomain, certDER, time, 683 KeyUsage::digitalSignature, //(EC)DHE 684 KeyUsage::keyEncipherment, // RSA 685 KeyUsage::keyAgreement, //(EC)DH 686 KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, 687 stapledOCSPResponse, ocspStaplingStatus); 688 if (madeOCSPRequests) { 689 *madeOCSPRequests |= 690 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 691 } 692 if (issuerSources) { 693 *issuerSources = trustDomain.GetIssuerSources(); 694 } 695 if (rv == Success) { 696 rv = VerifyCertificateTransparencyPolicy(trustDomain, builtChain, 697 sctsFromTLSInput, time, 698 hostname, ctInfo); 699 } 700 if (rv == Success) { 701 if (keySizeStatus) { 702 *keySizeStatus = keySizeStatuses[i]; 703 } 704 if (isBuiltChainRootBuiltInRoot) { 705 *isBuiltChainRootBuiltInRoot = 706 trustDomain.GetIsBuiltChainRootBuiltInRoot(); 707 } 708 break; 709 } 710 } 711 712 if (rv != Success && keySizeStatus) { 713 *keySizeStatus = KeySizeStatus::AlreadyBad; 714 } 715 716 break; 717 } 718 719 case VerifyUsage::EmailCA: 720 case VerifyUsage::TLSClientCA: 721 case VerifyUsage::TLSServerCA: { 722 KeyPurposeId purpose; 723 SECTrustType trustType; 724 725 if (usage == VerifyUsage::EmailCA || usage == VerifyUsage::TLSClientCA) { 726 purpose = KeyPurposeId::id_kp_clientAuth; 727 trustType = trustEmail; 728 } else if (usage == VerifyUsage::TLSServerCA) { 729 purpose = KeyPurposeId::id_kp_serverAuth; 730 trustType = trustSSL; 731 } else { 732 MOZ_ASSERT_UNREACHABLE("coding error"); 733 return Result::FATAL_ERROR_LIBRARY_FAILURE; 734 } 735 736 NSSCertDBTrustDomain trustDomain( 737 trustType, defaultRevCheckMode, mOCSPCache, mSignatureCache.get(), 738 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 739 mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, mCRLiteMode, 740 originAttributes, mThirdPartyRootInputs, 741 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 742 mCTVerifier, builtChain, nullptr, nullptr); 743 rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA, 744 KeyUsage::keyCertSign, purpose, 745 CertPolicyId::anyPolicy, stapledOCSPResponse); 746 if (madeOCSPRequests) { 747 *madeOCSPRequests |= 748 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 749 } 750 break; 751 } 752 753 case VerifyUsage::EmailSigner: { 754 NSSCertDBTrustDomain trustDomain( 755 trustEmail, defaultRevCheckMode, mOCSPCache, mSignatureCache.get(), 756 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 757 mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, mCRLiteMode, 758 originAttributes, mThirdPartyRootInputs, 759 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 760 mCTVerifier, builtChain, nullptr, nullptr); 761 rv = BuildCertChain( 762 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, 763 KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection, 764 CertPolicyId::anyPolicy, stapledOCSPResponse); 765 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { 766 rv = BuildCertChain( 767 trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, 768 KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection, 769 CertPolicyId::anyPolicy, stapledOCSPResponse); 770 } 771 if (madeOCSPRequests) { 772 *madeOCSPRequests |= 773 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 774 } 775 break; 776 } 777 778 case VerifyUsage::EmailRecipient: { 779 // TODO: The higher level S/MIME processing should pass in which key 780 // usage it is trying to verify for, and base its algorithm choices 781 // based on the result of the verification(s). 782 NSSCertDBTrustDomain trustDomain( 783 trustEmail, defaultRevCheckMode, mOCSPCache, mSignatureCache.get(), 784 mTrustCache.get(), pinArg, mOCSPTimeoutSoft, mOCSPTimeoutHard, 785 mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, mCRLiteMode, 786 originAttributes, mThirdPartyRootInputs, 787 mThirdPartyIntermediateInputs, extraCertificates, sctsFromTLSInput, 788 mCTVerifier, builtChain, nullptr, nullptr); 789 rv = BuildCertChain(trustDomain, certDER, time, 790 EndEntityOrCA::MustBeEndEntity, 791 KeyUsage::keyEncipherment, // RSA 792 KeyPurposeId::id_kp_emailProtection, 793 CertPolicyId::anyPolicy, stapledOCSPResponse); 794 if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { 795 rv = BuildCertChain(trustDomain, certDER, time, 796 EndEntityOrCA::MustBeEndEntity, 797 KeyUsage::keyAgreement, // ECDH/DH 798 KeyPurposeId::id_kp_emailProtection, 799 CertPolicyId::anyPolicy, stapledOCSPResponse); 800 } 801 if (madeOCSPRequests) { 802 *madeOCSPRequests |= 803 trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; 804 } 805 break; 806 } 807 808 default: 809 rv = Result::FATAL_ERROR_INVALID_ARGS; 810 } 811 812 if (rv != Success) { 813 return rv; 814 } 815 816 return Success; 817 } 818 819 static bool CertIsSelfSigned(const BackCert& backCert, void* pinarg) { 820 if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) { 821 return false; 822 } 823 824 nsTArray<Span<const uint8_t>> emptyCertList; 825 // AppTrustDomain is only used for its signature verification callbacks 826 // (AppTrustDomain::Verify{ECDSA,RSAPKCS1,RSAPSS}SignedData). 827 mozilla::psm::AppTrustDomain trustDomain(std::move(emptyCertList)); 828 Result rv = VerifySignedData(trustDomain, backCert.GetSignedData(), 829 backCert.GetSubjectPublicKeyInfo()); 830 return rv == Success; 831 } 832 833 static Result CheckCertHostnameHelper(Input peerCertInput, 834 const nsACString& hostname, 835 bool rootIsBuiltIn) { 836 Input hostnameInput; 837 Result rv = hostnameInput.Init( 838 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()), 839 hostname.Length()); 840 if (rv != Success) { 841 return Result::FATAL_ERROR_INVALID_ARGS; 842 } 843 844 SkipInvalidSANsForNonBuiltInRootsPolicy nameMatchingPolicy(rootIsBuiltIn); 845 rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy); 846 // Treat malformed name information as a domain mismatch. 847 if (rv == Result::ERROR_BAD_DER) { 848 return Result::ERROR_BAD_CERT_DOMAIN; 849 } 850 return rv; 851 } 852 853 Result CertVerifier::VerifySSLServerCert( 854 const nsTArray<uint8_t>& peerCertBytes, Time time, 855 /*optional*/ void* pinarg, const nsACString& hostname, 856 /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, 857 /*optional*/ Flags flags, 858 /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, 859 /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse, 860 /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, 861 /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo, 862 /*optional*/ const OriginAttributes& originAttributes, 863 /*optional out*/ EVStatus* evStatus, 864 /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, 865 /*optional out*/ KeySizeStatus* keySizeStatus, 866 /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, 867 /*optional out*/ CertificateTransparencyInfo* ctInfo, 868 /*optional out*/ bool* isBuiltChainRootBuiltInRoot, 869 /*optional out*/ bool* madeOCSPRequests, 870 /*optional out*/ IssuerSources* issuerSources) { 871 // XXX: MOZ_ASSERT(pinarg); 872 MOZ_ASSERT(!hostname.IsEmpty()); 873 874 if (isBuiltChainRootBuiltInRoot) { 875 *isBuiltChainRootBuiltInRoot = false; 876 } 877 878 if (evStatus) { 879 *evStatus = EVStatus::NotEV; 880 } 881 882 if (hostname.IsEmpty()) { 883 return Result::FATAL_ERROR_INVALID_ARGS; 884 } 885 886 // CreateCertErrorRunnable assumes that CheckCertHostname is only called 887 // if VerifyCert succeeded. 888 Input peerCertInput; 889 Result rv = 890 peerCertInput.Init(peerCertBytes.Elements(), peerCertBytes.Length()); 891 if (rv != Success) { 892 return rv; 893 } 894 895 bool errOnionWithSelfSignedCert = false; 896 897 bool isBuiltChainRootBuiltInRootLocal; 898 rv = VerifyCert( 899 peerCertBytes, VerifyUsage::TLSServer, time, pinarg, 900 PromiseFlatCString(hostname).get(), builtChain, flags, extraCertificates, 901 stapledOCSPResponse, sctsFromTLS, originAttributes, evStatus, 902 ocspStaplingStatus, keySizeStatus, pinningTelemetryInfo, ctInfo, 903 &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests, issuerSources); 904 if (rv != Success) { 905 // we don't use the certificate for path building, so this parameter doesn't 906 // matter 907 EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity; 908 BackCert peerBackCert(peerCertInput, notUsedForPaths, nullptr); 909 if (peerBackCert.Init() != Success) { 910 return rv; 911 } 912 if ((rv == Result::ERROR_UNKNOWN_ISSUER || 913 rv == Result::ERROR_BAD_SIGNATURE || 914 rv == Result::ERROR_INADEQUATE_KEY_USAGE) && 915 CertIsSelfSigned(peerBackCert, pinarg)) { 916 // In this case we didn't find any issuer for the certificate, or we did 917 // find other certificates with the same subject but different keys, and 918 // the certificate is self-signed. 919 if (StringEndsWith(hostname, ".onion"_ns)) { 920 // Self signed cert over onion is deemed secure in some cases, as the 921 // onion service provides encryption. 922 // Firefox treats some errors as self-signed certificates and it allows 923 // to override them. For Onion services, we prefer being stricter, and 924 // we return the original errors. 925 // Moreover, we need also to determine if there are other legitimate 926 // certificate errors (such as expired, wrong domain) that we would like 927 // to surface to the user. 928 errOnionWithSelfSignedCert = rv == Result::ERROR_UNKNOWN_ISSUER; 929 } else { 930 return Result::ERROR_SELF_SIGNED_CERT; 931 } 932 } 933 if (rv == Result::ERROR_UNKNOWN_ISSUER && !errOnionWithSelfSignedCert) { 934 // In this case we didn't get any valid path for the cert. Let's see if 935 // the issuer is the same as the issuer for our canary probe. If yes, this 936 // connection is connecting via a misconfigured proxy. 937 // Note: The MitM canary might not be set. In this case we consider this 938 // an unknown issuer error. 939 nsCOMPtr<nsINSSComponent> component( 940 do_GetService(PSM_COMPONENT_CONTRACTID)); 941 if (!component) { 942 return Result::FATAL_ERROR_LIBRARY_FAILURE; 943 } 944 // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and 945 // the feature is enabled. 946 Input issuerNameInput = peerBackCert.GetIssuer(); 947 SECItem issuerNameItem = UnsafeMapInputToSECItem(issuerNameInput); 948 UniquePORTString issuerName(CERT_DerNameToAscii(&issuerNameItem)); 949 if (!issuerName) { 950 return Result::ERROR_BAD_DER; 951 } 952 nsresult rv = component->IssuerMatchesMitmCanary(issuerName.get()); 953 if (NS_SUCCEEDED(rv)) { 954 return Result::ERROR_MITM_DETECTED; 955 } 956 } 957 // If the certificate is expired or not yet valid, first check whether or 958 // not it is valid for the indicated hostname, because that would be a more 959 // serious error. 960 if (rv == Result::ERROR_EXPIRED_CERTIFICATE || 961 rv == Result::ERROR_NOT_YET_VALID_CERTIFICATE || 962 rv == Result::ERROR_INVALID_DER_TIME) { 963 Result hostnameResult = 964 CheckCertHostnameHelper(peerCertInput, hostname, false); 965 if (hostnameResult != Success) { 966 return hostnameResult; 967 } 968 } 969 if (!errOnionWithSelfSignedCert) { 970 return rv; 971 } 972 } 973 974 if (dcInfo) { 975 rv = IsDelegatedCredentialAcceptable(*dcInfo); 976 if (rv != Success) { 977 return rv; 978 } 979 } 980 981 Input stapledOCSPResponseInput; 982 Input* responseInputPtr = nullptr; 983 if (stapledOCSPResponse) { 984 rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(), 985 stapledOCSPResponse->Length()); 986 if (rv != Success) { 987 // The stapled OCSP response was too big. 988 return Result::ERROR_OCSP_MALFORMED_RESPONSE; 989 } 990 responseInputPtr = &stapledOCSPResponseInput; 991 } 992 993 if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) { 994 rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr); 995 if (rv != Success) { 996 return rv; 997 } 998 } 999 1000 rv = CheckCertHostnameHelper(peerCertInput, hostname, 1001 isBuiltChainRootBuiltInRootLocal); 1002 if ((rv == Success || rv == Result::ERROR_BAD_CERT_DOMAIN) && 1003 isBuiltChainRootBuiltInRoot) { 1004 *isBuiltChainRootBuiltInRoot = isBuiltChainRootBuiltInRootLocal; 1005 } 1006 if (rv != Success) { 1007 return rv; 1008 } 1009 1010 if (errOnionWithSelfSignedCert) { 1011 return Result::ERROR_ONION_WITH_SELF_SIGNED_CERT; 1012 } 1013 return Success; 1014 } 1015 1016 // Take the (data, signature, subjectPublicKeyInfo, publicKeyAlgorithm, 1017 // digestAlgorithm) tuple that defines a signature and derive a hash that 1018 // uniquely identifies it. This is done by prefixing each variable-length 1019 // component (data, signature, and subjectPublicKeyInfo) with 1020 // sizeof(pkix::Input::size_type) bytes (currently 2) indicating the length of 1021 // that component and concatenating them together, followed by one byte for the 1022 // digestAlgorithm. The concatenation is then hashed with sha512. 1023 // It should be computationally infeasible to find two distinct sets of inputs 1024 // that have the same sha512 hash (and if it were possible, then it would be 1025 // possible to break the signature scheme itself). 1026 void HashSignatureParams(pkix::Input data, pkix::Input signature, 1027 pkix::Input subjectPublicKeyInfo, 1028 pkix::der::PublicKeyAlgorithm publicKeyAlgorithm, 1029 pkix::DigestAlgorithm digestAlgorithm, 1030 /*out*/ Maybe<nsTArray<uint8_t>>& sha512Hash) { 1031 sha512Hash.reset(); 1032 Digest digest; 1033 if (NS_FAILED(digest.Begin(SEC_OID_SHA512))) { 1034 return; 1035 } 1036 pkix::Input::size_type dataLength = data.GetLength(); 1037 if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&dataLength), 1038 sizeof(dataLength)))) { 1039 return; 1040 } 1041 if (NS_FAILED(digest.Update(data.UnsafeGetData(), dataLength))) { 1042 return; 1043 } 1044 pkix::Input::size_type signatureLength = signature.GetLength(); 1045 if (NS_FAILED( 1046 digest.Update(reinterpret_cast<const uint8_t*>(&signatureLength), 1047 sizeof(signatureLength)))) { 1048 return; 1049 } 1050 if (NS_FAILED(digest.Update(signature.UnsafeGetData(), signatureLength))) { 1051 return; 1052 } 1053 pkix::Input::size_type spkiLength = subjectPublicKeyInfo.GetLength(); 1054 if (NS_FAILED(digest.Update(reinterpret_cast<const uint8_t*>(&spkiLength), 1055 sizeof(spkiLength)))) { 1056 return; 1057 } 1058 if (NS_FAILED( 1059 digest.Update(subjectPublicKeyInfo.UnsafeGetData(), spkiLength))) { 1060 return; 1061 } 1062 if (NS_FAILED( 1063 digest.Update(reinterpret_cast<const uint8_t*>(&publicKeyAlgorithm), 1064 sizeof(publicKeyAlgorithm)))) { 1065 return; 1066 } 1067 if (NS_FAILED( 1068 digest.Update(reinterpret_cast<const uint8_t*>(&digestAlgorithm), 1069 sizeof(digestAlgorithm)))) { 1070 return; 1071 } 1072 nsTArray<uint8_t> result; 1073 if (NS_FAILED(digest.End(result))) { 1074 return; 1075 } 1076 sha512Hash.emplace(std::move(result)); 1077 } 1078 1079 Result VerifySignedDataWithCache( 1080 der::PublicKeyAlgorithm publicKeyAlg, 1081 mozilla::glean::impl::DenominatorMetric telemetryDenominator, 1082 mozilla::glean::impl::NumeratorMetric telemetryNumerator, Input data, 1083 DigestAlgorithm digestAlgorithm, Input signature, 1084 Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg) { 1085 telemetryDenominator.Add(1); 1086 Maybe<nsTArray<uint8_t>> sha512Hash; 1087 HashSignatureParams(data, signature, subjectPublicKeyInfo, publicKeyAlg, 1088 digestAlgorithm, sha512Hash); 1089 // If hashing the signature parameters succeeded, see if this signature is in 1090 // the signature cache. 1091 if (sha512Hash.isSome() && 1092 signature_cache_get(signatureCache, sha512Hash.ref().Elements())) { 1093 telemetryNumerator.AddToNumerator(1); 1094 return Success; 1095 } 1096 Result result; 1097 switch (publicKeyAlg) { 1098 case der::PublicKeyAlgorithm::ECDSA: 1099 result = VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, 1100 subjectPublicKeyInfo, pinArg); 1101 break; 1102 case der::PublicKeyAlgorithm::RSA_PKCS1: 1103 result = VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, 1104 subjectPublicKeyInfo, pinArg); 1105 break; 1106 case der::PublicKeyAlgorithm::RSA_PSS: 1107 result = VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, 1108 subjectPublicKeyInfo, pinArg); 1109 break; 1110 default: 1111 MOZ_ASSERT_UNREACHABLE("unhandled public key algorithm"); 1112 return Result::FATAL_ERROR_LIBRARY_FAILURE; 1113 } 1114 // Add this signature to the signature cache. 1115 if (sha512Hash.isSome() && result == Success) { 1116 signature_cache_insert(signatureCache, sha512Hash.ref().Elements()); 1117 } 1118 return result; 1119 } 1120 1121 } // namespace psm 1122 } // namespace mozilla