tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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(), &notBefore, &notAfter);
    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