diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /security/certverifier | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/certverifier')
-rw-r--r-- | security/certverifier/CRLiteTimestamp.h | 33 | ||||
-rw-r--r-- | security/certverifier/CertVerifier.cpp | 943 | ||||
-rw-r--r-- | security/certverifier/CertVerifier.h | 248 | ||||
-rw-r--r-- | security/certverifier/ExtendedValidation.cpp | 1393 | ||||
-rw-r--r-- | security/certverifier/ExtendedValidation.h | 43 | ||||
-rw-r--r-- | security/certverifier/NSSCertDBTrustDomain.cpp | 2029 | ||||
-rw-r--r-- | security/certverifier/NSSCertDBTrustDomain.h | 329 | ||||
-rw-r--r-- | security/certverifier/OCSPCache.cpp | 352 | ||||
-rw-r--r-- | security/certverifier/OCSPCache.h | 136 | ||||
-rw-r--r-- | security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc | 307 | ||||
-rw-r--r-- | security/certverifier/TrustOverride-SymantecData.inc | 164 | ||||
-rw-r--r-- | security/certverifier/TrustOverrideUtils.h | 149 | ||||
-rw-r--r-- | security/certverifier/moz.build | 54 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/TrustOverrideTest.cpp | 231 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/moz.build | 18 |
15 files changed, 6429 insertions, 0 deletions
diff --git a/security/certverifier/CRLiteTimestamp.h b/security/certverifier/CRLiteTimestamp.h new file mode 100644 index 0000000000..56b816eb7d --- /dev/null +++ b/security/certverifier/CRLiteTimestamp.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CRLiteTimestamp_h +#define CRLiteTimestamp_h + +#include "nsICertStorage.h" +#include "SignedCertificateTimestamp.h" + +namespace mozilla::psm { + +class CRLiteTimestamp final : public nsICRLiteTimestamp { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSICRLITETIMESTAMP + + CRLiteTimestamp() : mTimestamp(0) {} + explicit CRLiteTimestamp(const ct::SignedCertificateTimestamp& sct) + : mLogID(Span(sct.logId)), mTimestamp(sct.timestamp) {} + + private: + ~CRLiteTimestamp() = default; + + nsTArray<uint8_t> mLogID; + uint64_t mTimestamp; +}; + +} // namespace mozilla::psm + +#endif // CRLiteTimestamp_h diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp new file mode 100644 index 0000000000..ddf611a6ec --- /dev/null +++ b/security/certverifier/CertVerifier.cpp @@ -0,0 +1,943 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CertVerifier.h" + +#include <stdint.h> + +#include "AppTrustDomain.h" +#include "CTDiversityPolicy.h" +#include "CTKnownLogs.h" +#include "CTLogVerifier.h" +#include "ExtendedValidation.h" +#include "MultiLogCTVerifier.h" +#include "NSSCertDBTrustDomain.h" +#include "NSSErrorsService.h" +#include "cert.h" +#include "mozilla/Assertions.h" +#include "mozilla/Casting.h" +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Logging.h" +#include "nsNSSComponent.h" +#include "mozilla/SyncRunnable.h" +#include "nsPromiseFlatString.h" +#include "nsServiceManagerUtils.h" +#include "pk11pub.h" +#include "mozpkix/pkix.h" +#include "mozpkix/pkixcheck.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixutil.h" +#include "secmod.h" +#include "nsNetCID.h" + +using namespace mozilla::ct; +using namespace mozilla::pkix; +using namespace mozilla::psm; + +mozilla::LazyLogModule gCertVerifierLog("certverifier"); + +// Returns the certificate validity period in calendar months (rounded down). +// "extern" to allow unit tests in CTPolicyEnforcerTest.cpp. +extern mozilla::pkix::Result GetCertLifetimeInFullMonths(Time certNotBefore, + Time certNotAfter, + size_t& months) { + if (certNotBefore >= certNotAfter) { + MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter"); + return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS; + } + uint64_t notBeforeSeconds; + Result rv = SecondsSinceEpochFromTime(certNotBefore, ¬BeforeSeconds); + if (rv != Success) { + return rv; + } + uint64_t notAfterSeconds; + rv = SecondsSinceEpochFromTime(certNotAfter, ¬AfterSeconds); + if (rv != Success) { + return rv; + } + // PRTime is microseconds + PRTime notBeforePR = static_cast<PRTime>(notBeforeSeconds) * 1000000; + PRTime notAfterPR = static_cast<PRTime>(notAfterSeconds) * 1000000; + + PRExplodedTime explodedNotBefore; + PRExplodedTime explodedNotAfter; + + PR_ExplodeTime(notBeforePR, PR_LocalTimeParameters, &explodedNotBefore); + PR_ExplodeTime(notAfterPR, PR_LocalTimeParameters, &explodedNotAfter); + + PRInt32 signedMonths = + (explodedNotAfter.tm_year - explodedNotBefore.tm_year) * 12 + + (explodedNotAfter.tm_month - explodedNotBefore.tm_month); + if (explodedNotAfter.tm_mday < explodedNotBefore.tm_mday) { + --signedMonths; + } + + // Can't use `mozilla::AssertedCast<size_t>(signedMonths)` below + // since it currently generates a warning on Win x64 debug. + if (signedMonths < 0) { + MOZ_ASSERT_UNREACHABLE("Expected explodedNotBefore < explodedNotAfter"); + return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE; + } + months = static_cast<size_t>(signedMonths); + + return Success; +} + +namespace mozilla { +namespace psm { + +const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1; +const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2; +const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4; +static const unsigned int MIN_RSA_BITS = 2048; +static const unsigned int MIN_RSA_BITS_WEAK = 1024; + +void CertificateTransparencyInfo::Reset() { + enabled = false; + verifyResult.Reset(); + policyCompliance = CTPolicyCompliance::Unknown; +} + +CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, + mozilla::TimeDuration ocspTimeoutSoft, + mozilla::TimeDuration ocspTimeoutHard, + uint32_t certShortLifetimeInDays, + NetscapeStepUpPolicy netscapeStepUpPolicy, + CertificateTransparencyMode ctMode, + CRLiteMode crliteMode, + const Vector<EnterpriseCert>& thirdPartyCerts) + : mOCSPDownloadConfig(odc), + mOCSPStrict(osc == ocspStrict), + mOCSPTimeoutSoft(ocspTimeoutSoft), + mOCSPTimeoutHard(ocspTimeoutHard), + mCertShortLifetimeInDays(certShortLifetimeInDays), + mNetscapeStepUpPolicy(netscapeStepUpPolicy), + mCTMode(ctMode), + mCRLiteMode(crliteMode) { + LoadKnownCTLogs(); + for (const auto& root : thirdPartyCerts) { + EnterpriseCert rootCopy; + // Best-effort. If we run out of memory, users might see untrusted issuer + // errors, but the browser will probably crash before then. + if (NS_SUCCEEDED(rootCopy.Init(root))) { + Unused << mThirdPartyCerts.append(std::move(rootCopy)); + } + } + for (const auto& root : mThirdPartyCerts) { + Input input; + if (root.GetInput(input) == Success) { + // mThirdPartyCerts consists of roots and intermediates. + if (root.GetIsRoot()) { + // Best effort again. + Unused << mThirdPartyRootInputs.append(input); + } else { + Unused << mThirdPartyIntermediateInputs.append(input); + } + } + } +} + +CertVerifier::~CertVerifier() = default; + +Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo) { + bool isEcdsa = dcInfo.scheme == ssl_sig_ecdsa_secp256r1_sha256 || + dcInfo.scheme == ssl_sig_ecdsa_secp384r1_sha384 || + dcInfo.scheme == ssl_sig_ecdsa_secp521r1_sha512; + + // Firefox currently does not advertise any RSA schemes for use + // with Delegated Credentials. As a secondary (on top of NSS) + // check, disallow any RSA SPKI here. When ssl_sig_rsa_pss_pss_* + // schemes are supported, check the modulus size and allow RSA here. + if (!isEcdsa) { + return Result::ERROR_INVALID_KEY; + } + + return Result::Success; +} + +// The term "builtin root" traditionally refers to a root CA certificate that +// has been added to the NSS trust store, because it has been approved +// for inclusion according to the Mozilla CA policy, and might be accepted +// by Mozilla applications as an issuer for certificates seen on the public web. +Result IsCertBuiltInRoot(Input certInput, bool& result) { + result = false; + + if (NS_FAILED(BlockUntilLoadableCertsLoaded())) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + +#ifdef DEBUG + nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID)); + if (!component) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsTArray<uint8_t> certBytes; + certBytes.AppendElements(certInput.UnsafeGetData(), certInput.GetLength()); + if (NS_FAILED(component->IsCertTestBuiltInRoot(certBytes, &result))) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (result) { + return Success; + } +#endif // DEBUG + SECItem certItem(UnsafeMapInputToSECItem(certInput)); + AutoSECMODListReadLock lock; + for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list; + list = list->next) { + for (int i = 0; i < list->module->slotCount; i++) { + PK11SlotInfo* slot = list->module->slots[i]; + // We're searching for the "builtin root module", which is a module that + // contains an object with a CKA_CLASS of CKO_NETSCAPE_BUILTIN_ROOT_LIST. + // We use PK11_HasRootCerts() to identify a module with that property. + // In the past, we exclusively used the PKCS#11 module named nssckbi, + // which is provided by the NSS library. + // Nowadays, some distributions use a replacement module, which contains + // the builtin roots, but which also contains additional CA certificates, + // such as CAs trusted in a local deployment. + // We want to be able to distinguish between these two categories, + // because a CA, which may issue certificates for the public web, + // is expected to comply with additional requirements. + // If the certificate has attribute CKA_NSS_MOZILLA_CA_POLICY set to true, + // then we treat it as a "builtin root". + if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) { + continue; + } + CK_OBJECT_HANDLE handle = + PK11_FindEncodedCertInSlot(slot, &certItem, nullptr); + if (handle == CK_INVALID_HANDLE) { + continue; + } + if (PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY, + false)) { + // Attribute was found, and is set to true + result = true; + break; + } + } + } + return Success; +} + +static Result BuildCertChainForOneKeyUsage( + NSSCertDBTrustDomain& trustDomain, Input certDER, Time time, KeyUsage ku1, + KeyUsage ku2, KeyUsage ku3, KeyPurposeId eku, + const CertPolicyId& requiredPolicy, const Input* stapledOCSPResponse, + /*optional out*/ CertVerifier::OCSPStaplingStatus* ocspStaplingStatus) { + trustDomain.ResetAccumulatedState(); + Result rv = + BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, + ku1, eku, requiredPolicy, stapledOCSPResponse); + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { + trustDomain.ResetAccumulatedState(); + rv = BuildCertChain(trustDomain, certDER, time, + EndEntityOrCA::MustBeEndEntity, ku2, eku, + requiredPolicy, stapledOCSPResponse); + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { + trustDomain.ResetAccumulatedState(); + rv = BuildCertChain(trustDomain, certDER, time, + EndEntityOrCA::MustBeEndEntity, ku3, eku, + requiredPolicy, stapledOCSPResponse); + if (rv != Success) { + rv = Result::ERROR_INADEQUATE_KEY_USAGE; + } + } + } + if (ocspStaplingStatus) { + *ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus(); + } + return rv; +} + +void CertVerifier::LoadKnownCTLogs() { + if (mCTMode == CertificateTransparencyMode::Disabled) { + return; + } + mCTVerifier = MakeUnique<MultiLogCTVerifier>(); + for (const CTLogInfo& log : kCTLogList) { + Input publicKey; + Result rv = publicKey.Init( + BitwiseCast<const uint8_t*, const char*>(log.key), log.keyLength); + if (rv != Success) { + MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log"); + continue; + } + + CTLogVerifier logVerifier; + const CTLogOperatorInfo& logOperator = + kCTLogOperatorList[log.operatorIndex]; + rv = logVerifier.Init(publicKey, logOperator.id, log.status, + log.disqualificationTime); + if (rv != Success) { + MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log"); + continue; + } + + mCTVerifier->AddLog(std::move(logVerifier)); + } + // TBD: Initialize mCTDiversityPolicy with the CA dependency map + // of the known CT logs operators. + mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>(); +} + +Result CertVerifier::VerifyCertificateTransparencyPolicy( + NSSCertDBTrustDomain& trustDomain, + const nsTArray<nsTArray<uint8_t>>& builtChain, Input sctsFromTLS, Time time, + /*optional out*/ CertificateTransparencyInfo* ctInfo) { + if (ctInfo) { + ctInfo->Reset(); + } + if (mCTMode == CertificateTransparencyMode::Disabled) { + return Success; + } + if (ctInfo) { + ctInfo->enabled = true; + } + + if (builtChain.IsEmpty()) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + Input embeddedSCTs = trustDomain.GetSCTListFromCertificate(); + if (embeddedSCTs.GetLength() > 0) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("Got embedded SCT data of length %zu\n", + static_cast<size_t>(embeddedSCTs.GetLength()))); + } + Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling(); + if (sctsFromOCSP.GetLength() > 0) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("Got OCSP SCT data of length %zu\n", + static_cast<size_t>(sctsFromOCSP.GetLength()))); + } + if (sctsFromTLS.GetLength() > 0) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("Got TLS SCT data of length %zu\n", + static_cast<size_t>(sctsFromTLS.GetLength()))); + } + + if (builtChain.Length() == 1) { + // Issuer certificate is required for SCT verification. + // If we've arrived here, we probably have a "trust chain" with only one + // certificate (i.e. a self-signed end-entity that has been set as a trust + // anchor either by a third party modifying our trust DB or via the + // enterprise roots feature). If this is the case, certificate transparency + // information will probably not be present, and it certainly won't verify + // correctly. To simplify things, we return an empty CTVerifyResult and a + // "not enough SCTs" CTPolicyCompliance result. + if (ctInfo) { + CTVerifyResult emptyResult; + ctInfo->verifyResult = std::move(emptyResult); + ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts; + } + return Success; + } + + const nsTArray<uint8_t>& endEntityBytes = builtChain.ElementAt(0); + Input endEntityInput; + Result rv = + endEntityInput.Init(endEntityBytes.Elements(), endEntityBytes.Length()); + if (rv != Success) { + return rv; + } + + const nsTArray<uint8_t>& issuerBytes = builtChain.ElementAt(1); + Input issuerInput; + rv = issuerInput.Init(issuerBytes.Elements(), issuerBytes.Length()); + if (rv != Success) { + return rv; + } + + BackCert issuerBackCert(issuerInput, EndEntityOrCA::MustBeCA, nullptr); + rv = issuerBackCert.Init(); + if (rv != Success) { + return rv; + } + Input issuerPublicKeyInput = issuerBackCert.GetSubjectPublicKeyInfo(); + + CTVerifyResult result; + rv = mCTVerifier->Verify(endEntityInput, issuerPublicKeyInput, embeddedSCTs, + sctsFromOCSP, sctsFromTLS, time, result); + if (rv != Success) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("SCT verification failed with fatal error %" PRId32 "\n", + static_cast<uint32_t>(rv))); + return rv; + } + + if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) { + size_t validCount = 0; + size_t unknownLogCount = 0; + size_t disqualifiedLogCount = 0; + size_t invalidSignatureCount = 0; + size_t invalidTimestampCount = 0; + for (const VerifiedSCT& verifiedSct : result.verifiedScts) { + switch (verifiedSct.status) { + case VerifiedSCT::Status::Valid: + validCount++; + break; + case VerifiedSCT::Status::ValidFromDisqualifiedLog: + disqualifiedLogCount++; + break; + case VerifiedSCT::Status::UnknownLog: + unknownLogCount++; + break; + case VerifiedSCT::Status::InvalidSignature: + invalidSignatureCount++; + break; + case VerifiedSCT::Status::InvalidTimestamp: + invalidTimestampCount++; + break; + case VerifiedSCT::Status::None: + default: + MOZ_ASSERT_UNREACHABLE("Unexpected SCT verification status"); + } + } + MOZ_LOG( + gCertVerifierLog, LogLevel::Debug, + ("SCT verification result: " + "valid=%zu unknownLog=%zu disqualifiedLog=%zu " + "invalidSignature=%zu invalidTimestamp=%zu " + "decodingErrors=%zu\n", + validCount, unknownLogCount, disqualifiedLogCount, + invalidSignatureCount, invalidTimestampCount, result.decodingErrors)); + } + + BackCert endEntityBackCert(endEntityInput, EndEntityOrCA::MustBeEndEntity, + nullptr); + rv = endEntityBackCert.Init(); + if (rv != Success) { + return rv; + } + Time notBefore(Time::uninitialized); + Time notAfter(Time::uninitialized); + rv = ParseValidity(endEntityBackCert.GetValidity(), ¬Before, ¬After); + if (rv != Success) { + return rv; + } + size_t lifetimeInMonths; + rv = GetCertLifetimeInFullMonths(notBefore, notAfter, lifetimeInMonths); + if (rv != Success) { + return rv; + } + + CTLogOperatorList allOperators; + GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators); + + CTLogOperatorList dependentOperators; + rv = mCTDiversityPolicy->GetDependentOperators(builtChain, allOperators, + dependentOperators); + if (rv != Success) { + return rv; + } + + CTPolicyEnforcer ctPolicyEnforcer; + CTPolicyCompliance ctPolicyCompliance; + ctPolicyEnforcer.CheckCompliance(result.verifiedScts, lifetimeInMonths, + dependentOperators, ctPolicyCompliance); + + if (ctInfo) { + ctInfo->verifyResult = std::move(result); + ctInfo->policyCompliance = ctPolicyCompliance; + } + return Success; +} + +Result CertVerifier::VerifyCert( + const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage, Time time, + void* pinArg, const char* hostname, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, + /*optional*/ const Flags flags, + /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, + /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg, + /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, + /*optional*/ const OriginAttributes& originAttributes, + /*optional out*/ EVStatus* evStatus, + /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, + /*optional out*/ KeySizeStatus* keySizeStatus, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, + /*optional out*/ CertificateTransparencyInfo* ctInfo, + /*optional out*/ bool* isBuiltChainRootBuiltInRoot, + /*optional out*/ bool* madeOCSPRequests) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n")); + + MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV)); + MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus); + + if (NS_FAILED(BlockUntilLoadableCertsLoaded())) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (NS_FAILED(CheckForSmartCardChanges())) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + if (evStatus) { + *evStatus = EVStatus::NotEV; + } + if (ocspStaplingStatus) { + if (usage != certificateUsageSSLServer) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED; + } + + if (keySizeStatus) { + if (usage != certificateUsageSSLServer) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + *keySizeStatus = KeySizeStatus::NeverChecked; + } + + if (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV)) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + if (isBuiltChainRootBuiltInRoot) { + *isBuiltChainRootBuiltInRoot = false; + } + + if (madeOCSPRequests) { + *madeOCSPRequests = false; + } + + Input certDER; + Result rv = certDER.Init(certBytes.Elements(), certBytes.Length()); + if (rv != Success) { + return rv; + } + + // We configure the OCSP fetching modes separately for EV and non-EV + // verifications. + NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching = + (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) || + (flags & FLAG_LOCAL_ONLY) + ? NSSCertDBTrustDomain::NeverFetchOCSP + : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail + : NSSCertDBTrustDomain::FetchOCSPForDVHardFail; + + Input stapledOCSPResponseInput; + const Input* stapledOCSPResponse = nullptr; + if (stapledOCSPResponseArg) { + rv = stapledOCSPResponseInput.Init(stapledOCSPResponseArg->Elements(), + stapledOCSPResponseArg->Length()); + if (rv != Success) { + // The stapled OCSP response was too big. + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + stapledOCSPResponse = &stapledOCSPResponseInput; + } + + Input sctsFromTLSInput; + if (sctsFromTLS) { + rv = sctsFromTLSInput.Init(sctsFromTLS->Elements(), sctsFromTLS->Length()); + if (rv != Success && sctsFromTLSInput.GetLength() != 0) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + } + + switch (usage) { + case certificateUsageSSLClient: { + // XXX: We don't really have a trust bit for SSL client authentication so + // just use trustEmail as it is the closest alternative. + NSSCertDBTrustDomain trustDomain( + trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, + ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch, + mCRLiteMode, originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr, + nullptr); + rv = BuildCertChain( + trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, + KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth, + CertPolicyId::anyPolicy, stapledOCSPResponse); + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + break; + } + + case certificateUsageSSLServer: { + // TODO: When verifying a certificate in an SSL handshake, we should + // restrict the acceptable key usage based on the key exchange method + // chosen by the server. + + // Try to validate for EV first. + NSSCertDBTrustDomain::OCSPFetching evOCSPFetching = + (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY) + ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV + : NSSCertDBTrustDomain::FetchOCSPForEV; + + nsTArray<CertPolicyId> evPolicies; + GetKnownEVPolicies(certBytes, evPolicies); + rv = Result::ERROR_UNKNOWN_ERROR; + for (const auto& evPolicy : evPolicies) { + NSSCertDBTrustDomain trustDomain( + trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS, + ValidityCheckingMode::CheckForEV, mNetscapeStepUpPolicy, + mCRLiteMode, originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, + pinningTelemetryInfo, hostname); + rv = BuildCertChainForOneKeyUsage( + trustDomain, certDER, time, + KeyUsage::digitalSignature, // (EC)DHE + KeyUsage::keyEncipherment, // RSA + KeyUsage::keyAgreement, // (EC)DH + KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse, + ocspStaplingStatus); + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + if (rv == Success) { + rv = VerifyCertificateTransparencyPolicy( + trustDomain, builtChain, sctsFromTLSInput, time, ctInfo); + } + if (rv == Success) { + if (evStatus) { + *evStatus = EVStatus::EV; + } + if (isBuiltChainRootBuiltInRoot) { + *isBuiltChainRootBuiltInRoot = + trustDomain.GetIsBuiltChainRootBuiltInRoot(); + } + break; + } + } + if (rv == Success) { + break; + } + if (flags & FLAG_MUST_BE_EV) { + rv = Result::ERROR_POLICY_VALIDATION_FAILED; + break; + } + + // Now try non-EV. + unsigned int keySizeOptions[] = {MIN_RSA_BITS, MIN_RSA_BITS_WEAK}; + + KeySizeStatus keySizeStatuses[] = {KeySizeStatus::LargeMinimumSucceeded, + KeySizeStatus::CompatibilityRisk}; + + static_assert( + MOZ_ARRAY_LENGTH(keySizeOptions) == MOZ_ARRAY_LENGTH(keySizeStatuses), + "keySize array lengths differ"); + + size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses); + + for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) { + // invalidate any telemetry info relating to failed chains + if (pinningTelemetryInfo) { + pinningTelemetryInfo->Reset(); + } + + NSSCertDBTrustDomain trustDomain( + trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, keySizeOptions[i], + ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy, + mCRLiteMode, originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, + pinningTelemetryInfo, hostname); + rv = BuildCertChainForOneKeyUsage( + trustDomain, certDER, time, + KeyUsage::digitalSignature, //(EC)DHE + KeyUsage::keyEncipherment, // RSA + KeyUsage::keyAgreement, //(EC)DH + KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, + stapledOCSPResponse, ocspStaplingStatus); + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + if (rv != Success && !IsFatalError(rv) && + rv != Result::ERROR_REVOKED_CERTIFICATE && + trustDomain.GetIsErrorDueToDistrustedCAPolicy()) { + // Bug 1444440 - If there are multiple paths, at least one to a CA + // distrusted-by-policy, and none of them ending in a trusted root, + // then we might show a different error (UNKNOWN_ISSUER) than we + // intend, confusing users. + rv = Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED; + } + if (rv == Success) { + rv = VerifyCertificateTransparencyPolicy( + trustDomain, builtChain, sctsFromTLSInput, time, ctInfo); + } + if (rv == Success) { + if (keySizeStatus) { + *keySizeStatus = keySizeStatuses[i]; + } + if (isBuiltChainRootBuiltInRoot) { + *isBuiltChainRootBuiltInRoot = + trustDomain.GetIsBuiltChainRootBuiltInRoot(); + } + break; + } + } + + if (rv != Success && keySizeStatus) { + *keySizeStatus = KeySizeStatus::AlreadyBad; + } + + break; + } + + case certificateUsageSSLCA: { + NSSCertDBTrustDomain trustDomain( + trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, + ValidityCheckingMode::CheckingOff, mNetscapeStepUpPolicy, mCRLiteMode, + originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr, + nullptr); + rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA, + KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth, + CertPolicyId::anyPolicy, stapledOCSPResponse); + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + break; + } + + case certificateUsageEmailSigner: { + NSSCertDBTrustDomain trustDomain( + trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, + ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch, + mCRLiteMode, originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr, + nullptr); + rv = BuildCertChain( + trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, + KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection, + CertPolicyId::anyPolicy, stapledOCSPResponse); + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { + rv = BuildCertChain( + trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, + KeyUsage::nonRepudiation, KeyPurposeId::id_kp_emailProtection, + CertPolicyId::anyPolicy, stapledOCSPResponse); + } + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + break; + } + + case certificateUsageEmailRecipient: { + // TODO: The higher level S/MIME processing should pass in which key + // usage it is trying to verify for, and base its algorithm choices + // based on the result of the verification(s). + NSSCertDBTrustDomain trustDomain( + trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft, + mOCSPTimeoutHard, mCertShortLifetimeInDays, MIN_RSA_BITS_WEAK, + ValidityCheckingMode::CheckingOff, NetscapeStepUpPolicy::NeverMatch, + mCRLiteMode, originAttributes, mThirdPartyRootInputs, + mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr, + nullptr); + rv = BuildCertChain(trustDomain, certDER, time, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::keyEncipherment, // RSA + KeyPurposeId::id_kp_emailProtection, + CertPolicyId::anyPolicy, stapledOCSPResponse); + if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { + rv = BuildCertChain(trustDomain, certDER, time, + EndEntityOrCA::MustBeEndEntity, + KeyUsage::keyAgreement, // ECDH/DH + KeyPurposeId::id_kp_emailProtection, + CertPolicyId::anyPolicy, stapledOCSPResponse); + } + if (madeOCSPRequests) { + *madeOCSPRequests |= + trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; + } + break; + } + + default: + rv = Result::FATAL_ERROR_INVALID_ARGS; + } + + if (rv != Success) { + return rv; + } + + return Success; +} + +static bool CertIsSelfSigned(const BackCert& backCert, void* pinarg) { + if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) { + return false; + } + + nsTArray<Span<const uint8_t>> emptyCertList; + // AppTrustDomain is only used for its signature verification callbacks + // (AppTrustDomain::Verify{ECDSA,RSAPKCS1,RSAPSS}SignedData). + mozilla::psm::AppTrustDomain trustDomain(std::move(emptyCertList)); + Result rv = VerifySignedData(trustDomain, backCert.GetSignedData(), + backCert.GetSubjectPublicKeyInfo()); + return rv == Success; +} + +static Result CheckCertHostnameHelper(Input peerCertInput, + const nsACString& hostname) { + Input hostnameInput; + Result rv = hostnameInput.Init( + BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()), + hostname.Length()); + if (rv != Success) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + rv = CheckCertHostname(peerCertInput, hostnameInput); + // Treat malformed name information as a domain mismatch. + if (rv == Result::ERROR_BAD_DER) { + return Result::ERROR_BAD_CERT_DOMAIN; + } + return rv; +} + +Result CertVerifier::VerifySSLServerCert( + const nsTArray<uint8_t>& peerCertBytes, Time time, + /*optional*/ void* pinarg, const nsACString& hostname, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, + /*optional*/ Flags flags, + /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, + /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse, + /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS, + /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo, + /*optional*/ const OriginAttributes& originAttributes, + /*optional out*/ EVStatus* evStatus, + /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, + /*optional out*/ KeySizeStatus* keySizeStatus, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, + /*optional out*/ CertificateTransparencyInfo* ctInfo, + /*optional out*/ bool* isBuiltChainRootBuiltInRoot, + /*optional out*/ bool* madeOCSPRequests) { + // XXX: MOZ_ASSERT(pinarg); + MOZ_ASSERT(!hostname.IsEmpty()); + + if (isBuiltChainRootBuiltInRoot) { + *isBuiltChainRootBuiltInRoot = false; + } + + if (evStatus) { + *evStatus = EVStatus::NotEV; + } + + if (hostname.IsEmpty()) { + return Result::ERROR_BAD_CERT_DOMAIN; + } + + // CreateCertErrorRunnable assumes that CheckCertHostname is only called + // if VerifyCert succeeded. + Input peerCertInput; + Result rv = + peerCertInput.Init(peerCertBytes.Elements(), peerCertBytes.Length()); + if (rv != Success) { + return rv; + } + bool isBuiltChainRootBuiltInRootLocal; + rv = VerifyCert(peerCertBytes, certificateUsageSSLServer, time, pinarg, + PromiseFlatCString(hostname).get(), builtChain, flags, + extraCertificates, stapledOCSPResponse, sctsFromTLS, + originAttributes, evStatus, ocspStaplingStatus, keySizeStatus, + pinningTelemetryInfo, ctInfo, + &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests); + if (rv != Success) { + // we don't use the certificate for path building, so this parameter doesn't + // matter + EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity; + BackCert peerBackCert(peerCertInput, notUsedForPaths, nullptr); + if (peerBackCert.Init() != Success) { + return rv; + } + if ((rv == Result::ERROR_UNKNOWN_ISSUER || + rv == Result::ERROR_BAD_SIGNATURE || + rv == Result::ERROR_INADEQUATE_KEY_USAGE) && + CertIsSelfSigned(peerBackCert, pinarg)) { + // In this case we didn't find any issuer for the certificate, or we did + // find other certificates with the same subject but different keys, and + // the certificate is self-signed. + return Result::ERROR_SELF_SIGNED_CERT; + } + if (rv == Result::ERROR_UNKNOWN_ISSUER) { + // In this case we didn't get any valid path for the cert. Let's see if + // the issuer is the same as the issuer for our canary probe. If yes, this + // connection is connecting via a misconfigured proxy. + // Note: The MitM canary might not be set. In this case we consider this + // an unknown issuer error. + nsCOMPtr<nsINSSComponent> component( + do_GetService(PSM_COMPONENT_CONTRACTID)); + if (!component) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + // IssuerMatchesMitmCanary succeeds if the issuer matches the canary and + // the feature is enabled. + Input issuerNameInput = peerBackCert.GetIssuer(); + SECItem issuerNameItem = UnsafeMapInputToSECItem(issuerNameInput); + UniquePORTString issuerName(CERT_DerNameToAscii(&issuerNameItem)); + if (!issuerName) { + return Result::ERROR_BAD_DER; + } + nsresult rv = component->IssuerMatchesMitmCanary(issuerName.get()); + if (NS_SUCCEEDED(rv)) { + return Result::ERROR_MITM_DETECTED; + } + } + // If the certificate is expired or not yet valid, first check whether or + // not it is valid for the indicated hostname, because that would be a more + // serious error. + if (rv == Result::ERROR_EXPIRED_CERTIFICATE || + rv == Result::ERROR_NOT_YET_VALID_CERTIFICATE || + rv == Result::ERROR_INVALID_DER_TIME) { + Result hostnameResult = CheckCertHostnameHelper(peerCertInput, hostname); + if (hostnameResult != Success) { + return hostnameResult; + } + } + return rv; + } + + if (dcInfo) { + rv = IsDelegatedCredentialAcceptable(*dcInfo); + if (rv != Success) { + return rv; + } + } + + Input stapledOCSPResponseInput; + Input* responseInputPtr = nullptr; + if (stapledOCSPResponse) { + rv = stapledOCSPResponseInput.Init(stapledOCSPResponse->Elements(), + stapledOCSPResponse->Length()); + if (rv != Success) { + // The stapled OCSP response was too big. + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + responseInputPtr = &stapledOCSPResponseInput; + } + + if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) { + rv = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr); + if (rv != Success) { + return rv; + } + } + + rv = CheckCertHostnameHelper(peerCertInput, hostname); + if (rv != Success) { + return rv; + } + + if (isBuiltChainRootBuiltInRoot) { + *isBuiltChainRootBuiltInRoot = isBuiltChainRootBuiltInRootLocal; + } + + return Success; +} + +} // namespace psm +} // namespace mozilla diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h new file mode 100644 index 0000000000..d03f81b62d --- /dev/null +++ b/security/certverifier/CertVerifier.h @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CertVerifier_h +#define CertVerifier_h + +#include "CTPolicyEnforcer.h" +#include "CTVerifyResult.h" +#include "EnterpriseRoots.h" +#include "OCSPCache.h" +#include "RootCertificateTelemetryUtils.h" +#include "ScopedNSSTypes.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "nsString.h" +#include "mozpkix/pkixtypes.h" +#include "sslt.h" + +#if defined(_MSC_VER) +# pragma warning(push) +// Silence "RootingAPI.h(718): warning C4324: 'js::DispatchWrapper<T>': +// structure was padded due to alignment specifier with [ T=void * ]" +# pragma warning(disable : 4324) +#endif /* defined(_MSC_VER) */ +#include "mozilla/BasePrincipal.h" +#if defined(_MSC_VER) +# pragma warning(pop) /* popping the pragma in this file */ +#endif /* defined(_MSC_VER) */ + +namespace mozilla { +namespace ct { + +// Including the headers of the classes below would bring along all of their +// dependent headers and force us to export them in moz.build. +// Just forward-declare the classes here instead. +class MultiLogCTVerifier; +class CTDiversityPolicy; + +} // namespace ct +} // namespace mozilla + +namespace mozilla { +namespace psm { + +typedef mozilla::pkix::Result Result; + +enum class EVStatus : uint8_t { + NotEV = 0, + EV = 1, +}; + +// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry. +enum class KeySizeStatus { + NeverChecked = 0, + LargeMinimumSucceeded = 1, + CompatibilityRisk = 2, + AlreadyBad = 3, +}; + +enum class CRLiteMode { + Disabled = 0, + TelemetryOnly = 1, + Enforce = 2, + ConfirmRevocations = 3, +}; + +enum class NetscapeStepUpPolicy : uint32_t; + +class PinningTelemetryInfo { + public: + PinningTelemetryInfo() + : certPinningResultBucket(0), rootBucket(ROOT_CERTIFICATE_UNKNOWN) { + Reset(); + } + + // Should we accumulate pinning telemetry for the result? + bool accumulateResult; + Maybe<Telemetry::HistogramID> certPinningResultHistogram; + int32_t certPinningResultBucket; + // Should we accumulate telemetry for the root? + bool accumulateForRoot; + int32_t rootBucket; + + void Reset() { + accumulateForRoot = false; + accumulateResult = false; + } +}; + +class CertificateTransparencyInfo { + public: + CertificateTransparencyInfo() + : enabled(false), + policyCompliance(mozilla::ct::CTPolicyCompliance::Unknown) { + Reset(); + } + + // Was CT enabled? + bool enabled; + // Verification result of the processed SCTs. + mozilla::ct::CTVerifyResult verifyResult; + // Connection compliance to the CT Policy. + mozilla::ct::CTPolicyCompliance policyCompliance; + + void Reset(); +}; + +class DelegatedCredentialInfo { + public: + DelegatedCredentialInfo() : scheme(ssl_sig_none), authKeyBits(0) {} + DelegatedCredentialInfo(SSLSignatureScheme scheme, uint32_t authKeyBits) + : scheme(scheme), authKeyBits(authKeyBits) {} + + // The signature scheme to be used in CertVerify. This tells us + // whether to interpret |authKeyBits| in an RSA or ECDSA context. + SSLSignatureScheme scheme; + + // The size of the key, in bits. + uint32_t authKeyBits; +}; + +class NSSCertDBTrustDomain; + +class CertVerifier { + public: + typedef unsigned int Flags; + // XXX: FLAG_LOCAL_ONLY is ignored in the classic verification case + static const Flags FLAG_LOCAL_ONLY; + // Don't perform fallback DV validation on EV validation failure. + static const Flags FLAG_MUST_BE_EV; + // TLS feature request_status should be ignored + static const Flags FLAG_TLS_IGNORE_STATUS_REQUEST; + + // These values correspond to the SSL_OCSP_STAPLING telemetry. + enum OCSPStaplingStatus { + OCSP_STAPLING_NEVER_CHECKED = 0, + OCSP_STAPLING_GOOD = 1, + OCSP_STAPLING_NONE = 2, + OCSP_STAPLING_EXPIRED = 3, + OCSP_STAPLING_INVALID = 4, + }; + + // *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV + // Only one usage per verification is supported. + mozilla::pkix::Result VerifyCert( + const nsTArray<uint8_t>& certBytes, SECCertificateUsage usage, + mozilla::pkix::Time time, void* pinArg, const char* hostname, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, Flags flags = 0, + /*optional in*/ + const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates = Nothing(), + /*optional in*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponseArg = + Nothing(), + /*optional in*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS = Nothing(), + /*optional in*/ const OriginAttributes& originAttributes = + OriginAttributes(), + /*optional out*/ EVStatus* evStatus = nullptr, + /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, + /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, + /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, + /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, + /*optional out*/ bool* madeOCSPRequests = nullptr); + + mozilla::pkix::Result VerifySSLServerCert( + const nsTArray<uint8_t>& peerCert, mozilla::pkix::Time time, void* pinarg, + const nsACString& hostname, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, + /*optional*/ Flags flags = 0, + /*optional*/ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates = + Nothing(), + /*optional*/ const Maybe<nsTArray<uint8_t>>& stapledOCSPResponse = + Nothing(), + /*optional*/ const Maybe<nsTArray<uint8_t>>& sctsFromTLS = Nothing(), + /*optional*/ const Maybe<DelegatedCredentialInfo>& dcInfo = Nothing(), + /*optional*/ const OriginAttributes& originAttributes = + OriginAttributes(), + /*optional out*/ EVStatus* evStatus = nullptr, + /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, + /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, + /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, + /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, + /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, + /*optional out*/ bool* madeOCSPRequests = nullptr); + + enum OcspDownloadConfig { ocspOff = 0, ocspOn = 1, ocspEVOnly = 2 }; + enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict }; + + enum class CertificateTransparencyMode { + Disabled = 0, + TelemetryOnly = 1, + }; + + CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, + mozilla::TimeDuration ocspTimeoutSoft, + mozilla::TimeDuration ocspTimeoutHard, + uint32_t certShortLifetimeInDays, + NetscapeStepUpPolicy netscapeStepUpPolicy, + CertificateTransparencyMode ctMode, CRLiteMode crliteMode, + const Vector<EnterpriseCert>& thirdPartyCerts); + ~CertVerifier(); + + void ClearOCSPCache() { mOCSPCache.Clear(); } + + const OcspDownloadConfig mOCSPDownloadConfig; + const bool mOCSPStrict; + const mozilla::TimeDuration mOCSPTimeoutSoft; + const mozilla::TimeDuration mOCSPTimeoutHard; + const uint32_t mCertShortLifetimeInDays; + const NetscapeStepUpPolicy mNetscapeStepUpPolicy; + const CertificateTransparencyMode mCTMode; + const CRLiteMode mCRLiteMode; + + private: + OCSPCache mOCSPCache; + // We keep a copy of the bytes of each third party root to own. + Vector<EnterpriseCert> mThirdPartyCerts; + // This is a reusable, precomputed list of Inputs corresponding to each root + // in mThirdPartyCerts that wasn't too long to make an Input out of. + Vector<mozilla::pkix::Input> mThirdPartyRootInputs; + // Similarly, but with intermediates. + Vector<mozilla::pkix::Input> mThirdPartyIntermediateInputs; + + // We only have a forward declarations of these classes (see above) + // so we must allocate dynamically. + UniquePtr<mozilla::ct::MultiLogCTVerifier> mCTVerifier; + UniquePtr<mozilla::ct::CTDiversityPolicy> mCTDiversityPolicy; + + void LoadKnownCTLogs(); + mozilla::pkix::Result VerifyCertificateTransparencyPolicy( + NSSCertDBTrustDomain& trustDomain, + const nsTArray<nsTArray<uint8_t>>& builtChain, + mozilla::pkix::Input sctsFromTLS, mozilla::pkix::Time time, + /*optional out*/ CertificateTransparencyInfo* ctInfo); +}; + +mozilla::pkix::Result IsCertBuiltInRoot(pkix::Input certInput, bool& result); +mozilla::pkix::Result CertListContainsExpectedKeys(const CERTCertList* certList, + const char* hostname, + mozilla::pkix::Time time); + +} // namespace psm +} // namespace mozilla + +#endif // CertVerifier_h diff --git a/security/certverifier/ExtendedValidation.cpp b/security/certverifier/ExtendedValidation.cpp new file mode 100644 index 0000000000..7ad44da274 --- /dev/null +++ b/security/certverifier/ExtendedValidation.cpp @@ -0,0 +1,1393 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ExtendedValidation.h" + +#include "cert.h" +#include "hasht.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Base64.h" +#include "mozilla/Casting.h" +#include "mozilla/PodOperations.h" +#include "mozpkix/pkixder.h" +#include "mozpkix/pkixtypes.h" +#include "mozpkix/pkixutil.h" + +#include "nsDependentString.h" +#include "nsString.h" +#include "pk11pub.h" + +namespace mozilla { + +namespace psm { + +struct EVInfo { + // See bug 1338873 about making these fields const. + const char* dottedOid; + const char* + oidName; // Set this to null to signal an invalid structure, + // (We can't have an empty list, so we'll use a dummy entry) + unsigned char sha256Fingerprint[SHA256_LENGTH]; + const char* issuerBase64; + const char* serialBase64; +}; + +// HOWTO enable additional CA root certificates for EV: +// +// For each combination of "root certificate" and "policy OID", +// one entry must be added to the array named kEVInfos. +// +// We use the combination of "issuer name" and "serial number" to +// uniquely identify the certificate. In order to avoid problems +// because of encodings when comparing certificates, we don't +// use plain text representation, we rather use the original encoding +// as it can be found in the root certificate (in base64 format). +// +// We can use the NSS utility named "pp" to extract the encoding. +// +// Build standalone NSS including the NSS tools, then run +// pp -t certificate-identity -i the-cert-filename +// +// You will need the output from sections "Issuer", "Fingerprint (SHA-256)", +// "Issuer DER Base64" and "Serial DER Base64". +// +// The new section consists of the following components: +// +// - a comment that should contain the human readable issuer name +// of the certificate, as printed by the pp tool +// - the EV policy OID that is associated to the EV grant +// - a text description of the EV policy OID. The array can contain +// multiple entries with the same OID. +// Please make sure to use the identical OID text description for +// all entries with the same policy OID (use the text search +// feature of your text editor to find duplicates). +// When adding a new policy OID that is not yet contained in the array, +// please make sure that your new description is different from +// all the other descriptions (again use the text search feature +// to be sure). +// - the SHA-256 fingerprint +// - the "Issuer DER Base64" as printed by the pp tool. +// Remove all whitespaces. If you use multiple lines, make sure that +// only the final line will be followed by a comma. +// - the "Serial DER Base64" (as printed by pp) +// +// After adding an entry, test it locally against the test site that +// has been provided by the CA. Note that you must use a version of NSS +// where the root certificate has already been added and marked as trusted +// for issuing SSL server certificates (at least). +// +// If you are able to connect to the site without certificate errors, +// but you don't see the EV status indicator, then most likely the CA +// has a problem in their infrastructure. The most common problems are +// related to the CA's OCSP infrastructure, either they use an incorrect +// OCSP signing certificate, or OCSP for the intermediate certificates +// isn't working, or OCSP isn't working at all. + +#ifdef DEBUG +static const size_t NUM_TEST_EV_ROOTS = 2; +#endif + +static const struct EVInfo kEVInfos[] = { +// clang-format off + // IMPORTANT! When extending this list, if you add another entry that uses + // the same dottedOid as an existing entry, use the same oidName. +#ifdef DEBUG + // Debug EV certificates should all use the following OID: + // 1.3.6.1.4.1.13769.666.666.666.1.500.9.1. + // (multiple entries with the same OID is ok) + // If you add or remove debug EV certs you must also modify NUM_TEST_EV_ROOTS + // so that the correct number of certs are skipped as these debug EV certs + // are NOT part of the default trust store. + { + // This is the PSM xpcshell testing EV certificate. It can be generated + // using pycert.py and the following specification: + // + // issuer:evroot + // subject:evroot + // subjectKey:ev + // issuerKey:ev + // validity:20150101-20350101 + // extension:basicConstraints:cA, + // extension:keyUsage:keyCertSign,cRLSign + // + // If this ever needs to change, re-generate the certificate and update the + // following entry with the new fingerprint, issuer, and serial number. + "1.3.6.1.4.1.13769.666.666.666.1.500.9.1", + "DEBUGtesting EV OID", + { 0x70, 0xED, 0xCB, 0x5A, 0xCE, 0x02, 0xC7, 0xC5, 0x0B, 0xA3, 0xD2, 0xD7, + 0xC6, 0xF5, 0x0E, 0x18, 0x02, 0x19, 0x17, 0xF5, 0x48, 0x08, 0x9C, 0xB3, + 0x8E, 0xEF, 0x9A, 0x1A, 0x4D, 0x7F, 0x82, 0x94 }, + "MBExDzANBgNVBAMMBmV2cm9vdA==", + "IZSHsVgzcvhPgdfrgdMGlpSfMeg=", + }, + { + // This is an RSA root with an inadequate key size. It is used to test that + // minimum key sizes are enforced when verifying for EV. It can be + // generated using pycert.py and the following specification: + // + // issuer:ev_root_rsa_2040 + // subject:ev_root_rsa_2040 + // issuerKey:evRSA2040 + // subjectKey:evRSA2040 + // validity:20150101-20350101 + // extension:basicConstraints:cA, + // extension:keyUsage:cRLSign,keyCertSign + // + // If this ever needs to change, re-generate the certificate and update the + // following entry with the new fingerprint, issuer, and serial number. + "1.3.6.1.4.1.13769.666.666.666.1.500.9.1", + "DEBUGtesting EV OID", + { 0x40, 0xAB, 0x5D, 0xA5, 0x89, 0x15, 0xA9, 0x4B, 0x82, 0x87, 0xB8, 0xA6, + 0x9A, 0x84, 0xB1, 0xDB, 0x7A, 0x9D, 0xDB, 0xB8, 0x4E, 0xE1, 0x23, 0xE3, + 0xC6, 0x64, 0xE7, 0x50, 0xDC, 0x35, 0x8C, 0x68 }, + "MBsxGTAXBgNVBAMMEGV2X3Jvb3RfcnNhXzIwNDA=", + "J7nCMgtzNcSPG7jAh3CWzlTGHQg=", + }, +#endif + { + // CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH + "2.16.756.1.89.1.2.1.1", + "SwissSign EV OID", + { 0x62, 0xDD, 0x0B, 0xE9, 0xB9, 0xF5, 0x0A, 0x16, 0x3E, 0xA0, 0xF8, + 0xE7, 0x5C, 0x05, 0x3B, 0x1E, 0xCA, 0x57, 0xEA, 0x55, 0xC8, 0x68, + 0x8F, 0x64, 0x7C, 0x68, 0x81, 0xF2, 0xC8, 0x35, 0x7B, 0x95 }, + "MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMT" + "FlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=", + "ALtAHEP1Xk+w", + }, + { + // CN=XRamp Global Certification Authority,O=XRamp Security Services Inc,OU=www.xrampsecurity.com,C=US + "2.16.840.1.114404.1.1.2.4.1", + "Trustwave EV OID", + { 0xCE, 0xCD, 0xDC, 0x90, 0x50, 0x99, 0xD8, 0xDA, 0xDF, 0xC5, 0xB1, + 0xD2, 0x09, 0xB7, 0x37, 0xCB, 0xE2, 0xC1, 0x8C, 0xFB, 0x2C, 0x10, + 0xC0, 0xFF, 0x0B, 0xCF, 0x0D, 0x32, 0x86, 0xFC, 0x1A, 0xA2 }, + "MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29t" + "MSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMT" + "JFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "UJRs7Bjq1ZxN1ZfvdY+grQ==", + }, + { + // CN=SecureTrust CA,O=SecureTrust Corporation,C=US + "2.16.840.1.114404.1.1.2.4.1", + "Trustwave EV OID", + { 0xF1, 0xC1, 0xB5, 0x0A, 0xE5, 0xA2, 0x0D, 0xD8, 0x03, 0x0E, 0xC9, + 0xF6, 0xBC, 0x24, 0x82, 0x3D, 0xD3, 0x67, 0xB5, 0x25, 0x57, 0x59, + 0xB4, 0xE7, 0x1B, 0x61, 0xFC, 0xE9, 0xF7, 0x37, 0x5D, 0x73 }, + "MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv" + "bjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=", + "DPCOXAgWpa1Cf/DrJxhZ0A==", + }, + { + // CN=Secure Global CA,O=SecureTrust Corporation,C=US + "2.16.840.1.114404.1.1.2.4.1", + "Trustwave EV OID", + { 0x42, 0x00, 0xF5, 0x04, 0x3A, 0xC8, 0x59, 0x0E, 0xBB, 0x52, 0x7D, + 0x20, 0x9E, 0xD1, 0x50, 0x30, 0x29, 0xFB, 0xCB, 0xD4, 0x1C, 0xA1, + 0xB5, 0x06, 0xEC, 0x27, 0xF1, 0x5A, 0xDE, 0x7D, 0xAC, 0x69 }, + "MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlv" + "bjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==", + "B1YipOjUiolN9BPI8PjqpQ==", + }, + { + // CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB + "1.3.6.1.4.1.6449.1.2.1.5.1", + "Comodo EV OID", + { 0x17, 0x93, 0x92, 0x7A, 0x06, 0x14, 0x54, 0x97, 0x89, 0xAD, 0xCE, + 0x2F, 0x8F, 0x34, 0xF7, 0xF0, 0xB6, 0x6D, 0x0F, 0x3A, 0xE3, 0xA3, + 0xB8, 0x4D, 0x21, 0xEC, 0x15, 0xDB, 0xBA, 0x4F, 0xAD, 0xC7 }, + "MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw" + "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG" + "A1UEAxMiQ09NT0RPIEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "H0evqmIAcFBUTAGem2OZKg==", + }, + { + // CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB + "1.3.6.1.4.1.6449.1.2.1.5.1", + "Comodo EV OID", + { 0x0C, 0x2C, 0xD6, 0x3D, 0xF7, 0x80, 0x6F, 0xA3, 0x99, 0xED, 0xE8, + 0x09, 0x11, 0x6B, 0x57, 0x5B, 0xF8, 0x79, 0x89, 0xF0, 0x65, 0x18, + 0xF9, 0x80, 0x8C, 0x86, 0x05, 0x03, 0x17, 0x8B, 0xAF, 0x66 }, + "MIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw" + "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUG" + "A1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5", + "ToEtioJl4AsC7j41AkblPQ==", + }, + { + // OU=Go Daddy Class 2 Certification Authority,O=\"The Go Daddy Group, Inc.\",C=US + "2.16.840.1.114413.1.7.23.3", + "Go Daddy EV OID a", + { 0xC3, 0x84, 0x6B, 0xF2, 0x4B, 0x9E, 0x93, 0xCA, 0x64, 0x27, 0x4C, + 0x0E, 0xC6, 0x7C, 0x1E, 0xCC, 0x5E, 0x02, 0x4F, 0xFC, 0xAC, 0xD2, + 0xD7, 0x40, 0x19, 0x35, 0x0E, 0x81, 0xFE, 0x54, 0x6A, 0xE4 }, + "MGMxCzAJBgNVBAYTAlVTMSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIElu" + "Yy4xMTAvBgNVBAsTKEdvIERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo" + "b3JpdHk=", + "AA==", + }, + { + // CN=Go Daddy Root Certificate Authority - G2,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US + "2.16.840.1.114413.1.7.23.3", + "Go Daddy EV OID a", + { 0x45, 0x14, 0x0B, 0x32, 0x47, 0xEB, 0x9C, 0xC8, 0xC5, 0xB4, 0xF0, + 0xD7, 0xB5, 0x30, 0x91, 0xF7, 0x32, 0x92, 0x08, 0x9E, 0x6E, 0x5A, + 0x63, 0xE2, 0x74, 0x9D, 0xD3, 0xAC, 0xA9, 0x19, 0x8E, 0xDA }, + "MIGDMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv" + "dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv" + "IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzI=", + "AA==", + }, + { + // OU=Starfield Class 2 Certification Authority,O=\"Starfield Technologies, Inc.\",C=US + "2.16.840.1.114414.1.7.23.3", + "Go Daddy EV OID b", + { 0x14, 0x65, 0xFA, 0x20, 0x53, 0x97, 0xB8, 0x76, 0xFA, 0xA6, 0xF0, + 0xA9, 0x95, 0x8E, 0x55, 0x90, 0xE4, 0x0F, 0xCC, 0x7F, 0xAA, 0x4F, + 0xB7, 0xC2, 0xC8, 0x67, 0x75, 0x21, 0xFB, 0x5F, 0xB6, 0x58 }, + "MGgxCzAJBgNVBAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVz" + "LCBJbmMuMTIwMAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9u" + "IEF1dGhvcml0eQ==", + "AA==", + }, + { + // CN=Starfield Root Certificate Authority - G2,O="Starfield Technologies, Inc.",L=Scottsdale,ST=Arizona,C=US + "2.16.840.1.114414.1.7.23.3", + "Go Daddy EV OID b", + { 0x2C, 0xE1, 0xCB, 0x0B, 0xF9, 0xD2, 0xF9, 0xE1, 0x02, 0x99, 0x3F, + 0xBE, 0x21, 0x51, 0x52, 0xC3, 0xB2, 0xDD, 0x0C, 0xAB, 0xDE, 0x1C, + 0x68, 0xE5, 0x31, 0x9B, 0x83, 0x91, 0x54, 0xDB, 0xB7, 0xF5 }, + "MIGPMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv" + "dHRzZGFsZTElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEy" + "MDAGA1UEAxMpU3RhcmZpZWxkIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0g" + "RzI=", + "AA==", + }, + { + // CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0x74, 0x31, 0xE5, 0xF4, 0xC3, 0xC1, 0xCE, 0x46, 0x90, 0x77, 0x4F, + 0x0B, 0x61, 0xE0, 0x54, 0x40, 0x88, 0x3B, 0xA9, 0xA0, 0x1E, 0xD0, + 0x0B, 0xA6, 0xAB, 0xD7, 0x80, 0x6E, 0xD3, 0xB1, 0x18, 0xCF }, + "MGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJh" + "bmNlIEVWIFJvb3QgQ0E=", + "AqxcJmoLQJuPC3nyrkYldw==", + }, + { + // CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM + "1.3.6.1.4.1.8024.0.2.100.1.2", + "Quo Vadis EV OID", + { 0x85, 0xA0, 0xDD, 0x7D, 0xD7, 0x20, 0xAD, 0xB7, 0xFF, 0x05, 0xF8, + 0x3D, 0x54, 0x2B, 0x20, 0x9D, 0xC7, 0xFF, 0x45, 0x28, 0xF7, 0xD6, + 0x77, 0xB1, 0x83, 0x89, 0xFE, 0xA5, 0xE5, 0xC4, 0x9E, 0x86 }, + "MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYD" + "VQQDExJRdW9WYWRpcyBSb290IENBIDI=", + "BQk=", + }, + { + // CN=Entrust Root Certification Authority,OU="(c) 2006 Entrust, Inc.",OU=www.entrust.net/CPS is incorporated by reference,O="Entrust, Inc.",C=US + "2.16.840.1.114028.10.1.2", + "Entrust EV OID", + { 0x73, 0xC1, 0x76, 0x43, 0x4F, 0x1B, 0xC6, 0xD5, 0xAD, 0xF4, 0x5B, + 0x0E, 0x76, 0xE7, 0x27, 0x28, 0x7C, 0x8D, 0xE5, 0x76, 0x16, 0xC1, + 0xE6, 0xE6, 0x14, 0x1A, 0x2B, 0x2C, 0xBC, 0x7D, 0x8E, 0x4C }, + "MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UE" + "CxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJl" + "bmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRF" + "bnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=", + "RWtQVA==", + }, + { + // CN=Entrust Root Certification Authority - G4,OU="(c) 2015 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US + "2.16.840.1.114028.10.1.2", + "Entrust EV OID", + { 0xDB, 0x35, 0x17, 0xD1, 0xF6, 0x73, 0x2A, 0x2D, 0x5A, 0xB9, 0x7C, + 0x53, 0x3E, 0xC7, 0x07, 0x79, 0xEE, 0x32, 0x70, 0xA6, 0x2F, 0xB4, + 0xAC, 0x42, 0x38, 0x37, 0x24, 0x60, 0xE6, 0xF0, 0x1E, 0x88 }, + "MIG+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UE" + "CxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMp" + "IDIwMTUgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIw" + "MAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH" + "NA==", + "ANm1Q3+vqTkPAAAAAFVlrVg=", + }, + { + // CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xEB, 0xD4, 0x10, 0x40, 0xE4, 0xBB, 0x3E, 0xC7, 0x42, 0xC9, 0xE3, + 0x81, 0xD3, 0x1E, 0xF2, 0xA4, 0x1A, 0x48, 0xB6, 0x68, 0x5C, 0x96, + 0xE7, 0xCE, 0xF3, 0xC1, 0xDF, 0x6C, 0xD4, 0x33, 0x1C, 0x99 }, + "MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYD" + "VQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=", + "BAAAAAABFUtaw5Q=", + }, + { + // CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R3 + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xCB, 0xB5, 0x22, 0xD7, 0xB7, 0xF1, 0x27, 0xAD, 0x6A, 0x01, 0x13, + 0x86, 0x5B, 0xDF, 0x1C, 0xD4, 0x10, 0x2E, 0x7D, 0x07, 0x59, 0xAF, + 0x63, 0x5A, 0x7C, 0xF4, 0x72, 0x0D, 0xC9, 0x63, 0xC5, 0x3B }, + "MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpH" + "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu", + "BAAAAAABIVhTCKI=", + }, + { + // CN=Buypass Class 3 Root CA,O=Buypass AS-983163327,C=NO + "2.16.578.1.26.1.3.3", + "Buypass EV OID", + { 0xED, 0xF7, 0xEB, 0xBC, 0xA2, 0x7A, 0x2A, 0x38, 0x4D, 0x38, 0x7B, + 0x7D, 0x40, 0x10, 0xC6, 0x66, 0xE2, 0xED, 0xB4, 0x84, 0x3E, 0x4C, + 0x29, 0xB4, 0xAE, 0x1D, 0x5B, 0x93, 0x32, 0xE6, 0xB2, 0x4D }, + "ME4xCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEg" + "MB4GA1UEAwwXQnV5cGFzcyBDbGFzcyAzIFJvb3QgQ0E=", + "Ag==", + }, + { + // CN=AffirmTrust Commercial,O=AffirmTrust,C=US + "1.3.6.1.4.1.34697.2.1", + "AffirmTrust EV OID a", + { 0x03, 0x76, 0xAB, 0x1D, 0x54, 0xC5, 0xF9, 0x80, 0x3C, 0xE4, 0xB2, + 0xE2, 0x01, 0xA0, 0xEE, 0x7E, 0xEF, 0x7B, 0x57, 0xB6, 0x36, 0xE8, + 0xA9, 0x3C, 0x9B, 0x8D, 0x48, 0x60, 0xC9, 0x6F, 0x5F, 0xA7 }, + "MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW" + "QWZmaXJtVHJ1c3QgQ29tbWVyY2lhbA==", + "d3cGJyapsXw=", + }, + { + // CN=AffirmTrust Networking,O=AffirmTrust,C=US + "1.3.6.1.4.1.34697.2.2", + "AffirmTrust EV OID b", + { 0x0A, 0x81, 0xEC, 0x5A, 0x92, 0x97, 0x77, 0xF1, 0x45, 0x90, 0x4A, + 0xF3, 0x8D, 0x5D, 0x50, 0x9F, 0x66, 0xB5, 0xE2, 0xC5, 0x8F, 0xCD, + 0xB5, 0x31, 0x05, 0x8B, 0x0E, 0x17, 0xF3, 0xF0, 0xB4, 0x1B }, + "MEQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEfMB0GA1UEAwwW" + "QWZmaXJtVHJ1c3QgTmV0d29ya2luZw==", + "fE8EORzUmS0=", + }, + { + // CN=AffirmTrust Premium,O=AffirmTrust,C=US + "1.3.6.1.4.1.34697.2.3", + "AffirmTrust EV OID c", + { 0x70, 0xA7, 0x3F, 0x7F, 0x37, 0x6B, 0x60, 0x07, 0x42, 0x48, 0x90, + 0x45, 0x34, 0xB1, 0x14, 0x82, 0xD5, 0xBF, 0x0E, 0x69, 0x8E, 0xCC, + 0x49, 0x8D, 0xF5, 0x25, 0x77, 0xEB, 0xF2, 0xE9, 0x3B, 0x9A }, + "MEExCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEcMBoGA1UEAwwT" + "QWZmaXJtVHJ1c3QgUHJlbWl1bQ==", + "bYwURrGmCu4=", + }, + { + // CN=AffirmTrust Premium ECC,O=AffirmTrust,C=US + "1.3.6.1.4.1.34697.2.4", + "AffirmTrust EV OID d", + { 0xBD, 0x71, 0xFD, 0xF6, 0xDA, 0x97, 0xE4, 0xCF, 0x62, 0xD1, 0x64, + 0x7A, 0xDD, 0x25, 0x81, 0xB0, 0x7D, 0x79, 0xAD, 0xF8, 0x39, 0x7E, + 0xB4, 0xEC, 0xBA, 0x9C, 0x5E, 0x84, 0x88, 0x82, 0x14, 0x23 }, + "MEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwX" + "QWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0M=", + "dJclisc/elQ=", + }, + { + // CN=Certum Trusted Network CA,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL + "1.2.616.1.113527.2.5.1.1", + "Certum EV OID", + { 0x5C, 0x58, 0x46, 0x8D, 0x55, 0xF5, 0x8E, 0x49, 0x7E, 0x74, 0x39, + 0x82, 0xD2, 0xB5, 0x00, 0x10, 0xB6, 0xD1, 0x65, 0x37, 0x4A, 0xCF, + 0x83, 0xA7, 0xD4, 0xA3, 0x2D, 0xB7, 0x68, 0xC4, 0x40, 0x8E }, + "MH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBT" + "LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAg" + "BgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0E=", + "BETA", + }, + { + // CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL + "1.2.616.1.113527.2.5.1.1", + "Certum EV OID", + { 0xB6, 0x76, 0xF2, 0xED, 0xDA, 0xE8, 0x77, 0x5C, 0xD3, 0x6C, 0xB0, + 0xF6, 0x3C, 0xD1, 0xD4, 0x60, 0x39, 0x61, 0xF4, 0x9E, 0x62, 0x65, + 0xBA, 0x01, 0x3A, 0x2F, 0x03, 0x07, 0xB6, 0xD0, 0xB8, 0x04 }, + "MIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg" + "Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQw" + "IgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBIDI=", + "IdbQSk8lD8kyN/yqXhKN6Q==", + }, + { + // CN=Izenpe.com,O=IZENPE S.A.,C=ES + "1.3.6.1.4.1.14777.6.1.1", + "Izenpe EV OID 1", + { 0x25, 0x30, 0xCC, 0x8E, 0x98, 0x32, 0x15, 0x02, 0xBA, 0xD9, 0x6F, + 0x9B, 0x1F, 0xBA, 0x1B, 0x09, 0x9E, 0x2D, 0x29, 0x9E, 0x0F, 0x45, + 0x48, 0xBB, 0x91, 0x4F, 0x36, 0x3B, 0xC0, 0xD4, 0x53, 0x1F }, + "MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK" + "SXplbnBlLmNvbQ==", + "ALC3WhZIX7/hy/WL1xnmfQ==", + }, + { + // CN=Izenpe.com,O=IZENPE S.A.,C=ES + "1.3.6.1.4.1.14777.6.1.2", + "Izenpe EV OID 2", + { 0x25, 0x30, 0xCC, 0x8E, 0x98, 0x32, 0x15, 0x02, 0xBA, 0xD9, 0x6F, + 0x9B, 0x1F, 0xBA, 0x1B, 0x09, 0x9E, 0x2D, 0x29, 0x9E, 0x0F, 0x45, + 0x48, 0xBB, 0x91, 0x4F, 0x36, 0x3B, 0xC0, 0xD4, 0x53, 0x1F }, + "MDgxCzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwK" + "SXplbnBlLmNvbQ==", + "ALC3WhZIX7/hy/WL1xnmfQ==", + }, + { + // CN=T-TeleSec GlobalRoot Class 3,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE + "1.3.6.1.4.1.7879.13.24.1", + "T-Systems EV OID", + { 0xFD, 0x73, 0xDA, 0xD3, 0x1C, 0x64, 0x4F, 0xF1, 0xB4, 0x3B, 0xEF, + 0x0C, 0xCD, 0xDA, 0x96, 0x71, 0x0B, 0x9C, 0xD9, 0x87, 0x5E, 0xCA, + 0x7E, 0x31, 0x70, 0x7A, 0xF3, 0xE9, 0x6D, 0x52, 0x2B, 0xBD }, + "MIGCMQswCQYDVQQGEwJERTErMCkGA1UECgwiVC1TeXN0ZW1zIEVudGVycHJpc2Ug" + "U2VydmljZXMgR21iSDEfMB0GA1UECwwWVC1TeXN0ZW1zIFRydXN0IENlbnRlcjEl" + "MCMGA1UEAwwcVC1UZWxlU2VjIEdsb2JhbFJvb3QgQ2xhc3MgMw==", + "AQ==", + }, + { + // CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW + "1.3.6.1.4.1.40869.1.1.22.3", + "TWCA EV OID", + { 0xBF, 0xD8, 0x8F, 0xE1, 0x10, 0x1C, 0x41, 0xAE, 0x3E, 0x80, 0x1B, + 0xF8, 0xBE, 0x56, 0x35, 0x0E, 0xE9, 0xBA, 0xD1, 0xA6, 0xB9, 0xBD, + 0x51, 0x5E, 0xDC, 0x5C, 0x6D, 0x5B, 0x87, 0x11, 0xAC, 0x44 }, + "MF8xCzAJBgNVBAYTAlRXMRIwEAYDVQQKDAlUQUlXQU4tQ0ExEDAOBgNVBAsMB1Jv" + "b3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0" + "eQ==", + "AQ==", + }, + { + // CN=D-TRUST Root Class 3 CA 2 EV 2009,O=D-Trust GmbH,C=DE + "1.3.6.1.4.1.4788.2.202.1", + "D-TRUST EV OID", + { 0xEE, 0xC5, 0x49, 0x6B, 0x98, 0x8C, 0xE9, 0x86, 0x25, 0xB9, 0x34, + 0x09, 0x2E, 0xEC, 0x29, 0x08, 0xBE, 0xD0, 0xB0, 0xF3, 0x16, 0xC2, + 0xD4, 0x73, 0x0C, 0x84, 0xEA, 0xF1, 0xF3, 0xD3, 0x48, 0x81 }, + "MFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMM" + "IUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOQ==", + "CYP0", + }, + { + // CN = Autoridad de Certificacion Firmaprofesional CIF A62634068, C = ES + "1.3.6.1.4.1.13177.10.1.3.10", + "Firmaprofesional EV OID", + { 0x04, 0x04, 0x80, 0x28, 0xBF, 0x1F, 0x28, 0x64, 0xD4, 0x8F, 0x9A, + 0xD4, 0xD8, 0x32, 0x94, 0x36, 0x6A, 0x82, 0x88, 0x56, 0x55, 0x3F, + 0x3B, 0x14, 0x30, 0x3F, 0x90, 0x14, 0x7F, 0x5D, 0x40, 0xEF }, + "MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNh" + "Y2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=", + "U+w77vuySF8=", + }, + { + // CN = TWCA Global Root CA, OU = Root CA, O = TAIWAN-CA, C = TW + "1.3.6.1.4.1.40869.1.1.22.3", + "TWCA EV OID", + { 0x59, 0x76, 0x90, 0x07, 0xF7, 0x68, 0x5D, 0x0F, 0xCD, 0x50, 0x87, + 0x2F, 0x9F, 0x95, 0xD5, 0x75, 0x5A, 0x5B, 0x2B, 0x45, 0x7D, 0x81, + 0xF3, 0x69, 0x2B, 0x61, 0x0A, 0x98, 0x67, 0x2F, 0x0E, 0x1B }, + "MFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jv" + "b3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0E=", + "DL4=", + }, + { + // CN=E-Tugra Global Root CA RSA v3,OU=E-Tugra Trust Center,O=E-Tugra EBG A.S.,L=Ankara,C=TR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xEF, 0x66, 0xB0, 0xB1, 0x0A, 0x3C, 0xDB, 0x9F, 0x2E, 0x36, 0x48, + 0xC7, 0x6B, 0xD2, 0xAF, 0x18, 0xEA, 0xD2, 0xBF, 0xE6, 0xF1, 0x17, + 0x65, 0x5E, 0x28, 0xC4, 0x06, 0x0D, 0xA1, 0xA3, 0xF4, 0xC2 }, + "MIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1" + "Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3JhIFRydXN0IENlbnRlcjEmMCQG" + "A1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0EgdjM=", + "DU3FzRYilZYIfrgLfxUGNPt5EDQ=", + }, + { + // CN=E-Tugra Global Root CA ECC v3,OU=E-Tugra Trust Center,O=E-Tugra EBG A.S.,L=Ankara,C=TR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x87, 0x3F, 0x46, 0x85, 0xFA, 0x7F, 0x56, 0x36, 0x25, 0x25, 0x2E, + 0x6D, 0x36, 0xBC, 0xD7, 0xF1, 0x6F, 0xC2, 0x49, 0x51, 0xF2, 0x64, + 0xE4, 0x7E, 0x1B, 0x95, 0x4F, 0x49, 0x08, 0xCD, 0xCA, 0x13 }, + "MIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1" + "Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3JhIFRydXN0IENlbnRlcjEmMCQG" + "A1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjM=", + "JkYZdzHhT28oNt45UYbm1JeIIsE=", + }, + { + // CN=Actalis Authentication Root CA,O=Actalis S.p.A./03358520967,L=Milan,C=IT + "1.3.159.1.17.1", + "Actalis EV OID", + { 0x55, 0x92, 0x60, 0x84, 0xEC, 0x96, 0x3A, 0x64, 0xB9, 0x6E, 0x2A, + 0xBE, 0x01, 0xCE, 0x0B, 0xA8, 0x6A, 0x64, 0xFB, 0xFE, 0xBC, 0xC7, + 0xAA, 0xB5, 0xAF, 0xC1, 0x55, 0xB3, 0x7F, 0xD7, 0x60, 0x66 }, + "MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxp" + "cyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGlj" + "YXRpb24gUm9vdCBDQQ==", + "VwoRl0LE48w=", + }, + { + // CN=DigiCert Assured ID Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0x7D, 0x05, 0xEB, 0xB6, 0x82, 0x33, 0x9F, 0x8C, 0x94, 0x51, 0xEE, + 0x09, 0x4E, 0xEB, 0xFE, 0xFA, 0x79, 0x53, 0xA1, 0x14, 0xED, 0xB2, + 0xF4, 0x49, 0x49, 0x45, 0x2F, 0xAB, 0x7D, 0x2F, 0xC1, 0x85 }, + "MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg" + "Um9vdCBHMg==", + "C5McOtY5Z+pnI7/Dr5r0Sw==", + }, + { + // CN=DigiCert Assured ID Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0x7E, 0x37, 0xCB, 0x8B, 0x4C, 0x47, 0x09, 0x0C, 0xAB, 0x36, 0x55, + 0x1B, 0xA6, 0xF4, 0x5D, 0xB8, 0x40, 0x68, 0x0F, 0xBA, 0x16, 0x6A, + 0x95, 0x2D, 0xB1, 0x00, 0x71, 0x7F, 0x43, 0x05, 0x3F, 0xC2 }, + "MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg" + "Um9vdCBHMw==", + "C6Fa+h3foLVJRK/NJKBs7A==", + }, + { + // CN=DigiCert Global Root G2,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0xCB, 0x3C, 0xCB, 0xB7, 0x60, 0x31, 0xE5, 0xE0, 0x13, 0x8F, 0x8D, + 0xD3, 0x9A, 0x23, 0xF9, 0xDE, 0x47, 0xFF, 0xC3, 0x5E, 0x43, 0xC1, + 0x14, 0x4C, 0xEA, 0x27, 0xD4, 0x6A, 0x5A, 0xB1, 0xCB, 0x5F }, + "MGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290" + "IEcy", + "Azrx5qcRqaC7KGSxHQn65Q==", + }, + { + // CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0x31, 0xAD, 0x66, 0x48, 0xF8, 0x10, 0x41, 0x38, 0xC7, 0x38, 0xF3, + 0x9E, 0xA4, 0x32, 0x01, 0x33, 0x39, 0x3E, 0x3A, 0x18, 0xCC, 0x02, + 0x29, 0x6E, 0xF9, 0x7C, 0x2A, 0xC9, 0xEF, 0x67, 0x31, 0xD0 }, + "MGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290" + "IEcz", + "BVVWvPJepDU1w6QP1atFcg==", + }, + { + // CN=DigiCert Trusted Root G4,OU=www.digicert.com,O=DigiCert Inc,C=US + "2.16.840.1.114412.2.1", + "DigiCert EV OID", + { 0x55, 0x2F, 0x7B, 0xDC, 0xF1, 0xA7, 0xAF, 0x9E, 0x6C, 0xE6, 0x72, + 0x01, 0x7F, 0x4F, 0x12, 0xAB, 0xF7, 0x72, 0x40, 0xC7, 0x8E, 0x76, + 0x1A, 0xC2, 0x03, 0xD1, 0xD9, 0xD2, 0x0A, 0xC8, 0x99, 0x88 }, + "MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT" + "EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v" + "dCBHNA==", + "BZsbV56OITLiOQe9p3d1XA==", + }, + { + // CN=DigiCert TLS RSA4096 Root G5,O="DigiCert, Inc.",C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x37, 0x1A, 0x00, 0xDC, 0x05, 0x33, 0xB3, 0x72, 0x1A, 0x7E, 0xEB, + 0x40, 0xE8, 0x41, 0x9E, 0x70, 0x79, 0x9D, 0x2B, 0x0A, 0x0F, 0x2C, + 0x1D, 0x80, 0x69, 0x31, 0x65, 0xF7, 0xCE, 0xC4, 0xAD, 0x75 }, + "ME0xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjElMCMGA1UE" + "AxMcRGlnaUNlcnQgVExTIFJTQTQwOTYgUm9vdCBHNQ==", + "CPm0eKj6ftpqMzeJ3nzPig==", + }, + { + // CN=DigiCert TLS ECC P384 Root G5,O="DigiCert, Inc.",C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x01, 0x8E, 0x13, 0xF0, 0x77, 0x25, 0x32, 0xCF, 0x80, 0x9B, 0xD1, + 0xB1, 0x72, 0x81, 0x86, 0x72, 0x83, 0xFC, 0x48, 0xC6, 0xE1, 0x3B, + 0xE9, 0xC6, 0x98, 0x12, 0x85, 0x4A, 0x49, 0x0C, 0x1B, 0x05 }, + "ME4xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEmMCQGA1UE" + "AxMdRGlnaUNlcnQgVExTIEVDQyBQMzg0IFJvb3QgRzU=", + "CeCTZaz32ci5PhwLBCou8w==", + }, + { + // CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM + "1.3.6.1.4.1.8024.0.2.100.1.2", + "QuoVadis EV OID", + { 0x8F, 0xE4, 0xFB, 0x0A, 0xF9, 0x3A, 0x4D, 0x0D, 0x67, 0xDB, 0x0B, + 0xEB, 0xB2, 0x3E, 0x37, 0xC7, 0x1B, 0xF3, 0x25, 0xDC, 0xBC, 0xDD, + 0x24, 0x0E, 0xA0, 0x4D, 0xAF, 0x58, 0xB4, 0x7E, 0x18, 0x40 }, + "MEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYD" + "VQQDExVRdW9WYWRpcyBSb290IENBIDIgRzM=", + "RFc0JFuBiZs18s64KztbpybwdSg=", + }, + { + // CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB + "1.3.6.1.4.1.6449.1.2.1.5.1", + "Comodo EV OID", + { 0x52, 0xF0, 0xE1, 0xC4, 0xE5, 0x8E, 0xC6, 0x29, 0x29, 0x1B, 0x60, + 0x31, 0x7F, 0x07, 0x46, 0x71, 0xB8, 0x5D, 0x7E, 0xA8, 0x0D, 0x5B, + 0x07, 0x27, 0x34, 0x63, 0x53, 0x4B, 0x32, 0xB4, 0x02, 0x34 }, + "MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAw" + "DgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkG" + "A1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "TKr5yttjb+Af907YWwOGnQ==", + }, + { + // CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US + "1.3.6.1.4.1.6449.1.2.1.5.1", + "Comodo EV OID", + { 0xE7, 0x93, 0xC9, 0xB0, 0x2F, 0xD8, 0xAA, 0x13, 0xE2, 0x1C, 0x31, + 0x22, 0x8A, 0xCC, 0xB0, 0x81, 0x19, 0x64, 0x3B, 0x74, 0x9C, 0x89, + 0x89, 0x64, 0xB1, 0x74, 0x6D, 0x46, 0xC3, 0xD4, 0xCB, 0xD2 }, + "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML" + "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG" + "A1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "Af1tMPyjylGoG7xkDjUDLQ==", + }, + { + // CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US + "1.3.6.1.4.1.6449.1.2.1.5.1", + "Comodo EV OID", + { 0x4F, 0xF4, 0x60, 0xD5, 0x4B, 0x9C, 0x86, 0xDA, 0xBF, 0xBC, 0xFC, + 0x57, 0x12, 0xE0, 0x40, 0x0D, 0x2B, 0xED, 0x3F, 0xBC, 0x4D, 0x4F, + 0xBD, 0xAA, 0x86, 0xE0, 0x6A, 0xDC, 0xD2, 0xA9, 0xAD, 0x7A }, + "MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxML" + "SmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwG" + "A1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "XIuZxVqUxdJxVt7NiYDMJg==", + }, + { + // CN=GlobalSign,O=GlobalSign,OU=GlobalSign ECC Root CA - R5 + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x17, 0x9F, 0xBC, 0x14, 0x8A, 0x3D, 0xD0, 0x0F, 0xD2, 0x4E, 0xA1, + 0x34, 0x58, 0xCC, 0x43, 0xBF, 0xA7, 0xF5, 0x9C, 0x81, 0x82, 0xD7, + 0x83, 0xA5, 0x13, 0xF6, 0xEB, 0xEC, 0x10, 0x0C, 0x89, 0x24 }, + "MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNTETMBEGA1UE" + "ChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==", + "YFlJ4CYuu1X5CneKcflK2Gw=", + }, + { + // CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R6 + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x2C, 0xAB, 0xEA, 0xFE, 0x37, 0xD0, 0x6C, 0xA2, 0x2A, 0xBA, 0x73, + 0x91, 0xC0, 0x03, 0x3D, 0x25, 0x98, 0x29, 0x52, 0xC4, 0x53, 0x64, + 0x73, 0x49, 0x76, 0x3A, 0x3A, 0xB5, 0xAD, 0x6C, 0xCF, 0x69 }, + "MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFI2MRMwEQYDVQQKEwpH" + "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu", + "Rea7A4Mzw4VlSOb/RVE=", + }, + { + // CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net + "2.16.840.1.114028.10.1.2", + "Entrust EV OID", + { 0x6D, 0xC4, 0x71, 0x72, 0xE0, 0x1C, 0xBC, 0xB0, 0xBF, 0x62, 0x58, + 0x0D, 0x89, 0x5F, 0xE2, 0xB8, 0xAC, 0x9A, 0xD4, 0xF8, 0x73, 0x80, + 0x1E, 0x0C, 0x10, 0xB9, 0xC8, 0x37, 0xD2, 0x1E, 0xB1, 0x77 }, + "MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3Qu" + "bmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMG" + "A1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50" + "cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp", + "OGPe+A==", + }, + { + // CN=Entrust Root Certification Authority - G2,OU="(c) 2009 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US + "2.16.840.1.114028.10.1.2", + "Entrust EV OID", + { 0x43, 0xDF, 0x57, 0x74, 0xB0, 0x3E, 0x7F, 0xEF, 0x5F, 0xE4, 0x0D, + 0x93, 0x1A, 0x7B, 0xED, 0xF1, 0xBB, 0x2E, 0x6B, 0x42, 0x73, 0x8C, + 0x4E, 0x6D, 0x38, 0x41, 0x10, 0x3D, 0x3A, 0xA7, 0xF3, 0x39 }, + "MIG+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UE" + "CxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMp" + "IDIwMDkgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIw" + "MAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH" + "Mg==", + "SlOMKA==", + }, + { + // CN=Entrust Root Certification Authority - EC1,OU="(c) 2012 Entrust, Inc. - for authorized use only",OU=See www.entrust.net/legal-terms,O="Entrust, Inc.",C=US + "2.16.840.1.114028.10.1.2", + "Entrust EV OID", + { 0x02, 0xED, 0x0E, 0xB2, 0x8C, 0x14, 0xDA, 0x45, 0x16, 0x5C, 0x56, + 0x67, 0x91, 0x70, 0x0D, 0x64, 0x51, 0xD7, 0xFB, 0x56, 0xF0, 0xB2, + 0xAB, 0x1D, 0x3B, 0x8E, 0xB0, 0x70, 0xE5, 0x6E, 0xDF, 0xF5 }, + "MIG/MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UE" + "CxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMp" + "IDIwMTIgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTMw" + "MQYDVQQDEypFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBF" + "QzE=", + "AKaLeSkAAAAAUNCR+Q==", + }, + { + // CN=CFCA EV ROOT,O=China Financial Certification Authority,C=CN + "2.16.156.112554.3", + "CFCA EV OID", + { 0x5C, 0xC3, 0xD7, 0x8E, 0x4E, 0x1D, 0x5E, 0x45, 0x54, 0x7A, 0x04, + 0xE6, 0x87, 0x3E, 0x64, 0xF9, 0x0C, 0xF9, 0x53, 0x6D, 0x1C, 0xCC, + 0x2E, 0xF8, 0x00, 0xF3, 0x55, 0xC4, 0xC5, 0xFD, 0x70, 0xFD }, + "MFYxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlm" + "aWNhdGlvbiBBdXRob3JpdHkxFTATBgNVBAMMDENGQ0EgRVYgUk9PVA==", + "GErM1g==", + }, + { + // OU=Security Communication RootCA2,O="SECOM Trust Systems CO.,LTD.",C=JP + "1.2.392.200091.100.721.1", + "SECOM EV OID", + { 0x51, 0x3B, 0x2C, 0xEC, 0xB8, 0x10, 0xD4, 0xCD, 0xE5, 0xDD, 0x85, + 0x39, 0x1A, 0xDF, 0xC6, 0xC2, 0xDD, 0x60, 0xD8, 0x7B, 0xB7, 0x36, + 0xD2, 0xB5, 0x21, 0x48, 0x4A, 0xA4, 0x7A, 0x0E, 0xBE, 0xF6 }, + "MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENP" + "LixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=", + "AA==", + }, + { + // CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH + "2.16.756.5.14.7.4.8", + "WISeKey EV OID", + { 0x6B, 0x9C, 0x08, 0xE8, 0x6E, 0xB0, 0xF7, 0x67, 0xCF, 0xAD, 0x65, + 0xCD, 0x98, 0xB6, 0x21, 0x49, 0xE5, 0x49, 0x4A, 0x67, 0xF5, 0x84, + 0x5E, 0x7B, 0xD1, 0xED, 0x01, 0x9F, 0x27, 0xB8, 0x6B, 0xD6 }, + "MG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNU" + "RSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds" + "b2JhbCBSb290IEdCIENB", + "drEgUnTwhYdGs/gjGvbCwA==", + }, + { + // CN=Amazon Root CA 1,O=Amazon,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x8E, 0xCD, 0xE6, 0x88, 0x4F, 0x3D, 0x87, 0xB1, 0x12, 0x5B, 0xA3, + 0x1A, 0xC3, 0xFC, 0xB1, 0x3D, 0x70, 0x16, 0xDE, 0x7F, 0x57, 0xCC, + 0x90, 0x4F, 0xE1, 0xCB, 0x97, 0xC6, 0xAE, 0x98, 0x19, 0x6E }, + "MDkxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xGTAXBgNVBAMTEEFtYXpv" + "biBSb290IENBIDE=", + "Bmyfz5m/jAo54vB4ikPmljZbyg==", + }, + { + // CN=Amazon Root CA 2,O=Amazon,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x1B, 0xA5, 0xB2, 0xAA, 0x8C, 0x65, 0x40, 0x1A, 0x82, 0x96, 0x01, + 0x18, 0xF8, 0x0B, 0xEC, 0x4F, 0x62, 0x30, 0x4D, 0x83, 0xCE, 0xC4, + 0x71, 0x3A, 0x19, 0xC3, 0x9C, 0x01, 0x1E, 0xA4, 0x6D, 0xB4 }, + "MDkxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xGTAXBgNVBAMTEEFtYXpv" + "biBSb290IENBIDI=", + "Bmyf0pY1hp8KD+WGePhbJruKNw==", + }, + { + // CN=Amazon Root CA 3,O=Amazon,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x18, 0xCE, 0x6C, 0xFE, 0x7B, 0xF1, 0x4E, 0x60, 0xB2, 0xE3, 0x47, + 0xB8, 0xDF, 0xE8, 0x68, 0xCB, 0x31, 0xD0, 0x2E, 0xBB, 0x3A, 0xDA, + 0x27, 0x15, 0x69, 0xF5, 0x03, 0x43, 0xB4, 0x6D, 0xB3, 0xA4 }, + "MDkxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xGTAXBgNVBAMTEEFtYXpv" + "biBSb290IENBIDM=", + "Bmyf1XSXNmY/Owua2eiedgPySg==", + }, + { + // CN=Amazon Root CA 4,O=Amazon,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xE3, 0x5D, 0x28, 0x41, 0x9E, 0xD0, 0x20, 0x25, 0xCF, 0xA6, 0x90, + 0x38, 0xCD, 0x62, 0x39, 0x62, 0x45, 0x8D, 0xA5, 0xC6, 0x95, 0xFB, + 0xDE, 0xA3, 0xC2, 0x2B, 0x0B, 0xFB, 0x25, 0x89, 0x70, 0x92 }, + "MDkxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xGTAXBgNVBAMTEEFtYXpv" + "biBSb290IENBIDQ=", + "Bmyf18G7EEwpQ+Vxe3ssyBrBDg==", + }, + { + // CN=Starfield Services Root Certificate Authority - G2,O="Starfield Technologies, Inc.",L=Scottsdale,ST=Arizona,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x56, 0x8D, 0x69, 0x05, 0xA2, 0xC8, 0x87, 0x08, 0xA4, 0xB3, 0x02, + 0x51, 0x90, 0xED, 0xCF, 0xED, 0xB1, 0x97, 0x4A, 0x60, 0x6A, 0x13, + 0xC6, 0xE5, 0x29, 0x0F, 0xCB, 0x2A, 0xE6, 0x3E, 0xDA, 0xB5 }, + "MIGYMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv" + "dHRzZGFsZTElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7" + "MDkGA1UEAxMyU3RhcmZpZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0" + "aG9yaXR5IC0gRzI=", + "AA==", + }, + { + // CN=GDCA TrustAUTH R5 ROOT,O="GUANG DONG CERTIFICATE AUTHORITY CO.,LTD.",C=CN + "1.2.156.112559.1.1.6.1", + "GDCA EV OID", + { 0xBF, 0xFF, 0x8F, 0xD0, 0x44, 0x33, 0x48, 0x7D, 0x6A, 0x8A, 0xA6, + 0x0C, 0x1A, 0x29, 0x76, 0x7A, 0x9F, 0xC2, 0xBB, 0xB0, 0x5E, 0x42, + 0x0F, 0x71, 0x3A, 0x13, 0xB9, 0x92, 0x89, 0x1D, 0x38, 0x93 }, + "MGIxCzAJBgNVBAYTAkNOMTIwMAYDVQQKDClHVUFORyBET05HIENFUlRJRklDQVRF" + "IEFVVEhPUklUWSBDTy4sTFRELjEfMB0GA1UEAwwWR0RDQSBUcnVzdEFVVEggUjUg" + "Uk9PVA==", + "fQmX/vBH6no=", + }, + { + // CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x22, 0xA2, 0xC1, 0xF7, 0xBD, 0xED, 0x70, 0x4C, 0xC1, 0xE7, 0x01, + 0xB5, 0xF4, 0x08, 0xC3, 0x10, 0x88, 0x0F, 0xE9, 0x56, 0xB5, 0xDE, + 0x2A, 0x4A, 0x44, 0xF9, 0x9C, 0x87, 0x3A, 0x25, 0xA7, 0xC8 }, + "MH8xCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3Rv" + "bjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtTU0wuY29tIEVW" + "IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRUND", + "LCmcWxbtBZU=", + }, + { + // CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x2E, 0x7B, 0xF1, 0x6C, 0xC2, 0x24, 0x85, 0xA7, 0xBB, 0xE2, 0xAA, + 0x86, 0x96, 0x75, 0x07, 0x61, 0xB0, 0xAE, 0x39, 0xBE, 0x3B, 0x2F, + 0xE9, 0xD0, 0xCC, 0x6D, 0x4E, 0xF7, 0x34, 0x91, 0x42, 0x5C }, + "MIGCMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0" + "b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE3MDUGA1UEAwwuU1NMLmNvbSBF" + "ViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQSBSMg==", + "VrYpzTS8ePY=", + }, + { + // CN=UCA Extended Validation Root,O=UniTrust,C=CN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xD4, 0x3A, 0xF9, 0xB3, 0x54, 0x73, 0x75, 0x5C, 0x96, 0x84, 0xFC, + 0x06, 0xD7, 0xD8, 0xCB, 0x70, 0xEE, 0x5C, 0x28, 0xE7, 0x73, 0xFB, + 0x29, 0x4E, 0xB4, 0x1E, 0xE7, 0x17, 0x22, 0x92, 0x4D, 0x24 }, + "MEcxCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDElMCMGA1UEAwwcVUNB" + "IEV4dGVuZGVkIFZhbGlkYXRpb24gUm9vdA==", + "T9Irj/VkyDOeTzRYZiNwYA==", + }, + { + // CN=Hongkong Post Root CA 3,O=Hongkong Post,L=Hong Kong,ST=Hong Kong,C=HK + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x5A, 0x2F, 0xC0, 0x3F, 0x0C, 0x83, 0xB0, 0x90, 0xBB, 0xFA, 0x40, + 0x60, 0x4B, 0x09, 0x88, 0x44, 0x6C, 0x76, 0x36, 0x18, 0x3D, 0xF9, + 0x84, 0x6E, 0x17, 0x10, 0x1A, 0x44, 0x7F, 0xB8, 0xEF, 0xD6 }, + "MG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcTCUhv" + "bmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9uZ2tv" + "bmcgUG9zdCBSb290IENBIDM=", + "CBZfikyl7ADJk0DfxMauI7gcWqQ=", + }, + { + // CN=emSign Root CA - G1,O=eMudhra Technologies Limited,OU=emSign PKI,C=IN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x40, 0xF6, 0xAF, 0x03, 0x46, 0xA9, 0x9A, 0xA1, 0xCD, 0x1D, 0x55, + 0x5A, 0x4E, 0x9C, 0xCE, 0x62, 0xC7, 0xF9, 0x63, 0x46, 0x03, 0xEE, + 0x40, 0x66, 0x15, 0x83, 0x3D, 0xC8, 0xC8, 0xD0, 0x03, 0x67 }, + "MGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxl" + "TXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9v" + "dCBDQSAtIEcx", + "MfXkYgxsWO3W2A==", + }, + { + // CN=emSign ECC Root CA - G3,O=eMudhra Technologies Limited,OU=emSign PKI,C=IN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x86, 0xA1, 0xEC, 0xBA, 0x08, 0x9C, 0x4A, 0x8D, 0x3B, 0xBE, 0x27, + 0x34, 0xC6, 0x12, 0xBA, 0x34, 0x1D, 0x81, 0x3E, 0x04, 0x3C, 0xF9, + 0xE8, 0xA8, 0x62, 0xCD, 0x5C, 0x57, 0xA3, 0x6B, 0xBE, 0x6B }, + "MGsxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxl" + "TXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMSAwHgYDVQQDExdlbVNpZ24gRUND" + "IFJvb3QgQ0EgLSBHMw==", + "PPYHqWhwDtqLhA==", + }, + { + // CN=emSign Root CA - C1,O=eMudhra Inc,OU=emSign PKI,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x12, 0x56, 0x09, 0xAA, 0x30, 0x1D, 0xA0, 0xA2, 0x49, 0xB9, 0x7A, + 0x82, 0x39, 0xCB, 0x6A, 0x34, 0x21, 0x6F, 0x44, 0xDC, 0xAC, 0x9F, + 0x39, 0x54, 0xB1, 0x42, 0x92, 0xF2, 0xE8, 0xC8, 0x60, 0x8F }, + "MFYxCzAJBgNVBAYTAlVTMRMwEQYDVQQLEwplbVNpZ24gUEtJMRQwEgYDVQQKEwtl" + "TXVkaHJhIEluYzEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBDMQ==", + "AK7PALrEzzL4Q7I=", + }, + { + // CN=emSign ECC Root CA - C3,O=eMudhra Inc,OU=emSign PKI,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xBC, 0x4D, 0x80, 0x9B, 0x15, 0x18, 0x9D, 0x78, 0xDB, 0x3E, 0x1D, + 0x8C, 0xF4, 0xF9, 0x72, 0x6A, 0x79, 0x5D, 0xA1, 0x64, 0x3C, 0xA5, + 0xF1, 0x35, 0x8E, 0x1D, 0xDB, 0x0E, 0xDC, 0x0D, 0x7E, 0xB3 }, + "MFoxCzAJBgNVBAYTAlVTMRMwEQYDVQQLEwplbVNpZ24gUEtJMRQwEgYDVQQKEwtl" + "TXVkaHJhIEluYzEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gQzM=", + "e3G2gla4EnycqA==", + }, + { + // OU=certSIGN ROOT CA G2,O=CERTSIGN SA,C=RO + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x65, 0x7C, 0xFE, 0x2F, 0xA7, 0x3F, 0xAA, 0x38, 0x46, 0x25, 0x71, + 0xF3, 0x32, 0xA2, 0x36, 0x3A, 0x46, 0xFC, 0xE7, 0x02, 0x09, 0x51, + 0x71, 0x07, 0x02, 0xCD, 0xFB, 0xB6, 0xEE, 0xDA, 0x33, 0x05 }, + "MEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMT" + "Y2VydFNJR04gUk9PVCBDQSBHMg==", + "EQA0tk7GNi02", + }, + { + // CN=IdenTrust Commercial Root CA 1,O=IdenTrust,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x5D, 0x56, 0x49, 0x9B, 0xE4, 0xD2, 0xE0, 0x8B, 0xCF, 0xCA, 0xD0, + 0x8A, 0x3E, 0x38, 0x72, 0x3D, 0x50, 0x50, 0x3B, 0xDE, 0x70, 0x69, + 0x48, 0xE4, 0x2F, 0x55, 0x60, 0x30, 0x19, 0xE5, 0x28, 0xAE }, + "MEoxCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlJZGVuVHJ1c3QxJzAlBgNVBAMTHklk" + "ZW5UcnVzdCBDb21tZXJjaWFsIFJvb3QgQ0EgMQ==", + "CgFCgAAAAUUjyES1AAAAAg==", + }, + { + // CN=Trustwave Global Certification Authority,O="Trustwave Holdings, Inc.",L=Chicago,ST=Illinois,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x97, 0x55, 0x20, 0x15, 0xF5, 0xDD, 0xFC, 0x3C, 0x87, 0x88, 0xC0, 0x06, 0x94, 0x45, 0x55, 0x40, 0x88, 0x94, 0x45, 0x00, 0x84, 0xF1, 0x00, 0x86, 0x70, 0x86, 0xBC, 0x1A, 0x2B, 0xB5, 0x8D, 0xC8 }, + "MIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0No" + "aWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UE" + "AwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==", + "BfcOhtpJ80Y1Lrqy", + }, + { + // CN=Trustwave Global ECC P256 Certification Authority,O="Trustwave Holdings, Inc.",L=Chicago,ST=Illinois,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x94, 0x5B, 0xBC, 0x82, 0x5E, 0xA5, 0x54, 0xF4, 0x89, 0xD1, 0xFD, 0x51, 0xA7, 0x3D, 0xDF, 0x2E, 0xA6, 0x24, 0xAC, 0x70, 0x19, 0xA0, 0x52, 0x05, 0x22, 0x5C, 0x22, 0xA7, 0x8C, 0xCF, 0xA8, 0xB4 }, + "MIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0No" + "aWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UE" + "AxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhv" + "cml0eQ==", + "DWpfCD8oXD5Rld9d", + }, + { + // CN=Trustwave Global ECC P384 Certification Authority,O="Trustwave Holdings, Inc.",L=Chicago,ST=Illinois,C=US + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x55, 0x90, 0x38, 0x59, 0xC8, 0xC0, 0xC3, 0xEB, 0xB8, 0x75, 0x9E, 0xCE, 0x4E, 0x25, 0x57, 0x22, 0x5F, 0xF5, 0x75, 0x8B, 0xBD, 0x38, 0xEB, 0xD4, 0x82, 0x76, 0x60, 0x1E, 0x1B, 0xD5, 0x80, 0x97 }, + "MIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0No" + "aWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UE" + "AxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhv" + "cml0eQ==", + "CL2Fl2yZJ6SAaEc7", + }, + { + // CN=GlobalSign Root R46,O=GlobalSign nv-sa,C=BE + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x4F, 0xA3, 0x12, 0x6D, 0x8D, 0x3A, 0x11, 0xD1, 0xC4, 0x85, 0x5A, 0x4F, 0x80, 0x7C, 0xBA, 0xD6, 0xCF, 0x91, 0x9D, 0x3A, 0x5A, 0x88, 0xB0, 0x3B, 0xEA, 0x2C, 0x63, 0x72, 0xD9, 0x3C, 0x40, 0xC9 }, + "MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD" + "VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2", + "EdK7udcjGJ5AXwqdLdDfJWfR", + }, + { + // CN=GlobalSign Root E46,O=GlobalSign nv-sa,C=BE + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xCB, 0xB9, 0xC4, 0x4D, 0x84, 0xB8, 0x04, 0x3E, 0x10, 0x50, 0xEA, 0x31, 0xA6, 0x9F, 0x51, 0x49, 0x55, 0xD7, 0xBF, 0xD2, 0xE2, 0xC6, 0xB4, 0x93, 0x01, 0x01, 0x9A, 0xD6, 0x1D, 0x9F, 0x50, 0x58 }, + "MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD" + "VQQDExNHbG9iYWxTaWduIFJvb3QgRTQ2", + "EdK7ujNu1LzmJGjFDYQdmOhD", + }, + { + // "CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS,OID.2.5.4.97=VATES-Q2826004J,OU=Ceres,O=FNMT-RCM,C=E + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x55, 0x41, 0x53, 0xB1, 0x3D, 0x2C, 0xF9, 0xDD, 0xB7, 0x53, 0xBF, 0xBE, 0x1A, 0x4E, 0x0A, 0xE0, 0x8D, 0x0A, 0xA4, 0x18, 0x70, 0x58, 0xFE, 0x60, 0xA2, 0xB8, 0x62, 0xB2, 0xE4, 0xB8, 0x7B, 0xCB }, + "MHgxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEOMAwGA1UECwwFQ2Vy" + "ZXMxGDAWBgNVBGEMD1ZBVEVTLVEyODI2MDA0SjEsMCoGA1UEAwwjQUMgUkFJWiBG" + "Tk1ULVJDTSBTRVJWSURPUkVTIFNFR1VST1M=", + "YvYybOXE42hcG2LdnC6dlQ==", + }, + { + // CN=GLOBALTRUST 2020,O=e-commerce monitoring GmbH,C=AT + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x9A, 0x29, 0x6A, 0x51, 0x82, 0xD1, 0xD4, 0x51, 0xA2, 0xE3, 0x7F, 0x43, 0x9B, 0x74, 0xDA, 0xAF, 0xA2, 0x67, 0x52, 0x33, 0x29, 0xF9, 0x0F, 0x9A, 0x0D, 0x20, 0x07, 0xC3, 0x34, 0xE2, 0x3C, 0x9A }, + "ME0xCzAJBgNVBAYTAkFUMSMwIQYDVQQKExplLWNvbW1lcmNlIG1vbml0b3Jpbmcg" + "R21iSDEZMBcGA1UEAxMQR0xPQkFMVFJVU1QgMjAyMA==", + "Wku9WvtPilv6ZeU=", + }, + { + // CN=Certum Extended Validation ECC CA,OU=Certum Certification Authority,O=Asseco Data Systems S.A.,C=PL + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x6B, 0x32, 0x80, 0x85, 0x62, 0x53, 0x18, 0xAA, 0x50, 0xD1, 0x73, 0xC9, 0x8D, 0x8B, 0xDA, 0x09, 0xD5, 0x7E, 0x27, 0x41, 0x3D, 0x11, 0x4C, 0xF7, 0x87, 0xA0, 0xF5, 0xD0, 0x6C, 0x03, 0x0C, 0xF6 }, + "MHQxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMu" + "QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEZMBcG" + "A1UEAxMQQ2VydHVtIEVDLTM4NCBDQQ==", + "eI8nXIESUiClBNAt3bpz9A==", + }, + { + // CN=Certum Extended Validation RSA CA,OU=Certum Certification Authority,O=Asseco Data Systems S.A.,C=PL + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xFE, 0x76, 0x96, 0x57, 0x38, 0x55, 0x77, 0x3E, 0x37, 0xA9, 0x5E, 0x7A, 0xD4, 0xD9, 0xCC, 0x96, 0xC3, 0x01, 0x57, 0xC1, 0x5D, 0x31, 0x76, 0x5B, 0xA9, 0xB1, 0x57, 0x04, 0xE1, 0xAE, 0x78, 0xFD }, + "MHoxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMu" + "QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEfMB0G" + "A1UEAxMWQ2VydHVtIFRydXN0ZWQgUm9vdCBDQQ==", + "Hr9ZULjJgDdMBvfrVU+17Q==", + }, + { + // CN=ANF Secure Server Root CA,OU=ANF CA Raiz,O=ANF Autoridad de Certificacion,C=ES,serialNumber=G63287510 + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xFB, 0x8F, 0xEC, 0x75, 0x91, 0x69, 0xB9, 0x10, 0x6B, 0x1E, 0x51, 0x16, 0x44, 0xC6, 0x18, 0xC5, 0x13, 0x04, 0x37, 0x3F, 0x6C, 0x06, 0x43, 0x08, 0x8D, 0x8B, 0xEF, 0xFD, 0x1B, 0x99, 0x75, 0x99 }, + "MIGEMRIwEAYDVQQFEwlHNjMyODc1MTAxCzAJBgNVBAYTAkVTMScwJQYDVQQKEx5B" + "TkYgQXV0b3JpZGFkIGRlIENlcnRpZmljYWNpb24xFDASBgNVBAsTC0FORiBDQSBS" + "YWl6MSIwIAYDVQQDExlBTkYgU2VjdXJlIFNlcnZlciBSb290IENB", + "DdPjvGz5a7E=", + }, + { + // CN=Hellenic Academic and Research Institutions ECC RootCA 2015,O=Hellenic Academic and Research Institutions Cert. Authority,L=Athens,C=GR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x44, 0xB5, 0x45, 0xAA, 0x8A, 0x25, 0xE6, 0x5A, 0x73, 0xCA, 0x15, + 0xDC, 0x27, 0xFC, 0x36, 0xD2, 0x4C, 0x1C, 0xB9, 0x95, 0x3A, 0x06, + 0x65, 0x39, 0xB1, 0x15, 0x82, 0xDC, 0x48, 0x7B, 0x48, 0x33 }, + "MIGqMQswCQYDVQQGEwJHUjEPMA0GA1UEBxMGQXRoZW5zMUQwQgYDVQQKEztIZWxs" + "ZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENlcnQuIEF1" + "dGhvcml0eTFEMEIGA1UEAxM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo" + "IEluc3RpdHV0aW9ucyBFQ0MgUm9vdENBIDIwMTU=", + "AA==", + }, + { + // CN=Hellenic Academic and Research Institutions RootCA 2015,O=Hellenic Academic and Research Institutions Cert. Authority,L=Athens,C=GR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xA0, 0x40, 0x92, 0x9A, 0x02, 0xCE, 0x53, 0xB4, 0xAC, 0xF4, 0xF2, + 0xFF, 0xC6, 0x98, 0x1C, 0xE4, 0x49, 0x6F, 0x75, 0x5E, 0x6D, 0x45, + 0xFE, 0x0B, 0x2A, 0x69, 0x2B, 0xCD, 0x52, 0x52, 0x3F, 0x36 }, + "MIGmMQswCQYDVQQGEwJHUjEPMA0GA1UEBxMGQXRoZW5zMUQwQgYDVQQKEztIZWxs" + "ZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENlcnQuIEF1" + "dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo" + "IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxNQ==", + "AA==", + }, + { + // CN=HARICA TLS RSA Root CA 2021,O=Hellenic Academic and Research Institutions CA,C=GR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xD9, 0x5D, 0x0E, 0x8E, 0xDA, 0x79, 0x52, 0x5B, 0xF9, 0xBE, 0xB1, + 0x1B, 0x14, 0xD2, 0x10, 0x0D, 0x32, 0x94, 0x98, 0x5F, 0x0C, 0x62, + 0xD9, 0xFA, 0xBD, 0x9C, 0xD9, 0x99, 0xEC, 0xCB, 0x7B, 0x1D }, + "MGwxCzAJBgNVBAYTAkdSMTcwNQYDVQQKDC5IZWxsZW5pYyBBY2FkZW1pYyBhbmQg" + "UmVzZWFyY2ggSW5zdGl0dXRpb25zIENBMSQwIgYDVQQDDBtIQVJJQ0EgVExTIFJT" + "QSBSb290IENBIDIwMjE=", + "OcqTHO9D88aOk8f0ZIk4fg==", + }, + { + // CN=HARICA TLS ECC Root CA 2021,O=Hellenic Academic and Research Institutions CA,C=GR + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x3F, 0x99, 0xCC, 0x47, 0x4A, 0xCF, 0xCE, 0x4D, 0xFE, 0xD5, 0x87, + 0x94, 0x66, 0x5E, 0x47, 0x8D, 0x15, 0x47, 0x73, 0x9F, 0x2E, 0x78, + 0x0F, 0x1B, 0xB4, 0xCA, 0x9B, 0x13, 0x30, 0x97, 0xD4, 0x01 }, + "MGwxCzAJBgNVBAYTAkdSMTcwNQYDVQQKDC5IZWxsZW5pYyBBY2FkZW1pYyBhbmQg" + "UmVzZWFyY2ggSW5zdGl0dXRpb25zIENBMSQwIgYDVQQDDBtIQVJJQ0EgVExTIEVD" + "QyBSb290IENBIDIwMjE=", + "Z3SdjXfYO2rbIvT/WeK/zg==", + }, + { + // CN=HiPKI Root CA - G1,O="Chunghwa Telecom Co., Ltd.",C=TW + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xF0, 0x15, 0xCE, 0x3C, 0xC2, 0x39, 0xBF, 0xEF, 0x06, 0x4B, 0xE9, + 0xF1, 0xD2, 0xC4, 0x17, 0xE1, 0xA0, 0x26, 0x4A, 0x0A, 0x94, 0xBE, + 0x1F, 0x0C, 0x8D, 0x12, 0x18, 0x64, 0xEB, 0x69, 0x49, 0xCC }, + "ME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwg" + "THRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx", + "Ld2szmKXlKFD6LDNdmpeYA==", + }, + { + // CN=vTrus Root CA,O="iTrusChina Co.,Ltd.",C=CN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x8A, 0x71, 0xDE, 0x65, 0x59, 0x33, 0x6F, 0x42, 0x6C, 0x26, 0xE5, + 0x38, 0x80, 0xD0, 0x0D, 0x88, 0xA1, 0x8D, 0xA4, 0xC6, 0xA9, 0x1F, + 0x0D, 0xCB, 0x61, 0x94, 0xE2, 0x06, 0xC5, 0xC9, 0x63, 0x87 }, + "MEMxCzAJBgNVBAYTAkNOMRwwGgYDVQQKExNpVHJ1c0NoaW5hIENvLixMdGQuMRYw" + "FAYDVQQDEw12VHJ1cyBSb290IENB", + "Q+NxE9izWRRdt86M/TX9b7wFjUU=", + }, + { + // CN=vTrus ECC Root CA,O="iTrusChina Co.,Ltd.",C=CN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x30, 0xFB, 0xBA, 0x2C, 0x32, 0x23, 0x8E, 0x2A, 0x98, 0x54, 0x7A, + 0xF9, 0x79, 0x31, 0xE5, 0x50, 0x42, 0x8B, 0x9B, 0x3F, 0x1C, 0x8E, + 0xEB, 0x66, 0x33, 0xDC, 0xFA, 0x86, 0xC5, 0xB2, 0x7D, 0xD3 }, + "MEcxCzAJBgNVBAYTAkNOMRwwGgYDVQQKExNpVHJ1c0NoaW5hIENvLixMdGQuMRow" + "GAYDVQQDExF2VHJ1cyBFQ0MgUm9vdCBDQQ==", + "bmq8WapTvpg5Z6LSa6Q75m0c1to=", + }, + { + // CN=Autoridad de Certificacion Firmaprofesional CIF A62634068,C=ES + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x57, 0xDE, 0x05, 0x83, 0xEF, 0xD2, 0xB2, 0x6E, 0x03, 0x61, 0xDA, + 0x99, 0xDA, 0x9D, 0xF4, 0x64, 0x8D, 0xEF, 0x7E, 0xE8, 0x44, 0x1C, + 0x3B, 0x72, 0x8A, 0xFA, 0x9B, 0xCD, 0xE0, 0xF9, 0xB2, 0x6A }, + "MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNh" + "Y2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=", + "G3Dp0v+ubHE=", + }, + { + // CN=NetLock Arany (Class Gold) FÅ‘tanúsÃtvány,OU=TanúsÃtványkiadók (Certification Services),O=NetLock Kft.,L=Budapest,C=HU + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x6C, 0x61, 0xDA, 0xC3, 0xA2, 0xDE, 0xF0, 0x31, 0x50, 0x6B, 0xE0, + 0x36, 0xD2, 0xA6, 0xFE, 0x40, 0x19, 0x94, 0xFB, 0xD1, 0x3D, 0xF9, + 0xC8, 0xD4, 0x66, 0x59, 0x92, 0x74, 0xC4, 0x46, 0xEC, 0x98 }, + "MIGnMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5l" + "dExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0" + "aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xh" + "c3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnk=", + "SUEs5AAQ", + }, + { + // CN=D-TRUST EV Root CA 1 2020,O=D-Trust GmbH,C=DE + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x08, 0x17, 0x0D, 0x1A, 0xA3, 0x64, 0x53, 0x90, 0x1A, 0x2F, 0x95, + 0x92, 0x45, 0xE3, 0x47, 0xDB, 0x0C, 0x8D, 0x37, 0xAB, 0xAA, 0xBC, + 0x56, 0xB8, 0x1A, 0xA1, 0x00, 0xDC, 0x95, 0x89, 0x70, 0xDB }, + "MEgxCzAJBgNVBAYTAkRFMRUwEwYDVQQKEwxELVRydXN0IEdtYkgxIjAgBgNVBAMT" + "GUQtVFJVU1QgRVYgUm9vdCBDQSAxIDIwMjA=", + "XwJB13qHfEwDo6yWjfv/0A==", + }, + { + // CN=BJCA Global Root CA1,O=BEIJING CERTIFICATE AUTHORITY,C=CN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0xF3, 0x89, 0x6F, 0x88, 0xFE, 0x7C, 0x0A, 0x88, 0x27, 0x66, 0xA7, + 0xFA, 0x6A, 0xD2, 0x74, 0x9F, 0xB5, 0x7A, 0x7F, 0x3E, 0x98, 0xFB, + 0x76, 0x9C, 0x1F, 0xA7, 0xB0, 0x9C, 0x2C, 0x44, 0xD5, 0xAE }, + "MFQxCzAJBgNVBAYTAkNOMSYwJAYDVQQKDB1CRUlKSU5HIENFUlRJRklDQVRFIEFV" + "VEhPUklUWTEdMBsGA1UEAwwUQkpDQSBHbG9iYWwgUm9vdCBDQTE=", + "VW9l47TZkGobCdFsPsBsIA==", + }, + { + // CN=BJCA Global Root CA2,O=BEIJING CERTIFICATE AUTHORITY,C=CN + "2.23.140.1.1", + "CA/Browser Forum EV OID", + { 0x57, 0x4D, 0xF6, 0x93, 0x1E, 0x27, 0x80, 0x39, 0x66, 0x7B, 0x72, + 0x0A, 0xFD, 0xC1, 0x60, 0x0F, 0xC2, 0x7E, 0xB6, 0x6D, 0xD3, 0x09, + 0x29, 0x79, 0xFB, 0x73, 0x85, 0x64, 0x87, 0x21, 0x28, 0x82 }, + "MFQxCzAJBgNVBAYTAkNOMSYwJAYDVQQKDB1CRUlKSU5HIENFUlRJRklDQVRFIEFV" + "VEhPUklUWTEdMBsGA1UEAwwUQkpDQSBHbG9iYWwgUm9vdCBDQTI=", + "LBcIfWQqwP6FGFkGz7RK6w==", + }, + // clang-format on +}; + +static pkix::CertPolicyId sEVInfoIds[ArrayLength(kEVInfos)]; +static_assert( + ArrayLength(sEVInfoIds) == ArrayLength(kEVInfos), + "These arrays are used in parallel and must have the same length."); +static pkix::CertPolicyId sCABForumEVId = {}; + +bool CertIsAuthoritativeForEVPolicy(const nsTArray<uint8_t>& certBytes, + const pkix::CertPolicyId& policy) { + nsTArray<uint8_t> fingerprint; + nsresult rv = Digest::DigestBuf(SEC_OID_SHA256, certBytes.Elements(), + certBytes.Length(), fingerprint); + if (NS_FAILED(rv)) { + return false; + } + if (fingerprint.Length() != SHA256_LENGTH) { + return false; + } + + for (size_t i = 0; i < ArrayLength(kEVInfos); ++i) { + const EVInfo& entry = kEVInfos[i]; + + // This check ensures that only the specific roots we approve for EV get + // that status, and not certs (roots or otherwise) that happen to have an + // OID that's already been approved for EV. + if (!ArrayEqual(&fingerprint[0], &entry.sha256Fingerprint[0], + SHA256_LENGTH)) { + continue; + } + + if (policy == sCABForumEVId || policy == sEVInfoIds[i]) { + return true; + } + } + + return false; +} + +nsresult LoadExtendedValidationInfo() { + static const char* sCABForumOIDString = "2.23.140.1.1"; + + ScopedAutoSECItem cabforumOIDItem; + if (SEC_StringToOID(nullptr, &cabforumOIDItem, sCABForumOIDString, 0) != + SECSuccess) { + return NS_ERROR_FAILURE; + } + if (cabforumOIDItem.len > pkix::CertPolicyId::MAX_BYTES) { + return NS_ERROR_UNEXPECTED; + } + + sCABForumEVId.numBytes = cabforumOIDItem.len; + PodCopy(sCABForumEVId.bytes, cabforumOIDItem.data, sCABForumEVId.numBytes); + + for (size_t i = 0; i < ArrayLength(kEVInfos); ++i) { + const EVInfo& entry = kEVInfos[i]; + + SECStatus srv; +#ifdef DEBUG + // This section of code double-checks that we calculated the correct + // certificate hash given the issuer and serial number and that it is + // actually present in our loaded root certificates module. It is + // unnecessary to check this in non-debug builds since we will safely fall + // back to DV if the EV information is incorrect. + nsAutoCString derIssuer; + nsresult rv = + Base64Decode(nsDependentCString(entry.issuerBase64), derIssuer); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not base64-decode built-in EV issuer"); + if (NS_FAILED(rv)) { + return rv; + } + + nsAutoCString serialNumber; + rv = Base64Decode(nsDependentCString(entry.serialBase64), serialNumber); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not base64-decode built-in EV serial"); + if (NS_FAILED(rv)) { + return rv; + } + + CERTIssuerAndSN ias; + ias.derIssuer.data = + BitwiseCast<unsigned char*, const char*>(derIssuer.get()); + ias.derIssuer.len = derIssuer.Length(); + ias.serialNumber.data = + BitwiseCast<unsigned char*, const char*>(serialNumber.get()); + ias.serialNumber.len = serialNumber.Length(); + ias.serialNumber.type = siUnsignedInteger; + + UniqueCERTCertificate cert(CERT_FindCertByIssuerAndSN(nullptr, &ias)); + + // If an entry is missing in the NSS root database, it may be because the + // root database is out of sync with what we expect (e.g. a different + // version of system NSS is installed). + if (!cert) { + // The entries for the debug EV roots are at indices 0 through + // NUM_TEST_EV_ROOTS - 1. Since they're not built-in, they probably + // haven't been loaded yet. + MOZ_ASSERT(i < NUM_TEST_EV_ROOTS, "Could not find built-in EV root"); + } else { + unsigned char certFingerprint[SHA256_LENGTH]; + srv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data, + AssertedCast<int32_t>(cert->derCert.len)); + MOZ_ASSERT(srv == SECSuccess, "Could not hash EV root"); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + bool same = ArrayEqual(certFingerprint, entry.sha256Fingerprint); + MOZ_ASSERT(same, "EV root fingerprint mismatch"); + if (!same) { + return NS_ERROR_FAILURE; + } + } +#endif + // This is the code that actually enables these roots for EV. + ScopedAutoSECItem evOIDItem; + srv = SEC_StringToOID(nullptr, &evOIDItem, entry.dottedOid, 0); + MOZ_ASSERT(srv == SECSuccess, "SEC_StringToOID failed"); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + if (evOIDItem.len > pkix::CertPolicyId::MAX_BYTES) { + return NS_ERROR_UNEXPECTED; + } + sEVInfoIds[i].numBytes = evOIDItem.len; + PodCopy(sEVInfoIds[i].bytes, evOIDItem.data, sEVInfoIds[i].numBytes); + } + + return NS_OK; +} + +// Helper function for GetKnownEVPolicies(): reads an EV Policy if there is one, +// and appends it to the given list of CertPolicyIds. +void FindMatchingEVPolicy(pkix::Reader& idReader, + nsTArray<pkix::CertPolicyId>& policies) { + pkix::Input cabForumEVIdBytes; + pkix::Result rv = + cabForumEVIdBytes.Init(sCABForumEVId.bytes, sCABForumEVId.numBytes); + if (rv == pkix::Success && idReader.MatchRest(cabForumEVIdBytes)) { + policies.AppendElement(sCABForumEVId); + return; + } + + for (const pkix::CertPolicyId& id : sEVInfoIds) { + pkix::Input idBytes; + rv = idBytes.Init(id.bytes, id.numBytes); + if (rv == pkix::Success && idReader.MatchRest(idBytes)) { + policies.AppendElement(id); + return; + } + } +} + +void GetKnownEVPolicies(const nsTArray<uint8_t>& certBytes, + /*out*/ nsTArray<pkix::CertPolicyId>& policies) { + pkix::Input certInput; + pkix::Result rv = certInput.Init(certBytes.Elements(), certBytes.Length()); + if (rv != pkix::Success) { + return; + } + // we don't use the certificate for path building, so this parameter + // doesn't matter + pkix::EndEntityOrCA notUsedForPaths = pkix::EndEntityOrCA::MustBeEndEntity; + pkix::BackCert cert(certInput, notUsedForPaths, nullptr); + rv = cert.Init(); + if (rv != pkix::Success) { + return; + } + + const pkix::Input* extensionInput = cert.GetCertificatePolicies(); + if (!extensionInput) { + return; + } + + pkix::Reader extension(*extensionInput); + pkix::Reader certificatePolicies; + // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + // PolicyInformation ::= SEQUENCE { + // policyIdentifier CertPolicyId, + // ... + // } + // CertPolicyId ::= OBJECT IDENTIFIER + rv = pkix::der::ExpectTagAndGetValue(extension, pkix::der::SEQUENCE, + certificatePolicies); + if (rv != pkix::Success || !extension.AtEnd()) { + return; + } + + do { + pkix::Reader policyInformation; + rv = pkix::der::ExpectTagAndGetValue( + certificatePolicies, pkix::der::SEQUENCE, policyInformation); + if (rv != pkix::Success) { + return; + } + + pkix::Reader policyOid; + rv = pkix::der::ExpectTagAndGetValue(policyInformation, pkix::der::OIDTag, + policyOid); + if (rv != pkix::Success) { + return; + } + + // we don't validate policy qualifiers here + FindMatchingEVPolicy(policyOid, policies); + } while (!certificatePolicies.AtEnd()); +} + +} // namespace psm +} // namespace mozilla diff --git a/security/certverifier/ExtendedValidation.h b/security/certverifier/ExtendedValidation.h new file mode 100644 index 0000000000..c090f6d8e3 --- /dev/null +++ b/security/certverifier/ExtendedValidation.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ExtendedValidation_h +#define ExtendedValidation_h + +#include "ScopedNSSTypes.h" +#include "certt.h" + +namespace mozilla { +namespace pkix { +struct CertPolicyId; +} // namespace pkix +} // namespace mozilla + +namespace mozilla { +namespace psm { + +nsresult LoadExtendedValidationInfo(); + +/** + * Finds all policy OIDs in the given cert that are known to be EV policy OIDs. + * + * @param cert + * The bytes of the cert to find the EV policies of. + * @param policies + * The found policies. + */ +void GetKnownEVPolicies( + const nsTArray<uint8_t>& cert, + /*out*/ nsTArray<mozilla::pkix::CertPolicyId>& policies); + +// CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted +// or distrusted. +bool CertIsAuthoritativeForEVPolicy(const nsTArray<uint8_t>& cert, + const mozilla::pkix::CertPolicyId& policy); + +} // namespace psm +} // namespace mozilla + +#endif // ExtendedValidation_h diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp new file mode 100644 index 0000000000..0c128eabf2 --- /dev/null +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -0,0 +1,2029 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NSSCertDBTrustDomain.h" + +#include <stdint.h> +#include <utility> + +#include "CRLiteTimestamp.h" +#include "ExtendedValidation.h" +#include "MultiLogCTVerifier.h" +#include "NSSErrorsService.h" +#include "PublicKeyPinningService.h" +#include "cert.h" +#include "cert_storage/src/cert_storage.h" +#include "certdb.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/Assertions.h" +#include "mozilla/Casting.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Logging.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_security.h" +#include "mozilla/SyncRunnable.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Unused.h" +#include "mozpkix/Result.h" +#include "mozpkix/pkix.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixutil.h" +#include "nsCRTGlue.h" +#include "nsIObserverService.h" +#include "nsNetCID.h" +#include "nsNSSCallbacks.h" +#include "nsNSSCertHelper.h" +#include "nsNSSCertificate.h" +#include "nsNSSCertificateDB.h" +#include "nsNSSIOLayer.h" +#include "nsPrintfCString.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "nss.h" +#include "pk11pub.h" +#include "prerror.h" +#include "secder.h" +#include "secerr.h" + +#ifdef MOZ_WIDGET_COCOA +# include "nsCocoaFeatures.h" +#endif + +#include "TrustOverrideUtils.h" +#include "TrustOverride-AppleGoogleDigiCertData.inc" +#include "TrustOverride-SymantecData.inc" + +using namespace mozilla; +using namespace mozilla::ct; +using namespace mozilla::pkix; + +extern LazyLogModule gCertVerifierLog; + +static const uint64_t ServerFailureDelaySeconds = 5 * 60; + +namespace mozilla { +namespace psm { + +NSSCertDBTrustDomain::NSSCertDBTrustDomain( + SECTrustType certDBTrustType, OCSPFetching ocspFetching, + OCSPCache& ocspCache, + /*optional but shouldn't be*/ void* pinArg, TimeDuration ocspTimeoutSoft, + TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays, + unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, + NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode, + const OriginAttributes& originAttributes, + const Vector<Input>& thirdPartyRootInputs, + const Vector<Input>& thirdPartyIntermediateInputs, + const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, + /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo, + /*optional*/ const char* hostname) + : mCertDBTrustType(certDBTrustType), + mOCSPFetching(ocspFetching), + mOCSPCache(ocspCache), + mPinArg(pinArg), + mOCSPTimeoutSoft(ocspTimeoutSoft), + mOCSPTimeoutHard(ocspTimeoutHard), + mCertShortLifetimeInDays(certShortLifetimeInDays), + mMinRSABits(minRSABits), + mValidityCheckingMode(validityCheckingMode), + mNetscapeStepUpPolicy(netscapeStepUpPolicy), + mCRLiteMode(crliteMode), + mSawDistrustedCAByPolicyError(false), + mOriginAttributes(originAttributes), + mThirdPartyRootInputs(thirdPartyRootInputs), + mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs), + mExtraCertificates(extraCertificates), + mBuiltChain(builtChain), + mIsBuiltChainRootBuiltInRoot(false), + mPinningTelemetryInfo(pinningTelemetryInfo), + mHostname(hostname), + mCertStorage(do_GetService(NS_CERT_STORAGE_CID)), + mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED), + mSCTListFromCertificate(), + mSCTListFromOCSPStapling(), + mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName)), + mOCSPFetchStatus(OCSPFetchStatus::NotFetched) {} + +static void FindRootsWithSubject(UniqueSECMODModule& rootsModule, + SECItem subject, + /*out*/ nsTArray<nsTArray<uint8_t>>& roots) { + MOZ_ASSERT(rootsModule); + AutoSECMODListReadLock lock; + for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) { + CERTCertificateList* rawResults = nullptr; + if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject, + &rawResults) != SECSuccess) { + continue; + } + // rawResults == nullptr means we didn't find any matching certificates + if (!rawResults) { + continue; + } + UniqueCERTCertificateList results(rawResults); + for (int certIndex = 0; certIndex < results->len; certIndex++) { + nsTArray<uint8_t> root; + root.AppendElements(results->certs[certIndex].data, + results->certs[certIndex].len); + roots.AppendElement(std::move(root)); + } + } +} + +// A self-signed issuer certificate should never be necessary in order to build +// a trusted certificate chain unless it is a trust anchor. This is because if +// it were necessary, there would exist another certificate with the same +// subject and public key that is also a valid issing certificate. Given this +// certificate, it is possible to build another chain using just it instead of +// it and the self-signed certificate. This is only true as long as the +// certificate extensions we support are restrictive rather than additive in +// terms of the rest of the chain (for example, we don't support policy mapping +// and we ignore any SCT information in intermediates). +static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain, + Input certDER) { + BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr); + if (cert.Init() != Success) { + return false; // turn any failures into "don't skip trying this cert" + } + // If subject != issuer, this isn't a self-signed cert. + if (!InputsAreEqual(cert.GetSubject(), cert.GetIssuer())) { + return false; + } + TrustLevel trust; + if (trustDomain.GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, + certDER, trust) != Success) { + return false; + } + // If the trust for this certificate is anything other than "inherit", we want + // to process it like normal. + if (trust != TrustLevel::InheritsTrust) { + return false; + } + if (VerifySignedData(trustDomain, cert.GetSignedData(), + cert.GetSubjectPublicKeyInfo()) != Success) { + return false; + } + // This is a self-signed, non-trust-anchor certificate, so we shouldn't use it + // for path building. See bug 1056341. + return true; +} + +static Result CheckCandidates(TrustDomain& trustDomain, + TrustDomain::IssuerChecker& checker, + nsTArray<Input>& candidates, + Input* nameConstraintsInputPtr, bool& keepGoing) { + for (Input candidate : candidates) { + // Stop path building if the program is shutting down. + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + keepGoing = false; + return Success; + } + if (ShouldSkipSelfSignedNonTrustAnchor(trustDomain, candidate)) { + continue; + } + Result rv = checker.Check(candidate, nameConstraintsInputPtr, keepGoing); + if (rv != Success) { + return rv; + } + if (!keepGoing) { + return Success; + } + } + + return Success; +} + +Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, + IssuerChecker& checker, Time) { + SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName); + // Handle imposed name constraints, if any. + ScopedAutoSECItem nameConstraints; + Input nameConstraintsInput; + Input* nameConstraintsInputPtr = nullptr; + SECStatus srv = + CERT_GetImposedNameConstraints(&encodedIssuerNameItem, &nameConstraints); + if (srv == SECSuccess) { + if (nameConstraintsInput.Init(nameConstraints.data, nameConstraints.len) != + Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nameConstraintsInputPtr = &nameConstraintsInput; + } else if (PR_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + // First try all relevant certificates known to Gecko, which avoids calling + // CERT_CreateSubjectCertList, because that can be expensive. + nsTArray<Input> geckoRootCandidates; + nsTArray<Input> geckoIntermediateCandidates; + + if (!mCertStorage) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsTArray<uint8_t> subject; + subject.AppendElements(encodedIssuerName.UnsafeGetData(), + encodedIssuerName.GetLength()); + nsTArray<nsTArray<uint8_t>> certs; + nsresult rv = mCertStorage->FindCertsBySubject(subject, certs); + if (NS_FAILED(rv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + for (auto& cert : certs) { + Input certDER; + Result rv = certDER.Init(cert.Elements(), cert.Length()); + if (rv != Success) { + continue; // probably too big + } + // Currently we're only expecting intermediate certificates in cert storage. + geckoIntermediateCandidates.AppendElement(std::move(certDER)); + } + + // We might not have this module if e.g. we're on a Linux distribution that + // does something unexpected. + nsTArray<nsTArray<uint8_t>> builtInRoots; + if (mBuiltInRootsModule) { + FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem, + builtInRoots); + for (const auto& root : builtInRoots) { + Input rootInput; + Result rv = rootInput.Init(root.Elements(), root.Length()); + if (rv != Success) { + continue; // probably too big + } + geckoRootCandidates.AppendElement(rootInput); + } + } else { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module")); + } + + for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) { + BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr); + Result rv = root.Init(); + if (rv != Success) { + continue; + } + // Filter out 3rd party roots that can't be issuers we're looking for + // because the subject distinguished name doesn't match. This prevents + // mozilla::pkix from accumulating spurious errors during path building. + if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) { + continue; + } + geckoRootCandidates.AppendElement(thirdPartyRootInput); + } + + for (const auto& thirdPartyIntermediateInput : + mThirdPartyIntermediateInputs) { + BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA, + nullptr); + Result rv = intermediate.Init(); + if (rv != Success) { + continue; + } + // Filter out 3rd party intermediates that can't be issuers we're looking + // for because the subject distinguished name doesn't match. This prevents + // mozilla::pkix from accumulating spurious errors during path building. + if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) { + continue; + } + geckoIntermediateCandidates.AppendElement(thirdPartyIntermediateInput); + } + + if (mExtraCertificates.isSome()) { + for (const auto& extraCert : *mExtraCertificates) { + Input certInput; + Result rv = certInput.Init(extraCert.Elements(), extraCert.Length()); + if (rv != Success) { + continue; + } + BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr); + rv = cert.Init(); + if (rv != Success) { + continue; + } + // Filter out certificates that can't be issuers we're looking for because + // the subject distinguished name doesn't match. This prevents + // mozilla::pkix from accumulating spurious errors during path building. + if (!InputsAreEqual(encodedIssuerName, cert.GetSubject())) { + continue; + } + // We assume that extra certificates (presumably from the TLS handshake) + // are intermediates, since sending trust anchors would be superfluous. + geckoIntermediateCandidates.AppendElement(certInput); + } + } + + // Try all root certs first and then all (presumably) intermediates. + geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates)); + + bool keepGoing = true; + Result result = CheckCandidates(*this, checker, geckoRootCandidates, + nameConstraintsInputPtr, keepGoing); + if (result != Success) { + return result; + } + if (!keepGoing) { + return Success; + } + + // Synchronously dispatch a task to the socket thread to find + // CERTCertificates with the given subject. This involves querying NSS + // structures and databases, so it should be done on the socket thread. + nsTArray<nsTArray<uint8_t>> nssRootCandidates; + nsTArray<nsTArray<uint8_t>> nssIntermediateCandidates; + RefPtr<Runnable> getCandidatesTask = + NS_NewRunnableFunction("NSSCertDBTrustDomain::FindIssuer", [&]() { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + return; + } + // NSS seems not to differentiate between "no potential issuers found" + // and "there was an error trying to retrieve the potential issuers." We + // assume there was no error if CERT_CreateSubjectCertList returns + // nullptr. + UniqueCERTCertList candidates( + CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), + &encodedIssuerNameItem, 0, false)); + if (candidates) { + for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); + !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { + nsTArray<uint8_t> candidate; + candidate.AppendElements(n->cert->derCert.data, + n->cert->derCert.len); + if (n->cert->isRoot) { + nssRootCandidates.AppendElement(std::move(candidate)); + } else { + nssIntermediateCandidates.AppendElement(std::move(candidate)); + } + } + } + }); + nsCOMPtr<nsIEventTarget> socketThread( + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID)); + if (!socketThread) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + rv = SyncRunnable::DispatchToThread(socketThread, getCandidatesTask); + if (NS_FAILED(rv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + nsTArray<Input> nssCandidates; + for (const auto& rootCandidate : nssRootCandidates) { + Input certDER; + Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length()); + if (rv != Success) { + continue; // probably too big + } + nssCandidates.AppendElement(std::move(certDER)); + } + for (const auto& intermediateCandidate : nssIntermediateCandidates) { + Input certDER; + Result rv = certDER.Init(intermediateCandidate.Elements(), + intermediateCandidate.Length()); + if (rv != Success) { + continue; // probably too big + } + nssCandidates.AppendElement(std::move(certDER)); + } + + return CheckCandidates(*this, checker, nssCandidates, nameConstraintsInputPtr, + keepGoing); +} + +Result NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) { + // Check the certificate against the OneCRL cert blocklist + if (!mCertStorage) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + // The certificate blocklist currently only applies to TLS server + // certificates. + if (mCertDBTrustType == trustSSL) { + int16_t revocationState; + + nsTArray<uint8_t> issuerBytes; + nsTArray<uint8_t> serialBytes; + nsTArray<uint8_t> subjectBytes; + nsTArray<uint8_t> pubKeyBytes; + + Result result = + BuildRevocationCheckArrays(candidateCertDER, endEntityOrCA, issuerBytes, + serialBytes, subjectBytes, pubKeyBytes); + if (result != Success) { + return result; + } + + nsresult nsrv = mCertStorage->GetRevocationState( + issuerBytes, serialBytes, subjectBytes, pubKeyBytes, &revocationState); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + if (revocationState == nsICertStorage::STATE_ENFORCE) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: certificate is in blocklist")); + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OneCRL); + return Result::ERROR_REVOKED_CERTIFICATE; + } + } + + // This may be a third-party root. + for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) { + if (InputsAreEqual(candidateCertDER, thirdPartyRootInput)) { + trustLevel = TrustLevel::TrustAnchor; + return Success; + } + } + + // This may be a third-party intermediate. + for (const auto& thirdPartyIntermediateInput : + mThirdPartyIntermediateInputs) { + if (InputsAreEqual(candidateCertDER, thirdPartyIntermediateInput)) { + trustLevel = TrustLevel::InheritsTrust; + return Success; + } + } + + // Synchronously dispatch a task to the socket thread to construct a + // CERTCertificate and get its trust from NSS. This involves querying NSS + // structures and databases, so it should be done on the socket thread. + Result result = Result::FATAL_ERROR_LIBRARY_FAILURE; + RefPtr<Runnable> getTrustTask = + NS_NewRunnableFunction("NSSCertDBTrustDomain::GetCertTrust", [&]() { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + result = Result::FATAL_ERROR_LIBRARY_FAILURE; + return; + } + // This would be cleaner and more efficient if we could get the trust + // information without constructing a CERTCertificate here, but NSS + // doesn't expose it in any other easy-to-use fashion. The use of + // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a + // performance problem for certificates already known to NSS because NSS + // will just find the existing CERTCertificate in its in-memory cache + // and return it. For certificates not already in NSS (namely + // third-party roots and intermediates), we want to avoid calling + // CERT_NewTempCertificate repeatedly, so we've already checked if the + // candidate certificate is a third-party certificate, above. + SECItem candidateCertDERSECItem = + UnsafeMapInputToSECItem(candidateCertDER); + UniqueCERTCertificate candidateCert(CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, + true)); + if (!candidateCert) { + result = MapPRErrorCodeToResult(PR_GetError()); + return; + } + // NB: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, + // where SECSuccess means that there is a trust record and SECFailure + // means there is not a trust record. I looked at NSS's internal uses of + // CERT_GetCertTrust, and all that code uses the result as a boolean + // meaning "We have a trust record." + CERTCertTrust trust; + if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) { + uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType); + + // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, + // because we can have active distrust for either type of cert. Note + // that CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so + // if the relevant trust bit isn't set then that means the cert must + // be considered distrusted. + uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA + ? CERTDB_TRUSTED_CA + : CERTDB_TRUSTED; + if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == + CERTDB_TERMINAL_RECORD) { + trustLevel = TrustLevel::ActivelyDistrusted; + result = Success; + return; + } + + // For TRUST, we use the CERTDB_TRUSTED_CA bit. + if (flags & CERTDB_TRUSTED_CA) { + if (policy.IsAnyPolicy()) { + trustLevel = TrustLevel::TrustAnchor; + result = Success; + return; + } + + nsTArray<uint8_t> certBytes(candidateCert->derCert.data, + candidateCert->derCert.len); + if (CertIsAuthoritativeForEVPolicy(certBytes, policy)) { + trustLevel = TrustLevel::TrustAnchor; + result = Success; + return; + } + } + } + trustLevel = TrustLevel::InheritsTrust; + result = Success; + }); + nsCOMPtr<nsIEventTarget> socketThread( + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID)); + if (!socketThread) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsresult rv = SyncRunnable::DispatchToThread(socketThread, getTrustTask); + if (NS_FAILED(rv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + return result; +} + +Result NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) { + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); +} + +TimeDuration NSSCertDBTrustDomain::GetOCSPTimeout() const { + switch (mOCSPFetching) { + case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail: + return mOCSPTimeoutSoft; + case NSSCertDBTrustDomain::FetchOCSPForEV: + case NSSCertDBTrustDomain::FetchOCSPForDVHardFail: + return mOCSPTimeoutHard; + // The rest of these are error cases. Assert in debug builds, but return + // the soft timeout value in release builds. + case NSSCertDBTrustDomain::NeverFetchOCSP: + case NSSCertDBTrustDomain::LocalOnlyOCSPForEV: + MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here"); + break; + } + + MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type"); + return mOCSPTimeoutSoft; +} + +// Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and +// CERT_GetGeneralNameByType. Returns a non-Result::Success result on error, +// Success with result.IsVoid() == true when an OCSP URI was not found, and +// Success with result.IsVoid() == false when an OCSP URI was found. +static Result GetOCSPAuthorityInfoAccessLocation(const UniquePLArenaPool& arena, + Input aiaExtension, + /*out*/ nsCString& result) { + MOZ_ASSERT(arena.get()); + if (!arena.get()) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + result.Assign(VoidCString()); + SECItem aiaExtensionSECItem = UnsafeMapInputToSECItem(aiaExtension); + CERTAuthInfoAccess** aia = + CERT_DecodeAuthInfoAccessExtension(arena.get(), &aiaExtensionSECItem); + if (!aia) { + return Result::ERROR_CERT_BAD_ACCESS_LOCATION; + } + for (size_t i = 0; aia[i]; ++i) { + if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) { + // NSS chooses the **last** OCSP URL; we choose the **first** + CERTGeneralName* current = aia[i]->location; + if (!current) { + continue; + } + do { + if (current->type == certURI) { + const SECItem& location = current->name.other; + // (location.len + 1) must be small enough to fit into a uint32_t, + // but we limit it to a smaller bound to reduce OOM risk. + if (location.len > 1024 || memchr(location.data, 0, location.len)) { + // Reject embedded nulls. (NSS doesn't do this) + return Result::ERROR_CERT_BAD_ACCESS_LOCATION; + } + result.Assign(nsDependentCSubstring( + reinterpret_cast<const char*>(location.data), location.len)); + return Success; + } + current = CERT_GetNextGeneralName(current); + } while (current != aia[i]->location); + } + } + + return Success; +} + +NS_IMPL_ISUPPORTS(CRLiteTimestamp, nsICRLiteTimestamp) + +NS_IMETHODIMP +CRLiteTimestamp::GetLogID(nsTArray<uint8_t>& aLogID) { + aLogID.Clear(); + aLogID.AppendElements(mLogID); + return NS_OK; +} + +NS_IMETHODIMP +CRLiteTimestamp::GetTimestamp(uint64_t* aTimestamp) { + *aTimestamp = mTimestamp; + return NS_OK; +} + +Result BuildCRLiteTimestampArray( + Input sctExtension, + /*out*/ nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps) { + Input sctList; + Result rv = + ExtractSignedCertificateTimestampListFromExtension(sctExtension, sctList); + if (rv != Success) { + return rv; + } + std::vector<SignedCertificateTimestamp> decodedSCTs; + size_t decodingErrors; + DecodeSCTs(sctList, decodedSCTs, decodingErrors); + Unused << decodingErrors; + + for (const auto& sct : decodedSCTs) { + timestamps.AppendElement(new CRLiteTimestamp(sct)); + } + return Success; +} + +Result NSSCertDBTrustDomain::CheckCRLiteStash( + const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes, + const nsTArray<uint8_t>& serialNumberBytes) { + // This information is deterministic and has already been validated by our + // infrastructure (it comes from signed CRLs), so if the stash says a + // certificate is revoked, it is. + bool isRevokedByStash = false; + nsresult rv = mCertStorage->IsCertRevokedByStash( + issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash); + if (NS_FAILED(rv)) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash " + "failed")); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (isRevokedByStash) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash " + "returned true")); + return Result::ERROR_REVOKED_CERTIFICATE; + } + return Success; +} + +Result NSSCertDBTrustDomain::CheckCRLite( + const nsTArray<uint8_t>& issuerBytes, + const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes, + const nsTArray<uint8_t>& serialNumberBytes, + const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps, + /*out*/ bool& filterCoversCertificate) { + filterCoversCertificate = false; + int16_t crliteRevocationState; + nsresult rv = mCertStorage->GetCRLiteRevocationState( + issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes, + timestamps, &crliteRevocationState); + if (NS_FAILED(rv)) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed")); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckCRLite: CRLite check returned " + "state=%hd", + crliteRevocationState)); + + switch (crliteRevocationState) { + case nsICertStorage::STATE_ENFORCE: + filterCoversCertificate = true; + return Result::ERROR_REVOKED_CERTIFICATE; + case nsICertStorage::STATE_UNSET: + filterCoversCertificate = true; + return Success; + case nsICertStorage::STATE_NOT_ENROLLED: + filterCoversCertificate = false; + return Success; + case nsICertStorage::STATE_NOT_COVERED: + filterCoversCertificate = false; + return Success; + case nsICertStorage::STATE_NO_FILTER: + filterCoversCertificate = false; + return Success; + default: + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckCRLite: Unknown CRLite revocation " + "state")); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } +} + +Result NSSCertDBTrustDomain::CheckRevocation( + EndEntityOrCA endEntityOrCA, const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPResponse, + /*optional*/ const Input* aiaExtension, + /*optional*/ const Input* sctExtension) { + // Actively distrusted certificates will have already been blocked by + // GetCertTrust. + + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: Top of CheckRevocation\n")); + + // None of the revocation methods in this function are consulted for CA + // certificates. Revocation for CAs is handled by GetCertTrust. + if (endEntityOrCA == EndEntityOrCA::MustBeCA) { + return Success; + } + + // Look for an OCSP Authority Information Access URL. Our behavior in + // ConfirmRevocations mode depends on whether a synchronous OCSP + // request is possible. + nsCString aiaLocation(VoidCString()); + if (aiaExtension) { + UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { + return Result::FATAL_ERROR_NO_MEMORY; + } + Result rv = + GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation); + if (rv != Success) { + return rv; + } + } + + bool crliteCoversCertificate = false; + Result crliteResult = Success; + if (mCRLiteMode != CRLiteMode::Disabled && sctExtension) { + crliteResult = + CheckRevocationByCRLite(certID, *sctExtension, crliteCoversCertificate); + + // If CheckCRLite returned an error other than "revoked certificate", + // propagate that error. + if (crliteResult != Success && + crliteResult != Result::ERROR_REVOKED_CERTIFICATE) { + return crliteResult; + } + + if (crliteCoversCertificate) { + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CRLite); + // If we don't return here we will consult OCSP. + // In Enforce CRLite mode we can return "Revoked" or "Not Revoked" + // without consulting OCSP. + if (mCRLiteMode == CRLiteMode::Enforce) { + return crliteResult; + } + // If we don't have a URL for an OCSP responder, then we can return any + // result ConfirmRevocations mode. Note that we might have a + // stapled or cached OCSP response which we ignore in this case. + if (mCRLiteMode == CRLiteMode::ConfirmRevocations && + aiaLocation.IsVoid()) { + return crliteResult; + } + // In ConfirmRevocations mode we can return "Not Revoked" + // without consulting OCSP. + if (mCRLiteMode == CRLiteMode::ConfirmRevocations && + crliteResult == Success) { + return Success; + } + } + } + + bool ocspSoftFailure = false; + Result ocspResult = CheckRevocationByOCSP( + certID, time, validityDuration, aiaLocation, crliteCoversCertificate, + crliteResult, stapledOCSPResponse, ocspSoftFailure); + + // In ConfirmRevocations mode we treat any OCSP failure as confirmation + // of a CRLite revoked result. + if (crliteCoversCertificate && + crliteResult == Result::ERROR_REVOKED_CERTIFICATE && + mCRLiteMode == CRLiteMode::ConfirmRevocations && + (ocspResult != Success || ocspSoftFailure)) { + return Result::ERROR_REVOKED_CERTIFICATE; + } + + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: end of CheckRevocation")); + + return ocspResult; +} + +Result NSSCertDBTrustDomain::CheckRevocationByCRLite( + const CertID& certID, const Input& sctExtension, + /*out*/ bool& crliteCoversCertificate) { + crliteCoversCertificate = false; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite")); + nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes; + issuerSubjectPublicKeyInfoBytes.AppendElements( + certID.issuerSubjectPublicKeyInfo.UnsafeGetData(), + certID.issuerSubjectPublicKeyInfo.GetLength()); + nsTArray<uint8_t> serialNumberBytes; + serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(), + certID.serialNumber.GetLength()); + // The CRLite stash is essentially a subset of a collection of CRLs, so if + // it says a certificate is revoked, it is. + Result rv = + CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes, serialNumberBytes); + if (rv != Success) { + crliteCoversCertificate = (rv == Result::ERROR_REVOKED_CERTIFICATE); + return rv; + } + + nsTArray<uint8_t> issuerBytes; + issuerBytes.AppendElements(certID.issuer.UnsafeGetData(), + certID.issuer.GetLength()); + + nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps; + rv = BuildCRLiteTimestampArray(sctExtension, timestamps); + if (rv != Success) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("decoding SCT extension failed - CRLite will be not be " + "consulted")); + return Success; + } + return CheckCRLite(issuerBytes, issuerSubjectPublicKeyInfoBytes, + serialNumberBytes, timestamps, crliteCoversCertificate); +} + +Result NSSCertDBTrustDomain::CheckRevocationByOCSP( + const CertID& certID, Time time, Duration validityDuration, + const nsCString& aiaLocation, const bool crliteCoversCertificate, + const Result crliteResult, + /*optional*/ const Input* stapledOCSPResponse, + /*out*/ bool& softFailure) { + softFailure = false; + const uint16_t maxOCSPLifetimeInDays = 10; + // If we have a stapled OCSP response then the verification of that response + // determines the result unless the OCSP response is expired. We make an + // exception for expired responses because some servers, nginx in particular, + // are known to serve expired responses due to bugs. + // We keep track of the result of verifying the stapled response but don't + // immediately return failure if the response has expired. + Result stapledOCSPResponseResult = Success; + if (stapledOCSPResponse) { + bool expired; + uint32_t ageInHours; + stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse( + certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse, + ResponseWasStapled, expired, ageInHours); + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::StapledOCSP); + if (stapledOCSPResponseResult == Success) { + // stapled OCSP response present and good + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: stapled OCSP response: good")); + return Success; + } + if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE || + expired) { + // stapled OCSP response present but expired + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: expired stapled OCSP response")); + } else if (stapledOCSPResponseResult == + Result::ERROR_OCSP_TRY_SERVER_LATER || + stapledOCSPResponseResult == + Result::ERROR_OCSP_INVALID_SIGNING_CERT) { + // Stapled OCSP response present but invalid for a small number of reasons + // CAs/servers commonly get wrong. This will be treated similarly to an + // expired stapled response. + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: stapled OCSP response: " + "failure (allowed for compatibility)")); + } else { + // stapled OCSP response present but invalid for some reason + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: stapled OCSP response: failure")); + return stapledOCSPResponseResult; + } + } else { + // no stapled OCSP response + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: no stapled OCSP response")); + } + + Result cachedResponseResult = Success; + Time cachedResponseValidThrough(Time::uninitialized); + bool cachedResponsePresent = + mOCSPCache.Get(certID, mOriginAttributes, cachedResponseResult, + cachedResponseValidThrough); + if (cachedResponsePresent) { + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::CachedOCSP); + if (cachedResponseResult == Success && cachedResponseValidThrough >= time) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: cached OCSP response: good")); + return Success; + } + // If we have a cached revoked response, use it. + if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: cached OCSP response: revoked")); + return Result::ERROR_REVOKED_CERTIFICATE; + } + // The cached response may indicate an unknown certificate or it may be + // expired. Don't return with either of these statuses yet - we may be + // able to fetch a more recent one. + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: cached OCSP response: error %d", + static_cast<int>(cachedResponseResult))); + // When a good cached response has expired, it is more convenient + // to convert that to an error code and just deal with + // cachedResponseResult from here on out. + if (cachedResponseResult == Success && cachedResponseValidThrough < time) { + cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE; + } + // We may have a cached indication of server failure. Ignore it if + // it has expired. + if (cachedResponseResult != Success && + cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT && + cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE && + cachedResponseValidThrough < time) { + cachedResponseResult = Success; + cachedResponsePresent = false; + } + } else { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: no cached OCSP response")); + } + // At this point, if and only if cachedErrorResult is Success, there was no + // cached response. + MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) || + (cachedResponsePresent && cachedResponseResult != Success)); + + // TODO: We still need to handle the fallback for invalid stapled responses. + // But, if/when we disable OCSP fetching by default, it would be ambiguous + // whether security.OCSP.enable==0 means "I want the default" or "I really + // never want you to ever fetch OCSP." + // Additionally, this doesn't properly handle OCSP-must-staple when OCSP + // fetching is disabled. + Duration shortLifetime(mCertShortLifetimeInDays * Time::ONE_DAY_IN_SECONDS); + if (validityDuration < shortLifetime) { + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::ShortValidity); + } + if ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime)) { + // We're not going to be doing any fetching, so if there was a cached + // "unknown" response, say so. + if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { + return Result::ERROR_OCSP_UNKNOWN_CERT; + } + // If we're doing hard-fail, we want to know if we have a cached response + // that has expired. + if (mOCSPFetching == FetchOCSPForDVHardFail && + cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + return Result::ERROR_OCSP_OLD_RESPONSE; + } + + softFailure = true; + return Success; + } + + if (mOCSPFetching == LocalOnlyOCSPForEV) { + if (cachedResponseResult != Success) { + return cachedResponseResult; + } + return Result::ERROR_OCSP_UNKNOWN_CERT; + } + + if (aiaLocation.IsVoid()) { + if (mOCSPFetching == FetchOCSPForEV || + cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { + return Result::ERROR_OCSP_UNKNOWN_CERT; + } + if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + return Result::ERROR_OCSP_OLD_RESPONSE; + } + if (stapledOCSPResponseResult != Success) { + return stapledOCSPResponseResult; + } + + // Nothing to do if we don't have an OCSP responder URI for the cert; just + // assume it is good. Note that this is the confusing, but intended, + // interpretation of "strict" revocation checking in the face of a + // certificate that lacks an OCSP responder URI. There's no need to set + // softFailure here---we check for the presence of an AIA before attempting + // OCSP when CRLite is configured in confirm revocations mode. + return Success; + } + + if (cachedResponseResult == Success || + cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT || + cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) { + // Only send a request to, and process a response from, the server if we + // didn't have a cached indication of failure. Also, don't keep requesting + // responses from a failing server. + return SynchronousCheckRevocationWithServer( + certID, aiaLocation, time, maxOCSPLifetimeInDays, cachedResponseResult, + stapledOCSPResponseResult, crliteCoversCertificate, crliteResult, + softFailure); + } + + return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult, + cachedResponseResult, softFailure); +} + +Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer( + const CertID& certID, const nsCString& aiaLocation, Time time, + uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult, + const Result stapledOCSPResponseResult, const bool crliteCoversCertificate, + const Result crliteResult, /*out*/ bool& softFailure) { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + uint8_t ocspRequestBytes[OCSP_REQUEST_MAX_LENGTH]; + size_t ocspRequestLength; + Result rv = CreateEncodedOCSPRequest(*this, certID, ocspRequestBytes, + ocspRequestLength); + if (rv != Success) { + return rv; + } + + Vector<uint8_t> ocspResponse; + Input response; + mOCSPFetchStatus = OCSPFetchStatus::Fetched; + rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes, + ocspRequestLength, GetOCSPTimeout(), ocspResponse); + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::OCSP); + if (rv == Success && + response.Init(ocspResponse.begin(), ocspResponse.length()) != Success) { + rv = Result::ERROR_OCSP_MALFORMED_RESPONSE; // too big + } + + if (rv != Success) { + Time timeout(time); + if (timeout.AddSeconds(ServerFailureDelaySeconds) != Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow + } + + Result cacheRV = + mOCSPCache.Put(certID, mOriginAttributes, rv, time, timeout); + if (cacheRV != Success) { + return cacheRV; + } + + if (crliteCoversCertificate) { + if (crliteResult == Success) { + // CRLite says the certificate is OK, but OCSP fetching failed. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPFail); + } else { + // CRLite says the certificate is revoked, but OCSP fetching failed. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPFail); + } + } + + return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult, + rv, softFailure); + } + + // If the response from the network has expired but indicates a revoked + // or unknown certificate, PR_GetError() will return the appropriate error. + // We actually ignore expired here. + bool expired; + uint32_t ageInHours; + rv = VerifyAndMaybeCacheEncodedOCSPResponse( + certID, time, maxOCSPLifetimeInDays, response, ResponseIsFromNetwork, + expired, ageInHours); + + // If the CRLite filter covers the certificate, compare the CRLite result + // with the OCSP fetching result. OCSP may have succeeded, said the + // certificate is revoked, said the certificate doesn't exist, or it may have + // failed for a reason that results in a "soft fail" (i.e. there is no + // indication that the certificate is either definitely revoked or definitely + // not revoked, so for usability, revocation checking says the certificate is + // valid by default). + if (crliteCoversCertificate) { + if (rv == Success) { + if (crliteResult == Success) { + // CRLite and OCSP fetching agree the certificate is OK. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPOk); + } else { + // CRLite says the certificate is revoked, but OCSP says it is OK. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPOk); + + if (mCRLiteMode == CRLiteMode::ConfirmRevocations) { + Telemetry::Accumulate(Telemetry::OCSP_AGE_AT_CRLITE_OVERRIDE, + ageInHours); + } + } + } else if (rv == Result::ERROR_REVOKED_CERTIFICATE) { + if (crliteResult == Success) { + // CRLite says the certificate is OK, but OCSP says it is revoked. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPRev); + } else { + // CRLite and OCSP fetching agree the certificate is revoked. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPRev); + } + } else if (rv == Result::ERROR_OCSP_UNKNOWN_CERT) { + if (crliteResult == Success) { + // CRLite says the certificate is OK, but OCSP says it doesn't exist. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPUnk); + } else { + // CRLite says the certificate is revoked, but OCSP says it doesn't + // exist. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPUnk); + } + } else { + if (crliteResult == Success) { + // CRLite says the certificate is OK, but OCSP encountered a soft-fail + // error. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteOkOCSPSoft); + } else { + // CRLite says the certificate is revoked, but OCSP encountered a + // soft-fail error. + Telemetry::AccumulateCategorical( + Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPSoft); + } + } + } + + if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning after " + "VerifyEncodedOCSPResponse")); + return rv; + } + + if (rv == Result::ERROR_OCSP_UNKNOWN_CERT || + rv == Result::ERROR_REVOKED_CERTIFICATE) { + return rv; + } + + if (stapledOCSPResponseResult != Success) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid " + "stapled response after OCSP request verification failure")); + return stapledOCSPResponseResult; + } + + softFailure = true; + return Success; // Soft fail -> success :( +} + +Result NSSCertDBTrustDomain::HandleOCSPFailure( + const Result cachedResponseResult, const Result stapledOCSPResponseResult, + const Result error, /*out*/ bool& softFailure) { + if (mOCSPFetching != FetchOCSPForDVSoftFail) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning SECFailure after OCSP request " + "failure")); + return error; + } + + if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning SECFailure from cached response " + "after OCSP request failure")); + return cachedResponseResult; + } + + if (stapledOCSPResponseResult != Success) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning SECFailure from expired/invalid " + "stapled response after OCSP request failure")); + return stapledOCSPResponseResult; + } + + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: returning SECSuccess after OCSP request " + "failure")); + + softFailure = true; + return Success; // Soft fail -> success :( +} + +Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse( + const CertID& certID, Time time, uint16_t maxLifetimeInDays, + Input encodedResponse, EncodedResponseSource responseSource, + /*out*/ bool& expired, + /*out*/ uint32_t& ageInHours) { + Time thisUpdate(Time::uninitialized); + Time validThrough(Time::uninitialized); + + Result rv = VerifyEncodedOCSPResponse(*this, certID, time, maxLifetimeInDays, + encodedResponse, expired, &thisUpdate, + &validThrough); + // If a response was stapled and expired, we don't want to cache it. Return + // early to simplify the logic here. + if (responseSource == ResponseWasStapled && expired) { + MOZ_ASSERT(rv != Success); + return rv; + } + // validThrough is only trustworthy if the response successfully verifies + // or it indicates a revoked or unknown certificate. + // If this isn't the case, store an indication of failure (to prevent + // repeatedly requesting a response from a failing server). + if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE && + rv != Result::ERROR_OCSP_UNKNOWN_CERT) { + validThrough = time; + if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow + } + } + // The `thisUpdate` field holds the latest time at which the server knew the + // response was correct. The age of the response is the time that has elapsed + // since. We only use this for the telemetry defined in Bug 1794479. + uint64_t timeInSeconds; + uint64_t thisUpdateInSeconds; + uint64_t ageInSeconds; + SecondsSinceEpochFromTime(time, &timeInSeconds); + SecondsSinceEpochFromTime(thisUpdate, &thisUpdateInSeconds); + if (timeInSeconds >= thisUpdateInSeconds) { + ageInSeconds = timeInSeconds - thisUpdateInSeconds; + // ageInHours is 32 bits because of the telemetry api. + if (ageInSeconds > UINT32_MAX) { + // We could divide by 3600 before checking the UINT32_MAX bound, but if + // ageInSeconds is more than UINT32_MAX then there's been some sort of + // error. + ageInHours = UINT32_MAX; + } else { + // We start at 1 and divide with truncation to reserve ageInHours=0 for + // the case where `thisUpdate` is in the future. + ageInHours = 1 + ageInSeconds / (60 * 60); + } + } else { + ageInHours = 0; + } + if (responseSource == ResponseIsFromNetwork || rv == Success || + rv == Result::ERROR_REVOKED_CERTIFICATE || + rv == Result::ERROR_OCSP_UNKNOWN_CERT) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: caching OCSP response")); + Result putRV = + mOCSPCache.Put(certID, mOriginAttributes, rv, thisUpdate, validThrough); + if (putRV != Success) { + return putRV; + } + } + + return rv; +} + +SECStatus GetCertDistrustAfterValue(const SECItem* distrustItem, + PRTime& distrustTime) { + if (!distrustItem || !distrustItem->data || distrustItem->len != 13) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); + return SECFailure; + } + return DER_DecodeTimeChoice(&distrustTime, distrustItem); +} + +SECStatus GetCertNotBeforeValue(const CERTCertificate* cert, + PRTime& distrustTime) { + return DER_DecodeTimeChoice(&distrustTime, &cert->validity.notBefore); +} + +nsresult isDistrustedCertificateChain( + const nsTArray<nsTArray<uint8_t>>& certArray, + const SECTrustType certDBTrustType, bool& isDistrusted) { + if (certArray.Length() == 0) { + return NS_ERROR_FAILURE; + } + + // Set the default result to be distrusted. + isDistrusted = true; + + // There is no distrust to set if the certDBTrustType is not SSL or Email. + if (certDBTrustType != trustSSL && certDBTrustType != trustEmail) { + isDistrusted = false; + return NS_OK; + } + + SECStatus runnableRV = SECFailure; + + RefPtr<Runnable> isDistrustedChainTask = + NS_NewRunnableFunction("isDistrustedCertificateChain", [&]() { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + runnableRV = SECFailure; + return; + } + // Allocate objects and retreive the root and end-entity certificates. + CERTCertDBHandle* certDB(CERT_GetDefaultCertDB()); + const nsTArray<uint8_t>& certRootDER = certArray.LastElement(); + SECItem certRootDERItem = { + siBuffer, const_cast<unsigned char*>(certRootDER.Elements()), + AssertedCast<unsigned int>(certRootDER.Length())}; + UniqueCERTCertificate certRoot(CERT_NewTempCertificate( + certDB, &certRootDERItem, nullptr, false, true)); + if (!certRoot) { + runnableRV = SECFailure; + return; + } + const nsTArray<uint8_t>& certLeafDER = certArray.ElementAt(0); + SECItem certLeafDERItem = { + siBuffer, const_cast<unsigned char*>(certLeafDER.Elements()), + AssertedCast<unsigned int>(certLeafDER.Length())}; + UniqueCERTCertificate certLeaf(CERT_NewTempCertificate( + certDB, &certLeafDERItem, nullptr, false, true)); + if (!certLeaf) { + runnableRV = SECFailure; + return; + } + + // Set isDistrusted to false if there is no distrust for the root. + if (!certRoot->distrust) { + isDistrusted = false; + runnableRV = SECSuccess; + return; + } + + // Create a pointer to refer to the selected distrust struct. + SECItem* distrustPtr = nullptr; + if (certDBTrustType == trustSSL) { + distrustPtr = &certRoot->distrust->serverDistrustAfter; + } + if (certDBTrustType == trustEmail) { + distrustPtr = &certRoot->distrust->emailDistrustAfter; + } + + // Get validity for the current end-entity certificate + // and get the distrust field for the root certificate. + PRTime certRootDistrustAfter; + PRTime certLeafNotBefore; + + runnableRV = + GetCertDistrustAfterValue(distrustPtr, certRootDistrustAfter); + if (runnableRV != SECSuccess) { + return; + } + + runnableRV = GetCertNotBeforeValue(certLeaf.get(), certLeafNotBefore); + if (runnableRV != SECSuccess) { + return; + } + + // Compare the validity of the end-entity certificate with + // the distrust value of the root. + if (certLeafNotBefore <= certRootDistrustAfter) { + isDistrusted = false; + } + + runnableRV = SECSuccess; + }); + nsCOMPtr<nsIEventTarget> socketThread( + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID)); + if (!socketThread) { + return NS_ERROR_FAILURE; + } + nsresult rv = + SyncRunnable::DispatchToThread(socketThread, isDistrustedChainTask); + if (NS_FAILED(rv) || runnableRV != SECSuccess) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +Result NSSCertDBTrustDomain::IsChainValid(const DERArray& reversedDERArray, + Time time, + const CertPolicyId& requiredPolicy) { + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("NSSCertDBTrustDomain: IsChainValid")); + + size_t numCerts = reversedDERArray.GetLength(); + if (numCerts < 1) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsTArray<nsTArray<uint8_t>> certArray; + for (size_t i = numCerts; i > 0; --i) { + const Input* derInput = reversedDERArray.GetDER(i - 1); + certArray.EmplaceBack(derInput->UnsafeGetData(), derInput->GetLength()); + } + + const nsTArray<uint8_t>& rootBytes = certArray.LastElement(); + Input rootInput; + Result rv = rootInput.Init(rootBytes.Elements(), rootBytes.Length()); + if (rv != Success) { + return rv; + } + rv = IsCertBuiltInRoot(rootInput, mIsBuiltChainRootBuiltInRoot); + if (rv != Result::Success) { + return rv; + } + nsresult nsrv; + // If mHostname isn't set, we're not verifying in the context of a TLS + // handshake, so don't verify key pinning in those cases. + if (mHostname) { + nsTArray<Span<const uint8_t>> derCertSpanList; + for (const auto& certDER : certArray) { + derCertSpanList.EmplaceBack(certDER.Elements(), certDER.Length()); + } + + bool chainHasValidPins; + nsrv = PublicKeyPinningService::ChainHasValidPins( + derCertSpanList, mHostname, time, mIsBuiltChainRootBuiltInRoot, + chainHasValidPins, mPinningTelemetryInfo); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (!chainHasValidPins) { + return Result::ERROR_KEY_PINNING_FAILURE; + } + } + + // Check that the childs' certificate NotBefore date is anterior to + // the NotAfter value of the parent when the root is a builtin. + if (mIsBuiltChainRootBuiltInRoot) { + bool isDistrusted; + nsrv = + isDistrustedCertificateChain(certArray, mCertDBTrustType, isDistrusted); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (isDistrusted) { + return Result::ERROR_UNTRUSTED_ISSUER; + } + } + + // See bug 1434300. If the root is a Symantec root, see if we distrust this + // path. Since we already have the root available, we can check that cheaply + // here before proceeding with the rest of the algorithm. + + // This algorithm only applies if we are verifying in the context of a TLS + // handshake. To determine this, we check mHostname: If it isn't set, this is + // not TLS, so don't run the algorithm. + const nsTArray<uint8_t>& rootCertDER = certArray.LastElement(); + if (mHostname && CertDNIsInList(rootCertDER, RootSymantecDNs)) { + if (numCerts <= 1) { + // This chain is supposed to be complete, so this is an error. + return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED; + } + nsTArray<Input> intCerts; + + for (size_t i = 1; i < certArray.Length() - 1; ++i) { + const nsTArray<uint8_t>& certBytes = certArray.ElementAt(i); + Input certInput; + rv = certInput.Init(certBytes.Elements(), certBytes.Length()); + if (rv != Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + intCerts.EmplaceBack(certInput); + } + + bool isDistrusted = false; + nsrv = CheckForSymantecDistrust(intCerts, RootAppleAndGoogleSPKIs, + isDistrusted); + if (NS_FAILED(nsrv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (isDistrusted) { + mSawDistrustedCAByPolicyError = true; + return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED; + } + } + + mBuiltChain = std::move(certArray); + + return Success; +} + +Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm( + DigestAlgorithm aAlg, EndEntityOrCA /*endEntityOrCA*/, Time /*notBefore*/) { + switch (aAlg) { + case DigestAlgorithm::sha256: // fall through + case DigestAlgorithm::sha384: // fall through + case DigestAlgorithm::sha512: + return Success; + case DigestAlgorithm::sha1: + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + return Result::FATAL_ERROR_LIBRARY_FAILURE; +} + +Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) { + if (modulusSizeInBits < mMinRSABits) { + return Result::ERROR_INADEQUATE_KEY_SIZE; + } + return Success; +} + +Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, mPinArg); +} + +Result NSSCertDBTrustDomain::VerifyRSAPSSSignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, mPinArg); +} + +Result NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable( + EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) { + switch (curve) { + case NamedCurve::secp256r1: // fall through + case NamedCurve::secp384r1: // fall through + case NamedCurve::secp521r1: + return Success; + } + + return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; +} + +Result NSSCertDBTrustDomain::VerifyECDSASignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, mPinArg); +} + +Result NSSCertDBTrustDomain::CheckValidityIsAcceptable( + Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) { + if (endEntityOrCA != EndEntityOrCA::MustBeEndEntity) { + return Success; + } + if (keyPurpose == KeyPurposeId::id_kp_OCSPSigning) { + return Success; + } + + Duration DURATION_27_MONTHS_PLUS_SLOP((2 * 365 + 3 * 31 + 7) * + Time::ONE_DAY_IN_SECONDS); + Duration maxValidityDuration(UINT64_MAX); + Duration validityDuration(notBefore, notAfter); + + switch (mValidityCheckingMode) { + case ValidityCheckingMode::CheckingOff: + return Success; + case ValidityCheckingMode::CheckForEV: + // The EV Guidelines say the maximum is 27 months, but we use a slightly + // higher limit here to (hopefully) minimize compatibility breakage. + maxValidityDuration = DURATION_27_MONTHS_PLUS_SLOP; + break; + default: + MOZ_ASSERT_UNREACHABLE( + "We're not handling every ValidityCheckingMode type"); + } + + if (validityDuration > maxValidityDuration) { + return Result::ERROR_VALIDITY_TOO_LONG; + } + + return Success; +} + +Result NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth( + Time notBefore, + /*out*/ bool& matches) { + // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000 + static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000); + // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000 + static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400); + + switch (mNetscapeStepUpPolicy) { + case NetscapeStepUpPolicy::AlwaysMatch: + matches = true; + return Success; + case NetscapeStepUpPolicy::MatchBefore23August2016: + matches = notBefore < AUGUST_23_2016; + return Success; + case NetscapeStepUpPolicy::MatchBefore23August2015: + matches = notBefore < AUGUST_23_2015; + return Success; + case NetscapeStepUpPolicy::NeverMatch: + matches = false; + return Success; + default: + MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type"); + } + return Result::FATAL_ERROR_LIBRARY_FAILURE; +} + +void NSSCertDBTrustDomain::ResetAccumulatedState() { + mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED; + mSCTListFromOCSPStapling = nullptr; + mSCTListFromCertificate = nullptr; + mSawDistrustedCAByPolicyError = false; + mIsBuiltChainRootBuiltInRoot = false; +} + +static Input SECItemToInput(const UniqueSECItem& item) { + Input result; + if (item) { + MOZ_ASSERT(item->type == siBuffer); + Result rv = result.Init(item->data, item->len); + // As used here, |item| originally comes from an Input, + // so there should be no issues converting it back. + MOZ_ASSERT(rv == Success); + Unused << rv; // suppresses warnings in release builds + } + return result; +} + +Input NSSCertDBTrustDomain::GetSCTListFromCertificate() const { + return SECItemToInput(mSCTListFromCertificate); +} + +Input NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const { + return SECItemToInput(mSCTListFromOCSPStapling); +} + +bool NSSCertDBTrustDomain::GetIsBuiltChainRootBuiltInRoot() const { + return mIsBuiltChainRootBuiltInRoot; +} + +bool NSSCertDBTrustDomain::GetIsErrorDueToDistrustedCAPolicy() const { + return mSawDistrustedCAByPolicyError; +} + +void NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension, + Input extensionData) { + UniqueSECItem* out = nullptr; + switch (extension) { + case AuxiliaryExtension::EmbeddedSCTList: + out = &mSCTListFromCertificate; + break; + case AuxiliaryExtension::SCTListFromOCSPResponse: + out = &mSCTListFromOCSPStapling; + break; + default: + MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension"); + } + if (out) { + SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData); + out->reset(SECITEM_DupItem(&extensionDataItem)); + } +} + +SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, + PKCS11DBConfig pkcs11DbConfig) { + MOZ_ASSERT(NS_IsMainThread()); + + // The NSS_INIT_NOROOTINIT flag turns off the loading of the root certs + // module by NSS_Initialize because we will load it in LoadLoadableRoots + // later. It also allows us to work around a bug in the system NSS in + // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as + // "/usr/lib/nss/libnssckbi.so". + uint32_t flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE; + if (nssDbConfig == NSSDBConfig::ReadOnly) { + flags |= NSS_INIT_READONLY; + } + if (pkcs11DbConfig == PKCS11DBConfig::DoNotLoadModules) { + flags |= NSS_INIT_NOMODDB; + } + nsAutoCString dbTypeAndDirectory("sql:"); + dbTypeAndDirectory.Append(dir); + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("InitializeNSS(%s, %d, %d)", dbTypeAndDirectory.get(), + (int)nssDbConfig, (int)pkcs11DbConfig)); + SECStatus srv = + NSS_Initialize(dbTypeAndDirectory.get(), "", "", SECMOD_DB, flags); + if (srv != SECSuccess) { + return srv; + } + + if (nssDbConfig == NSSDBConfig::ReadWrite) { + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + return SECFailure; + } + // If the key DB doesn't have a password set, PK11_NeedUserInit will return + // true. For the SQL DB, we need to set a password or we won't be able to + // import any certificates or change trust settings. + if (PK11_NeedUserInit(slot.get())) { + srv = PK11_InitPin(slot.get(), nullptr, nullptr); + MOZ_ASSERT(srv == SECSuccess); + Unused << srv; + } + } + + return SECSuccess; +} + +void DisableMD5() { + NSS_SetAlgorithmPolicy( + SEC_OID_MD5, 0, + NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); + NSS_SetAlgorithmPolicy( + SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, 0, + NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); + NSS_SetAlgorithmPolicy( + SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC, 0, + NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE); +} + +// Load a given PKCS#11 module located in the given directory. It will be named +// the given module name. Optionally pass some string parameters to it via +// 'params'. This argument will be provided to C_Initialize when called on the +// module. +// |libraryName| and |dir| are encoded in UTF-8. +bool LoadUserModuleAt(const char* moduleName, const char* libraryName, + const nsCString& dir, /* optional */ const char* params) { + // If a module exists with the same name, make a best effort attempt to delete + // it. Note that it isn't possible to delete the internal module, so checking + // the return value would be detrimental in that case. + int unusedModType; + Unused << SECMOD_DeleteModule(moduleName, &unusedModType); + + nsAutoCString fullLibraryPath; + if (!dir.IsEmpty()) { + fullLibraryPath.Assign(dir); + fullLibraryPath.AppendLiteral(FILE_PATH_SEPARATOR); + } + fullLibraryPath.Append(MOZ_DLL_PREFIX); + fullLibraryPath.Append(libraryName); + fullLibraryPath.Append(MOZ_DLL_SUFFIX); + // Escape the \ and " characters. + fullLibraryPath.ReplaceSubstring("\\", "\\\\"); + fullLibraryPath.ReplaceSubstring("\"", "\\\""); + + nsAutoCString pkcs11ModuleSpec("name=\""); + pkcs11ModuleSpec.Append(moduleName); + pkcs11ModuleSpec.AppendLiteral("\" library=\""); + pkcs11ModuleSpec.Append(fullLibraryPath); + pkcs11ModuleSpec.AppendLiteral("\""); + if (params) { + pkcs11ModuleSpec.AppendLiteral("\" parameters=\""); + pkcs11ModuleSpec.Append(params); + pkcs11ModuleSpec.AppendLiteral("\""); + } + + UniqueSECMODModule userModule(SECMOD_LoadUserModule( + const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false)); + if (!userModule) { + return false; + } + + if (!userModule->loaded) { + return false; + } + + return true; +} + +const char* kIPCClientCertsModuleName = "IPC Client Cert Module"; + +bool LoadIPCClientCertsModule(const nsCString& dir) { + // The IPC client certs module needs to be able to call back into gecko to be + // able to communicate with the parent process over IPC. This is achieved by + // serializing the addresses of the relevant functions and passing them as an + // extra string parameter that will be available when C_Initialize is called + // on IPC client certs. + nsPrintfCString addrs("%p,%p", DoFindObjects, DoSign); + if (!LoadUserModuleAt(kIPCClientCertsModuleName, "ipcclientcerts", dir, + addrs.get())) { + return false; + } + RunOnShutdown( + []() { + UniqueSECMODModule ipcClientCertsModule( + SECMOD_FindModule(kIPCClientCertsModuleName)); + if (ipcClientCertsModule) { + SECMOD_UnloadUserModule(ipcClientCertsModule.get()); + } + }, + ShutdownPhase::XPCOMWillShutdown); + return true; +} + +const char* kOSClientCertsModuleName = "OS Client Cert Module"; + +bool LoadOSClientCertsModule(const nsCString& dir) { +#ifdef MOZ_WIDGET_COCOA + // osclientcerts requires macOS 10.14 or later + if (!nsCocoaFeatures::OnMojaveOrLater()) { + return false; + } +#endif + nsLiteralCString params = + StaticPrefs::security_osclientcerts_assume_rsa_pss_support() + ? "RSA-PSS"_ns + : ""_ns; + return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir, + params.get()); +} + +bool LoadLoadableRoots(const nsCString& dir) { + // Some NSS command-line utilities will load a roots module under the name + // "Root Certs" if there happens to be a `MOZ_DLL_PREFIX "nssckbi" + // MOZ_DLL_SUFFIX` file in the directory being operated on. In some cases this + // can cause us to fail to load our roots module. In these cases, deleting the + // "Root Certs" module allows us to load the correct one. See bug 1406396. + int unusedModType; + Unused << SECMOD_DeleteModule("Root Certs", &unusedModType); + return LoadUserModuleAt(kRootModuleName, "nssckbi", dir, nullptr); +} + +nsresult DefaultServerNicknameForCert(const CERTCertificate* cert, + /*out*/ nsCString& nickname) { + MOZ_ASSERT(cert); + NS_ENSURE_ARG_POINTER(cert); + + UniquePORTString baseName(CERT_GetCommonName(&cert->subject)); + if (!baseName) { + baseName = UniquePORTString(CERT_GetOrgUnitName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetOrgName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetLocalityName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetStateName(&cert->subject)); + } + if (!baseName) { + baseName = UniquePORTString(CERT_GetCountryName(&cert->subject)); + } + if (!baseName) { + return NS_ERROR_FAILURE; + } + + // This function is only used in contexts where a failure to find a suitable + // nickname does not block the overall task from succeeding. + // As such, we use an arbitrary limit to prevent this nickname searching + // process from taking forever. + static const uint32_t ARBITRARY_LIMIT = 500; + for (uint32_t count = 1; count < ARBITRARY_LIMIT; count++) { + nickname = baseName.get(); + if (count != 1) { + nickname.AppendPrintf(" #%u", count); + } + if (nickname.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + bool conflict = SEC_CertNicknameConflict(nickname.get(), &cert->derSubject, + cert->dbhandle); + if (!conflict) { + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} + +Result BuildRevocationCheckArrays(Input certDER, EndEntityOrCA endEntityOrCA, + /*out*/ nsTArray<uint8_t>& issuerBytes, + /*out*/ nsTArray<uint8_t>& serialBytes, + /*out*/ nsTArray<uint8_t>& subjectBytes, + /*out*/ nsTArray<uint8_t>& pubKeyBytes) { + BackCert cert(certDER, endEntityOrCA, nullptr); + Result rv = cert.Init(); + if (rv != Success) { + return rv; + } + issuerBytes.Clear(); + Input issuer(cert.GetIssuer()); + issuerBytes.AppendElements(issuer.UnsafeGetData(), issuer.GetLength()); + serialBytes.Clear(); + Input serial(cert.GetSerialNumber()); + serialBytes.AppendElements(serial.UnsafeGetData(), serial.GetLength()); + subjectBytes.Clear(); + Input subject(cert.GetSubject()); + subjectBytes.AppendElements(subject.UnsafeGetData(), subject.GetLength()); + pubKeyBytes.Clear(); + Input pubKey(cert.GetSubjectPublicKeyInfo()); + pubKeyBytes.AppendElements(pubKey.UnsafeGetData(), pubKey.GetLength()); + + return Success; +} + +bool CertIsInCertStorage(const nsTArray<uint8_t>& certDER, + nsICertStorage* certStorage) { + MOZ_ASSERT(certStorage); + if (!certStorage) { + return false; + } + Input certInput; + Result rv = certInput.Init(certDER.Elements(), certDER.Length()); + if (rv != Success) { + return false; + } + BackCert cert(certInput, EndEntityOrCA::MustBeCA, nullptr); + rv = cert.Init(); + if (rv != Success) { + return false; + } + nsTArray<uint8_t> subject; + subject.AppendElements(cert.GetSubject().UnsafeGetData(), + cert.GetSubject().GetLength()); + nsTArray<nsTArray<uint8_t>> certStorageCerts; + if (NS_FAILED(certStorage->FindCertsBySubject(subject, certStorageCerts))) { + return false; + } + for (const auto& certStorageCert : certStorageCerts) { + if (certStorageCert.Length() != certDER.Length()) { + continue; + } + if (memcmp(certStorageCert.Elements(), certDER.Elements(), + certStorageCert.Length()) == 0) { + return true; + } + } + return false; +} + +/** + * Given a list of certificates representing a verified certificate path from an + * end-entity certificate to a trust anchor, imports the intermediate + * certificates into the permanent certificate database. This is an attempt to + * cope with misconfigured servers that don't include the appropriate + * intermediate certificates in the TLS handshake. + * + * @param certList the verified certificate list + */ +void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList) { + if (certList.IsEmpty()) { + return; + } + nsTArray<nsTArray<uint8_t>> intermediates; + // Skip the end-entity; we only want to store intermediates. Similarly, + // there's no need to save the trust anchor - it's either already a permanent + // certificate or it's the Microsoft Family Safety root or an enterprise root + // temporarily imported via the child mode or enterprise root features. We + // don't want to import these because they're intended to be temporary (and + // because importing them happens to reset their trust settings, which breaks + // these features). + for (size_t index = 1; index < certList.Length() - 1; index++) { + intermediates.AppendElement(certList.ElementAt(index).Clone()); + } + nsCOMPtr<nsIRunnable> importCertsRunnable(NS_NewRunnableFunction( + "IdleSaveIntermediateCerts", + [intermediates = std::move(intermediates)]() -> void { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + return; + } + UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + return; + } + size_t numCertsImported = 0; + nsCOMPtr<nsICertStorage> certStorage( + do_GetService(NS_CERT_STORAGE_CID)); + for (const auto& certDER : intermediates) { + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + return; + } + if (CertIsInCertStorage(certDER, certStorage)) { + continue; + } + SECItem certDERItem = {siBuffer, + const_cast<unsigned char*>(certDER.Elements()), + AssertedCast<unsigned int>(certDER.Length())}; + UniqueCERTCertificate cert(CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &certDERItem, nullptr, false, true)); + if (!cert) { + continue; + } + if (cert->slot) { + // This cert was found on a token; no need to remember it in the + // permanent database. + continue; + } + PRBool isperm; + if (CERT_GetCertIsPerm(cert.get(), &isperm) != SECSuccess) { + continue; + } + if (isperm) { + // We don't need to remember certs already stored in perm db. + continue; + } + // This is a best-effort attempt at avoiding unknown issuer errors + // in the future, so ignore failures here. + nsAutoCString nickname; + if (NS_FAILED(DefaultServerNicknameForCert(cert.get(), nickname))) { + continue; + } + Unused << PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE, + nickname.get(), false); + numCertsImported++; + } + + nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction( + "IdleSaveIntermediateCertsDone", [numCertsImported]() -> void { + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + NS_ConvertUTF8toUTF16 numCertsImportedString( + nsPrintfCString("%zu", numCertsImported)); + observerService->NotifyObservers( + nullptr, "psm:intermediate-certs-cached", + numCertsImportedString.get()); + } + })); + Unused << NS_DispatchToMainThread(runnable.forget()); + })); + Unused << NS_DispatchToCurrentThreadQueue(importCertsRunnable.forget(), + EventQueuePriority::Idle); +} + +} // namespace psm +} // namespace mozilla diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h new file mode 100644 index 0000000000..438e9bec10 --- /dev/null +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -0,0 +1,329 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NSSCertDBTrustDomain_h +#define NSSCertDBTrustDomain_h + +#include "CertVerifier.h" +#include "CRLiteTimestamp.h" +#include "ScopedNSSTypes.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/TimeStamp.h" +#include "mozpkix/pkixtypes.h" +#include "nsICertStorage.h" +#include "nsString.h" +#include "secmodt.h" + +namespace mozilla { +namespace psm { + +enum class ValidityCheckingMode { + CheckingOff = 0, + CheckForEV = 1, +}; + +enum class NSSDBConfig { + ReadWrite = 0, + ReadOnly = 1, +}; + +enum class PKCS11DBConfig { + DoNotLoadModules = 0, + LoadModules = 1, +}; + +// Policy options for matching id-Netscape-stepUp with id-kp-serverAuth (for CA +// certificates only): +// * Always match: the step-up OID is considered equivalent to serverAuth +// * Match before 23 August 2016: the OID is considered equivalent if the +// certificate's notBefore is before 23 August 2016 +// * Match before 23 August 2015: similarly, but for 23 August 2015 +// * Never match: the OID is never considered equivalent to serverAuth +enum class NetscapeStepUpPolicy : uint32_t { + AlwaysMatch = 0, + MatchBefore23August2016 = 1, + MatchBefore23August2015 = 2, + NeverMatch = 3, +}; + +enum class OCSPFetchStatus : uint16_t { + NotFetched = 0, + Fetched = 1, +}; + +SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, + PKCS11DBConfig pkcs11DbConfig); + +void DisableMD5(); + +/** + * Loads root certificates from a module. + * + * @param dir + * The path to the directory containing the NSS builtin roots module. + * Usually the same as the path to the other NSS shared libraries. + * If empty, the (library) path will be searched. + * @return true if the roots were successfully loaded, false otherwise. + */ +bool LoadLoadableRoots(const nsCString& dir); + +/** + * Loads the OS client certs module. + * + * @param dir + * The path to the directory containing the module. This should be the + * same as where all of the other gecko libraries live. + * @return true if the module was successfully loaded, false otherwise. + */ +bool LoadOSClientCertsModule(const nsCString& dir); + +extern const char* kOSClientCertsModuleName; + +/** + * Loads the IPC client certs module. + * + * @param dir + * The path to the directory containing the module. This should be the + * same as where all of the other gecko libraries live. + * @return true if the module was successfully loaded, false otherwise. + */ +bool LoadIPCClientCertsModule(const nsCString& dir); + +extern const char* kIPCClientCertsModuleName; + +/** + * Unloads the loadable roots module and os client certs module, if loaded. + */ +void UnloadUserModules(); + +nsresult DefaultServerNicknameForCert(const CERTCertificate* cert, + /*out*/ nsCString& nickname); + +/** + * Build nsTArray<uint8_t>s out of the issuer, serial, subject and public key + * data from the supplied certificate for use in revocation checks. + * + * @param certDER + * The Input that references the encoded bytes of the certificate. + * @param endEntityOrCA + * Whether the certificate is an end-entity or CA. + * @param out encIssuer + * The array to populate with issuer data. + * @param out encSerial + * The array to populate with serial number data. + * @param out encSubject + * The array to populate with subject data. + * @param out encPubKey + * The array to populate with public key data. + * @return + * Result::Success, unless there's a problem decoding the certificate. + */ +pkix::Result BuildRevocationCheckArrays(pkix::Input certDER, + pkix::EndEntityOrCA endEntityOrCA, + /*out*/ nsTArray<uint8_t>& issuerBytes, + /*out*/ nsTArray<uint8_t>& serialBytes, + /*out*/ nsTArray<uint8_t>& subjectBytes, + /*out*/ nsTArray<uint8_t>& pubKeyBytes); + +void SaveIntermediateCerts(const nsTArray<nsTArray<uint8_t>>& certList); + +class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { + public: + typedef mozilla::pkix::Result Result; + + enum OCSPFetching { + NeverFetchOCSP = 0, + FetchOCSPForDVSoftFail = 1, + FetchOCSPForDVHardFail = 2, + FetchOCSPForEV = 3, + LocalOnlyOCSPForEV = 4, + }; + + NSSCertDBTrustDomain( + SECTrustType certDBTrustType, OCSPFetching ocspFetching, + OCSPCache& ocspCache, void* pinArg, mozilla::TimeDuration ocspTimeoutSoft, + mozilla::TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays, + unsigned int minRSABits, ValidityCheckingMode validityCheckingMode, + NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode, + const OriginAttributes& originAttributes, + const Vector<mozilla::pkix::Input>& thirdPartyRootInputs, + const Vector<mozilla::pkix::Input>& thirdPartyIntermediateInputs, + const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates, + /*out*/ nsTArray<nsTArray<uint8_t>>& builtChain, + /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, + /*optional*/ const char* hostname = nullptr); + + virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName, + IssuerChecker& checker, + mozilla::pkix::Time time) override; + + virtual Result GetCertTrust( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertPolicyId& policy, + mozilla::pkix::Input candidateCertDER, + /*out*/ mozilla::pkix::TrustLevel& trustLevel) override; + + virtual Result CheckSignatureDigestAlgorithm( + mozilla::pkix::DigestAlgorithm digestAlg, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::Time notBefore) override; + + virtual Result CheckRSAPublicKeyModulusSizeInBits( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + unsigned int modulusSizeInBits) override; + + virtual Result VerifyRSAPKCS1SignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + + virtual Result VerifyRSAPSSSignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + + virtual Result CheckECDSACurveIsAcceptable( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::NamedCurve curve) override; + + virtual Result VerifyECDSASignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + + virtual Result DigestBuf(mozilla::pkix::Input item, + mozilla::pkix::DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) override; + + virtual Result CheckValidityIsAcceptable( + mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::KeyPurposeId keyPurpose) override; + + virtual Result NetscapeStepUpMatchesServerAuth( + mozilla::pkix::Time notBefore, + /*out*/ bool& matches) override; + + virtual Result CheckRevocation( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertID& certID, mozilla::pkix::Time time, + mozilla::pkix::Duration validityDuration, + /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse, + /*optional*/ const mozilla::pkix::Input* aiaExtension, + /*optional*/ const mozilla::pkix::Input* sctExtension) override; + + virtual Result IsChainValid( + const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time, + const mozilla::pkix::CertPolicyId& requiredPolicy) override; + + virtual void NoteAuxiliaryExtension( + mozilla::pkix::AuxiliaryExtension extension, + mozilla::pkix::Input extensionData) override; + + // Resets the OCSP stapling status and SCT lists accumulated during + // the chain building. + void ResetAccumulatedState(); + + CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const { + return mOCSPStaplingStatus; + } + + // SCT lists (see Certificate Transparency) extracted during + // certificate verification. Note that the returned Inputs are invalidated + // the next time a chain is built and by ResetAccumulatedState method + // (and when the TrustDomain object is destroyed). + + mozilla::pkix::Input GetSCTListFromCertificate() const; + mozilla::pkix::Input GetSCTListFromOCSPStapling() const; + + bool GetIsBuiltChainRootBuiltInRoot() const; + + bool GetIsErrorDueToDistrustedCAPolicy() const; + + OCSPFetchStatus GetOCSPFetchStatus() { return mOCSPFetchStatus; } + + private: + Result CheckCRLiteStash( + const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes, + const nsTArray<uint8_t>& serialNumberBytes); + Result CheckCRLite( + const nsTArray<uint8_t>& issuerBytes, + const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes, + const nsTArray<uint8_t>& serialNumberBytes, + const nsTArray<RefPtr<nsICRLiteTimestamp>>& crliteTimestamps, + bool& filterCoversCertificate); + + enum EncodedResponseSource { + ResponseIsFromNetwork = 1, + ResponseWasStapled = 2 + }; + Result VerifyAndMaybeCacheEncodedOCSPResponse( + const mozilla::pkix::CertID& certID, mozilla::pkix::Time time, + uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse, + EncodedResponseSource responseSource, /*out*/ bool& expired, + /*out*/ uint32_t& ageInHours); + TimeDuration GetOCSPTimeout() const; + + Result CheckRevocationByCRLite(const mozilla::pkix::CertID& certID, + const mozilla::pkix::Input& sctExtension, + /*out*/ bool& crliteCoversCertificate); + + Result CheckRevocationByOCSP( + const mozilla::pkix::CertID& certID, mozilla::pkix::Time time, + mozilla::pkix::Duration validityDuration, const nsCString& aiaLocation, + const bool crliteCoversCertificate, const Result crliteResult, + /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse, + /*out*/ bool& softFailure); + + Result SynchronousCheckRevocationWithServer( + const mozilla::pkix::CertID& certID, const nsCString& aiaLocation, + mozilla::pkix::Time time, uint16_t maxOCSPLifetimeInDays, + const Result cachedResponseResult, const Result stapledOCSPResponseResult, + const bool crliteFilterCoversCertificate, const Result crliteResult, + /*out*/ bool& softFailure); + Result HandleOCSPFailure(const Result cachedResponseResult, + const Result stapledOCSPResponseResult, + const Result error, + /*out*/ bool& softFailure); + + const SECTrustType mCertDBTrustType; + const OCSPFetching mOCSPFetching; + OCSPCache& mOCSPCache; // non-owning! + void* mPinArg; // non-owning! + const mozilla::TimeDuration mOCSPTimeoutSoft; + const mozilla::TimeDuration mOCSPTimeoutHard; + const uint32_t mCertShortLifetimeInDays; + const unsigned int mMinRSABits; + ValidityCheckingMode mValidityCheckingMode; + NetscapeStepUpPolicy mNetscapeStepUpPolicy; + CRLiteMode mCRLiteMode; + bool mSawDistrustedCAByPolicyError; + const OriginAttributes& mOriginAttributes; + const Vector<mozilla::pkix::Input>& mThirdPartyRootInputs; // non-owning + const Vector<mozilla::pkix::Input>& + mThirdPartyIntermediateInputs; // non-owning + const Maybe<nsTArray<nsTArray<uint8_t>>>& mExtraCertificates; // non-owning + nsTArray<nsTArray<uint8_t>>& mBuiltChain; // non-owning + bool mIsBuiltChainRootBuiltInRoot; + PinningTelemetryInfo* mPinningTelemetryInfo; + const char* mHostname; // non-owning - only used for pinning checks + nsCOMPtr<nsICertStorage> mCertStorage; + CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus; + // Certificate Transparency data extracted during certificate verification + UniqueSECItem mSCTListFromCertificate; + UniqueSECItem mSCTListFromOCSPStapling; + + // The built-in roots module, if available. + UniqueSECMODModule mBuiltInRootsModule; + + OCSPFetchStatus mOCSPFetchStatus; +}; + +} // namespace psm +} // namespace mozilla + +#endif // NSSCertDBTrustDomain_h diff --git a/security/certverifier/OCSPCache.cpp b/security/certverifier/OCSPCache.cpp new file mode 100644 index 0000000000..557b501ad5 --- /dev/null +++ b/security/certverifier/OCSPCache.cpp @@ -0,0 +1,352 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OCSPCache.h" + +#include <limits> + +#include "NSSCertDBTrustDomain.h" +#include "pk11pub.h" +#include "mozilla/Logging.h" +#include "mozilla/StaticPrefs_privacy.h" +#include "mozpkix/pkixnss.h" +#include "ScopedNSSTypes.h" +#include "secerr.h" + +extern mozilla::LazyLogModule gCertVerifierLog; + +using namespace mozilla::pkix; + +namespace mozilla { +namespace psm { + +typedef mozilla::pkix::Result Result; + +static SECStatus DigestLength(UniquePK11Context& context, uint32_t length) { + // Restrict length to 2 bytes because it should be big enough for all + // inputs this code will actually see and that it is well-defined and + // type-size-independent. + if (length >= 65536) { + return SECFailure; + } + unsigned char array[2]; + array[0] = length & 255; + array[1] = (length >> 8) & 255; + + return PK11_DigestOp(context.get(), array, MOZ_ARRAY_LENGTH(array)); +} + +// Let derIssuer be the DER encoding of the issuer of certID. +// Let derPublicKey be the DER encoding of the public key of certID. +// Let serialNumber be the bytes of the serial number of certID. +// Let serialNumberLen be the number of bytes of serialNumber. +// Let firstPartyDomain be the first party domain of originAttributes. +// It is only non-empty when "privacy.firstParty.isolate" is enabled, in order +// to isolate OCSP cache by first party. +// Let firstPartyDomainLen be the number of bytes of firstPartyDomain. +// Let partitionKey be the partition key of originAttributes. +// Let partitionKeyLen be the number of bytes of partitionKey. +// The value calculated is SHA384(derIssuer || derPublicKey || serialNumberLen +// || serialNumber || firstPartyDomainLen || firstPartyDomain || partitionKeyLen +// || partitionKey). +// Because the DER encodings include the length of the data encoded, and we also +// include the length of serialNumber and originAttributes, there do not exist +// A(derIssuerA, derPublicKeyA, serialNumberLenA, serialNumberA, +// originAttributesLenA, originAttributesA) and B(derIssuerB, derPublicKeyB, +// serialNumberLenB, serialNumberB, originAttributesLenB, originAttributesB) +// such that the concatenation of each tuple results in the same string of +// bytes but where each part in A is not equal to its counterpart in B. This is +// important because as a result it is computationally infeasible to find +// collisions that would subvert this cache (given that SHA384 is a +// cryptographically-secure hash function). +static SECStatus CertIDHash(SHA384Buffer& buf, const CertID& certID, + const OriginAttributes& originAttributes) { + UniquePK11Context context(PK11_CreateDigestContext(SEC_OID_SHA384)); + if (!context) { + return SECFailure; + } + SECStatus rv = PK11_DigestBegin(context.get()); + if (rv != SECSuccess) { + return rv; + } + SECItem certIDIssuer = UnsafeMapInputToSECItem(certID.issuer); + rv = PK11_DigestOp(context.get(), certIDIssuer.data, certIDIssuer.len); + if (rv != SECSuccess) { + return rv; + } + SECItem certIDIssuerSubjectPublicKeyInfo = + UnsafeMapInputToSECItem(certID.issuerSubjectPublicKeyInfo); + rv = PK11_DigestOp(context.get(), certIDIssuerSubjectPublicKeyInfo.data, + certIDIssuerSubjectPublicKeyInfo.len); + if (rv != SECSuccess) { + return rv; + } + SECItem certIDSerialNumber = UnsafeMapInputToSECItem(certID.serialNumber); + rv = DigestLength(context, certIDSerialNumber.len); + if (rv != SECSuccess) { + return rv; + } + rv = PK11_DigestOp(context.get(), certIDSerialNumber.data, + certIDSerialNumber.len); + if (rv != SECSuccess) { + return rv; + } + + auto populateOriginAttributesKey = [&context](const nsString& aKey) { + NS_ConvertUTF16toUTF8 key(aKey); + + if (key.IsEmpty()) { + return SECSuccess; + } + + SECStatus rv = DigestLength(context, key.Length()); + if (rv != SECSuccess) { + return rv; + } + + return PK11_DigestOp(context.get(), + BitwiseCast<const unsigned char*>(key.get()), + key.Length()); + }; + + // OCSP should be isolated by firstPartyDomain and partitionKey, but not + // by containers. + rv = populateOriginAttributesKey(originAttributes.mFirstPartyDomain); + if (rv != SECSuccess) { + return rv; + } + + bool isolateByPartitionKey = + originAttributes.mPrivateBrowsingId > 0 + ? StaticPrefs::privacy_partition_network_state_ocsp_cache_pbmode() + : StaticPrefs::privacy_partition_network_state_ocsp_cache(); + if (isolateByPartitionKey) { + rv = populateOriginAttributesKey(originAttributes.mPartitionKey); + if (rv != SECSuccess) { + return rv; + } + } + uint32_t outLen = 0; + rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH); + if (outLen != SHA384_LENGTH) { + return SECFailure; + } + return rv; +} + +Result OCSPCache::Entry::Init(const CertID& aCertID, + const OriginAttributes& aOriginAttributes) { + SECStatus srv = CertIDHash(mIDHash, aCertID, aOriginAttributes); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + return Success; +} + +OCSPCache::OCSPCache() : mMutex("OCSPCache-mutex") {} + +OCSPCache::~OCSPCache() { Clear(); } + +// Returns false with index in an undefined state if no matching entry was +// found. +bool OCSPCache::FindInternal(const CertID& aCertID, + const OriginAttributes& aOriginAttributes, + /*out*/ size_t& index, + const MutexAutoLock& /* aProofOfLock */) { + mMutex.AssertCurrentThreadOwns(); + if (mEntries.length() == 0) { + return false; + } + + SHA384Buffer idHash; + SECStatus rv = CertIDHash(idHash, aCertID, aOriginAttributes); + if (rv != SECSuccess) { + return false; + } + + // mEntries is sorted with the most-recently-used entry at the end. + // Thus, searching from the end will often be fastest. + index = mEntries.length(); + while (index > 0) { + --index; + if (memcmp(mEntries[index]->mIDHash, idHash, SHA384_LENGTH) == 0) { + return true; + } + } + return false; +} + +static inline void LogWithCertID(const char* aMessage, const CertID& aCertID, + const OriginAttributes& aOriginAttributes) { + nsAutoString info = u"firstPartyDomain: "_ns + + aOriginAttributes.mFirstPartyDomain + + u", partitionKey: "_ns + aOriginAttributes.mPartitionKey; + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + (aMessage, &aCertID, NS_ConvertUTF16toUTF8(info).get())); +} + +void OCSPCache::MakeMostRecentlyUsed(size_t aIndex, + const MutexAutoLock& /* aProofOfLock */) { + mMutex.AssertCurrentThreadOwns(); + Entry* entry = mEntries[aIndex]; + // Since mEntries is sorted with the most-recently-used entry at the end, + // aIndex is likely to be near the end, so this is likely to be fast. + mEntries.erase(mEntries.begin() + aIndex); + // erase() does not shrink or realloc memory, so the append below should + // always succeed. + MOZ_RELEASE_ASSERT(mEntries.append(entry)); +} + +bool OCSPCache::Get(const CertID& aCertID, + const OriginAttributes& aOriginAttributes, Result& aResult, + Time& aValidThrough) { + MutexAutoLock lock(mMutex); + + size_t index; + if (!FindInternal(aCertID, aOriginAttributes, index, lock)) { + LogWithCertID("OCSPCache::Get(%p,\"%s\") not in cache", aCertID, + aOriginAttributes); + return false; + } + LogWithCertID("OCSPCache::Get(%p,\"%s\") in cache", aCertID, + aOriginAttributes); + aResult = mEntries[index]->mResult; + aValidThrough = mEntries[index]->mValidThrough; + MakeMostRecentlyUsed(index, lock); + return true; +} + +Result OCSPCache::Put(const CertID& aCertID, + const OriginAttributes& aOriginAttributes, Result aResult, + Time aThisUpdate, Time aValidThrough) { + MutexAutoLock lock(mMutex); + + size_t index; + if (FindInternal(aCertID, aOriginAttributes, index, lock)) { + // Never replace an entry indicating a revoked certificate. + if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) { + LogWithCertID( + "OCSPCache::Put(%p, \"%s\") already in cache as revoked - " + "not replacing", + aCertID, aOriginAttributes); + MakeMostRecentlyUsed(index, lock); + return Success; + } + + // Never replace a newer entry with an older one unless the older entry + // indicates a revoked certificate, which we want to remember. + if (mEntries[index]->mThisUpdate > aThisUpdate && + aResult != Result::ERROR_REVOKED_CERTIFICATE) { + LogWithCertID( + "OCSPCache::Put(%p, \"%s\") already in cache with more " + "recent validity - not replacing", + aCertID, aOriginAttributes); + MakeMostRecentlyUsed(index, lock); + return Success; + } + + // Only known good responses or responses indicating an unknown + // or revoked certificate should replace previously known responses. + if (aResult != Success && aResult != Result::ERROR_OCSP_UNKNOWN_CERT && + aResult != Result::ERROR_REVOKED_CERTIFICATE) { + LogWithCertID( + "OCSPCache::Put(%p, \"%s\") already in cache - not " + "replacing with less important status", + aCertID, aOriginAttributes); + MakeMostRecentlyUsed(index, lock); + return Success; + } + + LogWithCertID("OCSPCache::Put(%p, \"%s\") already in cache - replacing", + aCertID, aOriginAttributes); + mEntries[index]->mResult = aResult; + mEntries[index]->mThisUpdate = aThisUpdate; + mEntries[index]->mValidThrough = aValidThrough; + MakeMostRecentlyUsed(index, lock); + return Success; + } + + if (mEntries.length() == MaxEntries) { + LogWithCertID("OCSPCache::Put(%p, \"%s\") too full - evicting an entry", + aCertID, aOriginAttributes); + for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end(); + toEvict++) { + // Never evict an entry that indicates a revoked or unknokwn certificate, + // because revoked responses are more security-critical to remember. + if ((*toEvict)->mResult != Result::ERROR_REVOKED_CERTIFICATE && + (*toEvict)->mResult != Result::ERROR_OCSP_UNKNOWN_CERT) { + delete *toEvict; + mEntries.erase(toEvict); + break; + } + } + // Well, we tried, but apparently everything is revoked or unknown. + // We don't want to remove a cached revoked or unknown response. If we're + // trying to insert a good response, we can just return "successfully" + // without doing so. This means we'll lose some speed, but it's not a + // security issue. If we're trying to insert a revoked or unknown response, + // we can't. We should return with an error that causes the current + // verification to fail. + if (mEntries.length() == MaxEntries) { + return aResult; + } + } + + Entry* newEntry = + new (std::nothrow) Entry(aResult, aThisUpdate, aValidThrough); + // Normally we don't have to do this in Gecko, because OOM is fatal. + // However, if we want to embed this in another project, OOM might not + // be fatal, so handle this case. + if (!newEntry) { + return Result::FATAL_ERROR_NO_MEMORY; + } + Result rv = newEntry->Init(aCertID, aOriginAttributes); + if (rv != Success) { + delete newEntry; + return rv; + } + if (!mEntries.append(newEntry)) { + delete newEntry; + return Result::FATAL_ERROR_NO_MEMORY; + } + LogWithCertID("OCSPCache::Put(%p, \"%s\") added to cache", aCertID, + aOriginAttributes); + return Success; +} + +void OCSPCache::Clear() { + MutexAutoLock lock(mMutex); + MOZ_LOG(gCertVerifierLog, LogLevel::Debug, + ("OCSPCache::Clear: clearing cache")); + // First go through and delete the memory being pointed to by the pointers + // in the vector. + for (Entry** entry = mEntries.begin(); entry < mEntries.end(); entry++) { + delete *entry; + } + // Then remove the pointers themselves. + mEntries.clearAndFree(); +} + +} // namespace psm +} // namespace mozilla diff --git a/security/certverifier/OCSPCache.h b/security/certverifier/OCSPCache.h new file mode 100644 index 0000000000..d2ff7dfc89 --- /dev/null +++ b/security/certverifier/OCSPCache.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef mozilla_psm_OCSPCache_h +#define mozilla_psm_OCSPCache_h + +#include "hasht.h" +#include "mozilla/Mutex.h" +#include "mozilla/Vector.h" +#include "mozpkix/Result.h" +#include "mozpkix/Time.h" +#include "prerror.h" +#include "seccomon.h" + +namespace mozilla { +class OriginAttributes; +} + +namespace mozilla { +namespace pkix { +struct CertID; +} +} // namespace mozilla + +namespace mozilla { +namespace psm { + +// make SHA384Buffer be of type "array of uint8_t of length SHA384_LENGTH" +typedef uint8_t SHA384Buffer[SHA384_LENGTH]; + +// OCSPCache can store and retrieve OCSP response verification results. Each +// result is keyed on the certificate that purportedly corresponds to it (where +// certificates are distinguished based on serial number, issuer, and +// issuer public key, much like in an encoded OCSP response itself). A maximum +// of 1024 distinct entries can be stored. +// OCSPCache is thread-safe. +class OCSPCache { + public: + OCSPCache(); + ~OCSPCache(); + + // Returns true if the status of the given certificate (issued by the given + // issuer) is in the cache, and false otherwise. + // If it is in the cache, returns by reference the error code of the cached + // status and the time through which the status is considered trustworthy. + // The passed in origin attributes are used to isolate the OCSP cache. + // We currently only use the first party domain portion of the attributes, and + // it is non-empty only when "privacy.firstParty.isolate" is enabled. + bool Get(const mozilla::pkix::CertID& aCertID, + const OriginAttributes& aOriginAttributes, + /*out*/ mozilla::pkix::Result& aResult, + /*out*/ mozilla::pkix::Time& aValidThrough); + + // Caches the status of the given certificate (issued by the given issuer). + // The status is considered trustworthy through the given time. + // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not + // be replaced or evicted. + // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not + // be evicted when the cache is full. + // A status with a more recent thisUpdate will not be replaced with a + // status with a less recent thisUpdate unless the less recent status + // indicates the certificate is revoked. + // The passed in origin attributes are used to isolate the OCSP cache. + // We currently only use the first party domain portion of the attributes, and + // it is non-empty only when "privacy.firstParty.isolate" is enabled. + mozilla::pkix::Result Put(const mozilla::pkix::CertID& aCertID, + const OriginAttributes& aOriginAttributes, + mozilla::pkix::Result aResult, + mozilla::pkix::Time aThisUpdate, + mozilla::pkix::Time aValidThrough); + + // Removes everything from the cache. + void Clear(); + + private: + class Entry { + public: + Entry(mozilla::pkix::Result aResult, mozilla::pkix::Time aThisUpdate, + mozilla::pkix::Time aValidThrough) + : mResult(aResult), + mThisUpdate(aThisUpdate), + mValidThrough(aValidThrough) {} + mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID, + const OriginAttributes& aOriginAttributes); + + mozilla::pkix::Result mResult; + mozilla::pkix::Time mThisUpdate; + mozilla::pkix::Time mValidThrough; + // The SHA-384 hash of the concatenation of the DER encodings of the + // issuer name and issuer key, followed by the length of the serial number, + // the serial number, the length of the first party domain, and the first + // party domain (if "privacy.firstparty.isolate" is enabled). + // See the documentation for CertIDHash in OCSPCache.cpp. + SHA384Buffer mIDHash; + }; + + bool FindInternal(const mozilla::pkix::CertID& aCertID, + const OriginAttributes& aOriginAttributes, + /*out*/ size_t& index, const MutexAutoLock& aProofOfLock); + void MakeMostRecentlyUsed(size_t aIndex, const MutexAutoLock& aProofOfLock); + + Mutex mMutex; + static const size_t MaxEntries = 1024; + // Sorted with the most-recently-used entry at the end. + // Using 256 here reserves as much possible inline storage as the vector + // implementation will give us. 1024 bytes is the maximum it allows, + // which results in 256 Entry pointers or 128 Entry pointers, depending + // on the size of a pointer. + Vector<Entry*, 256> mEntries MOZ_GUARDED_BY(mMutex); +}; + +} // namespace psm +} // namespace mozilla + +#endif // mozilla_psm_OCSPCache_h diff --git a/security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc b/security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc new file mode 100644 index 0000000000..7a616efdd8 --- /dev/null +++ b/security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc @@ -0,0 +1,307 @@ +// Script from security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py +// Invocation: crtshToIdentifyingStruct.py -spki -listname RootAppleAndGoogleSPKIs 142951186 23635000 5250464 12716200 19602712 19602724 21760447 19602706 19602741 8656329 8568700 281399768 281399766 + +// /C=US/O=Google Inc/CN=Google Internet Authority G2 +// SHA256 Fingerprint: 9B:75:9D:41:E3:DE:30:F9:D2:F9:02:02:7D:79:2B:65 +// D9:50:A9:8B:BB:6D:6D:56:BE:7F:25:28:45:3B:F8:E9 +// https://crt.sh/?id=142951186 (crt.sh ID=142951186) +// +// and +// +// /C=US/O=Google Inc/CN=Google Internet Authority G2 +// SHA256 Fingerprint: 9F:63:04:26:DF:1D:8A:BF:D8:0A:CE:98:87:1B:A8:33 +// AB:97:42:CB:34:83:8D:E2:B5:28:5E:D5:4C:0C:7D:CC +// https://crt.sh/?id=23635000 (crt.sh ID=23635000) +static const uint8_t CAGoogleInternetAuthorityG2SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9C, 0x2A, 0x04, 0x77, 0x5C, 0xD8, + 0x50, 0x91, 0x3A, 0x06, 0xA3, 0x82, 0xE0, 0xD8, 0x50, 0x48, 0xBC, 0x89, 0x3F, + 0xF1, 0x19, 0x70, 0x1A, 0x88, 0x46, 0x7E, 0xE0, 0x8F, 0xC5, 0xF1, 0x89, 0xCE, + 0x21, 0xEE, 0x5A, 0xFE, 0x61, 0x0D, 0xB7, 0x32, 0x44, 0x89, 0xA0, 0x74, 0x0B, + 0x53, 0x4F, 0x55, 0xA4, 0xCE, 0x82, 0x62, 0x95, 0xEE, 0xEB, 0x59, 0x5F, 0xC6, + 0xE1, 0x05, 0x80, 0x12, 0xC4, 0x5E, 0x94, 0x3F, 0xBC, 0x5B, 0x48, 0x38, 0xF4, + 0x53, 0xF7, 0x24, 0xE6, 0xFB, 0x91, 0xE9, 0x15, 0xC4, 0xCF, 0xF4, 0x53, 0x0D, + 0xF4, 0x4A, 0xFC, 0x9F, 0x54, 0xDE, 0x7D, 0xBE, 0xA0, 0x6B, 0x6F, 0x87, 0xC0, + 0xD0, 0x50, 0x1F, 0x28, 0x30, 0x03, 0x40, 0xDA, 0x08, 0x73, 0x51, 0x6C, 0x7F, + 0xFF, 0x3A, 0x3C, 0xA7, 0x37, 0x06, 0x8E, 0xBD, 0x4B, 0x11, 0x04, 0xEB, 0x7D, + 0x24, 0xDE, 0xE6, 0xF9, 0xFC, 0x31, 0x71, 0xFB, 0x94, 0xD5, 0x60, 0xF3, 0x2E, + 0x4A, 0xAF, 0x42, 0xD2, 0xCB, 0xEA, 0xC4, 0x6A, 0x1A, 0xB2, 0xCC, 0x53, 0xDD, + 0x15, 0x4B, 0x8B, 0x1F, 0xC8, 0x19, 0x61, 0x1F, 0xCD, 0x9D, 0xA8, 0x3E, 0x63, + 0x2B, 0x84, 0x35, 0x69, 0x65, 0x84, 0xC8, 0x19, 0xC5, 0x46, 0x22, 0xF8, 0x53, + 0x95, 0xBE, 0xE3, 0x80, 0x4A, 0x10, 0xC6, 0x2A, 0xEC, 0xBA, 0x97, 0x20, 0x11, + 0xC7, 0x39, 0x99, 0x10, 0x04, 0xA0, 0xF0, 0x61, 0x7A, 0x95, 0x25, 0x8C, 0x4E, + 0x52, 0x75, 0xE2, 0xB6, 0xED, 0x08, 0xCA, 0x14, 0xFC, 0xCE, 0x22, 0x6A, 0xB3, + 0x4E, 0xCF, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7E, 0xC0, 0xB1, 0xDE, 0x7B, 0xAF, + 0x45, 0x33, 0xCF, 0xBA, 0x3E, 0x71, 0xB7, 0xDE, 0xF4, 0x25, 0x25, 0xC2, 0x0D, + 0x35, 0x89, 0x9D, 0x9D, 0xFB, 0x0E, 0x11, 0x79, 0x89, 0x1E, 0x37, 0xC5, 0xAF, + 0x8E, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: AC:2B:92:2E:CF:D5:E0:17:11:77:2F:EA:8E:D3:72:DE +// 9D:1E:22:45:FC:E3:F5:7A:9C:DB:EC:77:29:6A:42:4B +// https://crt.sh/?id=5250464 (crt.sh ID=5250464) +static const uint8_t CAAppleISTCA2G1SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD0, 0x93, 0xA1, 0x1D, 0x47, 0x43, + 0x20, 0x16, 0xB2, 0x0B, 0x6B, 0xEB, 0xC3, 0xD5, 0xB4, 0xE8, 0xC7, 0x98, 0xCD, + 0xF3, 0xDE, 0xBF, 0xE8, 0x4D, 0xE9, 0xE3, 0x36, 0x80, 0x07, 0xFC, 0x45, 0x1B, + 0x6A, 0x7C, 0x45, 0x86, 0xAE, 0x56, 0xD3, 0xA4, 0x09, 0x7F, 0x61, 0x0D, 0x6B, + 0x5D, 0x7E, 0x52, 0x6B, 0x7D, 0xB4, 0xC8, 0x39, 0xC4, 0xF4, 0x67, 0x3A, 0xF7, + 0x83, 0xCE, 0x19, 0x6F, 0x86, 0x2F, 0x7E, 0x45, 0x7E, 0x47, 0x1C, 0x67, 0x52, + 0xCA, 0x95, 0x05, 0x5D, 0xE2, 0x36, 0x51, 0x85, 0xC0, 0xD4, 0x67, 0x80, 0x35, + 0x6F, 0x15, 0xDD, 0x3E, 0xFD, 0x1D, 0xD2, 0xFD, 0x8F, 0x34, 0x50, 0xD8, 0xEC, + 0x76, 0x2A, 0xBE, 0xE3, 0xD3, 0xDA, 0xE4, 0xFD, 0xC8, 0xEB, 0x28, 0x02, 0x96, + 0x11, 0x97, 0x17, 0x61, 0x1C, 0xE9, 0xC4, 0x59, 0x3B, 0x42, 0xDC, 0x32, 0xD1, + 0x09, 0x1D, 0xDA, 0xA6, 0xD1, 0x43, 0x86, 0xFF, 0x5E, 0xB2, 0xBC, 0x8C, 0xCF, + 0x66, 0xDB, 0x01, 0x8B, 0x02, 0xAE, 0x94, 0x48, 0xF3, 0x38, 0x8F, 0xFD, 0xEA, + 0x32, 0xA8, 0x08, 0xEC, 0x86, 0x97, 0x51, 0x94, 0x24, 0x3E, 0x49, 0x49, 0x96, + 0x53, 0xE8, 0x79, 0xA1, 0x40, 0x81, 0xE9, 0x05, 0xBB, 0x93, 0x95, 0x51, 0xFC, + 0xE3, 0xFD, 0x7C, 0x11, 0x4B, 0xF7, 0x9E, 0x08, 0xB3, 0x15, 0x49, 0x15, 0x07, + 0xF9, 0xD1, 0x37, 0xA0, 0x9B, 0x4B, 0x32, 0xF6, 0xB5, 0xC4, 0xDC, 0x6A, 0xD1, + 0xFC, 0x0A, 0xED, 0xF6, 0xE0, 0xC5, 0x29, 0xA0, 0xA8, 0x8B, 0x71, 0xFE, 0x0D, + 0x92, 0xBC, 0xFE, 0x54, 0x70, 0x18, 0x0A, 0x6D, 0xC7, 0xED, 0x0C, 0xFB, 0xC9, + 0x2D, 0x06, 0xC3, 0x8C, 0x85, 0xFC, 0xCB, 0x86, 0x5C, 0xD6, 0x36, 0x8E, 0x12, + 0x8B, 0x09, 0x7F, 0xFB, 0x19, 0x1A, 0x38, 0xD5, 0xF0, 0x94, 0x30, 0x7A, 0x0F, + 0xA6, 0x8C, 0xF3, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /CN=Apple IST CA 5 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: 3D:B7:6D:1D:D7:D3:A7:59:DC:CC:3F:8F:A7:F6:86:75 +// C0:80:CB:09:5E:48:81:06:3A:6B:85:0F:DD:68:B8:BC +// https://crt.sh/?id=12716200 (crt.sh ID=12716200) +static const uint8_t CAAppleISTCA5G1SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xF0, 0x8A, 0x08, 0xBA, 0x2C, 0x13, + 0x5C, 0x5A, 0xF1, 0x98, 0xFD, 0x31, 0x59, 0x66, 0xC2, 0x56, 0x7A, 0x7E, 0x40, + 0x2A, 0x4C, 0x94, 0xC9, 0x68, 0xB6, 0xB3, 0x23, 0xBD, 0x60, 0x1B, 0x3B, 0xE7, + 0xFD, 0x3D, 0x5D, 0x70, 0x26, 0xC5, 0x3A, 0xAA, 0xB0, 0xCA, 0x69, 0x64, 0x0B, + 0x62, 0x3E, 0x49, 0xE9, 0x4C, 0x05, 0x21, 0xBE, 0x34, 0xF4, 0xAA, 0x73, 0x21, + 0x13, 0x31, 0x84, 0xE8, 0xCE, 0xEF, 0x38, 0xCF, 0x57, 0xE9, 0xDB, 0xCB, 0xCE, + 0xD1, 0x6D, 0xFA, 0xC8, 0x81, 0x92, 0x2D, 0x22, 0xCE, 0x15, 0x7E, 0x7E, 0xB1, + 0x07, 0xAC, 0x88, 0xC7, 0x18, 0x92, 0xC1, 0x96, 0xC6, 0x0C, 0x90, 0x26, 0x17, + 0x55, 0x5F, 0x19, 0x1B, 0x25, 0xCF, 0x9E, 0x51, 0x34, 0xFA, 0xF3, 0xE7, 0xB1, + 0x1C, 0x78, 0x18, 0xDA, 0xE4, 0x39, 0x1A, 0x91, 0x1B, 0xC2, 0xDF, 0xA8, 0x00, + 0x5B, 0x5F, 0x4E, 0xC4, 0x22, 0xB4, 0xBA, 0x64, 0xE2, 0x4A, 0x77, 0xBA, 0xED, + 0x2C, 0xEB, 0xFE, 0x8B, 0x61, 0x96, 0xF0, 0x1E, 0x84, 0x2D, 0x74, 0x0A, 0x7B, + 0x17, 0xCD, 0xC3, 0xEE, 0x00, 0x6E, 0xD7, 0x66, 0x79, 0x8B, 0x50, 0xE9, 0x4F, + 0xAF, 0xA6, 0x3D, 0x91, 0x31, 0x2F, 0xCA, 0x87, 0x2B, 0xCF, 0xF7, 0x08, 0x49, + 0x14, 0x8A, 0x8E, 0x62, 0x7D, 0xAD, 0x56, 0xAA, 0x95, 0x62, 0xE3, 0xE9, 0x6B, + 0x4E, 0x64, 0x41, 0xE2, 0x4F, 0x22, 0xF7, 0x4B, 0x56, 0xF1, 0x2C, 0xA8, 0x71, + 0x11, 0x38, 0x09, 0x8B, 0x97, 0xB9, 0x08, 0xBF, 0xCF, 0x30, 0x26, 0x83, 0x40, + 0x90, 0x63, 0x1A, 0xB6, 0x69, 0xBA, 0x79, 0xB7, 0xAE, 0x59, 0xEC, 0x6B, 0x0D, + 0x84, 0x47, 0xA7, 0xAE, 0x0B, 0x47, 0x4C, 0x06, 0xFB, 0x76, 0x82, 0x69, 0x7B, + 0x5E, 0x23, 0x60, 0x52, 0x35, 0xD0, 0xAC, 0x46, 0x1C, 0xEA, 0xA0, 0xB6, 0x5A, + 0x8B, 0xD9, 0xED, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /CN=Apple IST CA 4 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: 61:15:F0:6A:33:8A:64:9E:61:58:52:10:E7:6F:2E:CE +// 39:89:BC:A6:5A:62:B0:66:04:0C:D7:C5:F4:08:ED:D0 +// https://crt.sh/?id=19602712 (crt.sh ID=19602712) +static const uint8_t CAAppleISTCA4G1SPKI[91] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, + 0x04, 0x61, 0xC3, 0xA3, 0x9E, 0x49, 0x7E, 0xCA, 0x08, 0x86, 0xEB, 0x46, 0xD9, + 0x46, 0x87, 0x67, 0xE0, 0x3A, 0x5D, 0xDB, 0x25, 0xE0, 0x83, 0x34, 0x03, 0xCB, + 0xB7, 0xD1, 0xBE, 0xAD, 0x43, 0x1D, 0x10, 0x3A, 0xC4, 0x16, 0xDD, 0x71, 0x12, + 0xB4, 0xFD, 0x93, 0x60, 0x69, 0x49, 0xFA, 0xBF, 0x4F, 0x90, 0xE0, 0xC7, 0x3F, + 0x3F, 0xBE, 0x08, 0xC1, 0x28, 0xAE, 0xFE, 0x7F, 0x2D, 0x9E, 0x37, 0x08, 0x4F, +}; + +// /CN=Apple IST CA 7 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: 17:F9:66:09:AC:6A:D0:A2:D6:AB:0A:21:B2:D1:B5:B2 +// 94:6B:D0:4D:BF:12:07:03:D1:DE:F6:FB:62:F4:B6:61 +// https://crt.sh/?id=19602724 (crt.sh ID=19602724) +static const uint8_t CAAppleISTCA7G1SPKI[91] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, + 0x04, 0x55, 0x9A, 0x69, 0x4D, 0x4B, 0xE2, 0x14, 0xD3, 0xA7, 0xFF, 0x6A, 0xC4, + 0x22, 0xD4, 0xDD, 0x76, 0x64, 0xE0, 0x64, 0xA6, 0x1D, 0x2A, 0x4D, 0x47, 0xB1, + 0x4B, 0x13, 0x4A, 0x3E, 0xA6, 0xD2, 0x37, 0x18, 0xDF, 0xB4, 0x3B, 0x69, 0xB3, + 0xFC, 0x20, 0xC8, 0x43, 0x4C, 0x3F, 0x26, 0x5C, 0xC0, 0xE6, 0x9F, 0x6D, 0xDA, + 0x05, 0xFF, 0xD2, 0xAE, 0x58, 0xF0, 0xA4, 0xA1, 0xF1, 0xB4, 0x59, 0xB1, 0x8C, +}; + +// /CN=Apple IST CA 8 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: A4:FE:7C:7F:15:15:5F:3F:0A:EF:7A:AA:83:CF:6E:06 +// DE:B9:7C:A3:F9:09:DF:92:0A:C1:49:08:82:D4:88:ED +// https://crt.sh/?id=21760447 (crt.sh ID=21760447) +static const uint8_t CAAppleISTCA8G1SPKI[91] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, + 0x04, 0x2D, 0x54, 0x8E, 0x68, 0xB0, 0x10, 0x13, 0xEF, 0xF4, 0x2D, 0xDB, 0xD8, + 0x09, 0xB2, 0x43, 0xE8, 0x9D, 0x65, 0x99, 0x6E, 0x34, 0xB8, 0xD4, 0x97, 0xC9, + 0x8E, 0xC9, 0xD6, 0xA8, 0x22, 0x37, 0x32, 0xC8, 0x71, 0x60, 0xEE, 0xB0, 0xF1, + 0xF2, 0xC5, 0x64, 0xF6, 0xBA, 0x47, 0x5F, 0xFC, 0xE6, 0x07, 0x78, 0x32, 0x2D, + 0xF6, 0xCA, 0x80, 0x20, 0xC9, 0xFD, 0x70, 0xF8, 0x72, 0x93, 0x21, 0x88, 0x45, +}; + +// /CN=Apple IST CA 3 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: 6D:E9:09:78:91:04:22:A8:9E:26:F2:DF:85:97:14:30 +// C3:F4:4C:D1:78:5D:AD:94:30:8F:7C:A4:B6:FB:E5:21 +// https://crt.sh/?id=19602706 (crt.sh ID=19602706) +static const uint8_t CAAppleISTCA3G1SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD7, 0xED, 0xF5, 0x8A, 0xCC, 0x0C, + 0xA7, 0x2F, 0xCC, 0xFB, 0xA4, 0xC6, 0x26, 0x7C, 0x56, 0x7A, 0x47, 0xAE, 0xF9, + 0xC2, 0x3D, 0xCC, 0x66, 0x9A, 0xE2, 0x6F, 0x52, 0x89, 0xC4, 0x98, 0x27, 0x9D, + 0xB3, 0x17, 0x33, 0x55, 0xAC, 0xF3, 0xFF, 0xFD, 0xC1, 0xF6, 0xBA, 0x50, 0x14, + 0x03, 0x72, 0xDD, 0xAB, 0x78, 0xFD, 0xFA, 0x7D, 0x20, 0xB3, 0x3E, 0x32, 0xFA, + 0x44, 0xB8, 0xC9, 0x0D, 0x58, 0x28, 0x4E, 0x6C, 0x81, 0x78, 0x6D, 0xA3, 0x42, + 0xD0, 0xA5, 0x6A, 0xE1, 0x69, 0xE7, 0xC2, 0x29, 0x24, 0xEC, 0x86, 0xA0, 0xA4, + 0xF2, 0x36, 0x3D, 0xB4, 0x64, 0xAB, 0x16, 0x6F, 0x4B, 0xD7, 0x0D, 0xA6, 0xB4, + 0x61, 0x3E, 0xD2, 0x7D, 0xF5, 0x2F, 0x22, 0x4F, 0x99, 0x11, 0x1F, 0x33, 0xA1, + 0x15, 0xD7, 0x80, 0x7C, 0xD0, 0x65, 0xCA, 0xD4, 0xE0, 0x9B, 0x76, 0xF3, 0x56, + 0x56, 0xEE, 0xB6, 0x56, 0x40, 0x4E, 0xA9, 0x2B, 0x78, 0x3E, 0xD2, 0xF4, 0x0B, + 0x1E, 0x81, 0xFC, 0xE3, 0xFD, 0xAD, 0x94, 0x07, 0x15, 0x0F, 0x2A, 0x48, 0x19, + 0xEE, 0xB8, 0xA1, 0x56, 0x1A, 0x3D, 0xBC, 0xA8, 0x38, 0xD7, 0x80, 0x0C, 0x14, + 0x8F, 0x6C, 0xAD, 0x5F, 0xF3, 0x83, 0xD0, 0xE7, 0xDD, 0x5D, 0x25, 0xAB, 0x12, + 0x95, 0x31, 0x8C, 0x7E, 0xF9, 0xA1, 0x38, 0xB8, 0x04, 0xB7, 0xE1, 0x73, 0x74, + 0x52, 0xF9, 0x4B, 0x6F, 0xDA, 0x26, 0xB9, 0x40, 0x0D, 0x35, 0x41, 0x8D, 0x65, + 0xA9, 0x25, 0xAB, 0xD2, 0xB5, 0xE2, 0x9C, 0x54, 0x93, 0x64, 0xFC, 0xE7, 0x9E, + 0x67, 0x43, 0x31, 0xE5, 0x63, 0xF0, 0x15, 0x58, 0x39, 0x0A, 0x56, 0x8B, 0xEA, + 0x8C, 0xB2, 0xBE, 0x72, 0x64, 0xF1, 0x2E, 0x35, 0x57, 0xF3, 0xCD, 0x22, 0x1B, + 0x7A, 0xC3, 0x27, 0xD1, 0x65, 0x2C, 0x5B, 0xB3, 0xF5, 0xA4, 0x88, 0x64, 0x86, + 0xAC, 0x70, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /CN=Apple IST CA 6 - G1/OU=Certification Authority/O=Apple Inc./C=US +// SHA256 Fingerprint: 90:4F:B5:A4:37:75:4B:1B:32:B8:0E:BA:E7:41:6D:B6 +// 3D:05:F5:6A:99:39:72:0B:7C:8E:3D:CC:54:F6:A3:D1 +// https://crt.sh/?id=19602741 (crt.sh ID=19602741) +static const uint8_t CAAppleISTCA6G1SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE7, 0xDC, 0x81, 0xF0, 0x9A, 0x55, + 0x51, 0xFD, 0x03, 0x80, 0xFD, 0xCA, 0xF5, 0xF8, 0x2E, 0xEF, 0xD4, 0xBC, 0xC1, + 0xF0, 0x1F, 0x19, 0x09, 0x0C, 0x74, 0x3F, 0x55, 0x3A, 0xCA, 0x6E, 0xA5, 0x86, + 0x2C, 0xFB, 0x65, 0x7E, 0x07, 0xDC, 0x18, 0x60, 0xE2, 0xDF, 0x0D, 0x09, 0x19, + 0x84, 0xB1, 0xE5, 0x07, 0x1A, 0x1E, 0x08, 0x25, 0xCC, 0x59, 0x3E, 0x6B, 0x62, + 0x81, 0xD1, 0x55, 0x85, 0xDC, 0x86, 0x91, 0x3F, 0xF1, 0xBB, 0xF7, 0x1C, 0xAE, + 0x79, 0x7C, 0x41, 0xD0, 0x7D, 0xD0, 0x98, 0x3C, 0x2B, 0xCE, 0x53, 0x6F, 0x47, + 0xD5, 0x6E, 0x48, 0x6F, 0x7E, 0x4D, 0x2E, 0x90, 0x37, 0x6F, 0x84, 0x4F, 0x20, + 0x75, 0x8C, 0x1B, 0xAB, 0x3A, 0x00, 0xB2, 0xDC, 0xB2, 0x3A, 0x3F, 0x7F, 0xCD, + 0xF7, 0xC6, 0xC0, 0x91, 0xA2, 0xBB, 0x0C, 0x55, 0x84, 0xB9, 0x85, 0xBE, 0x1D, + 0xBB, 0x16, 0x8E, 0x81, 0x12, 0xCF, 0x0E, 0x3A, 0xBA, 0x36, 0x76, 0x0F, 0x14, + 0xF9, 0x23, 0x04, 0x66, 0x03, 0x58, 0x22, 0xFC, 0xF8, 0x7E, 0x67, 0x3F, 0x5C, + 0x6B, 0x8D, 0xAC, 0xAE, 0xE4, 0x2E, 0x6D, 0x2E, 0x18, 0xEB, 0xCC, 0xDD, 0x5C, + 0x8F, 0x04, 0x97, 0xD3, 0xEF, 0x0F, 0x06, 0x02, 0x93, 0x16, 0x86, 0x46, 0xF5, + 0x19, 0x5B, 0x6F, 0x72, 0x85, 0x07, 0x46, 0x09, 0x6F, 0xE9, 0x76, 0x17, 0xF7, + 0x83, 0x18, 0x5F, 0xB7, 0x1A, 0xA2, 0x39, 0x97, 0xCA, 0x29, 0x60, 0xC2, 0xF2, + 0x73, 0x83, 0x0D, 0x84, 0xD1, 0xBF, 0xDB, 0x9E, 0xEF, 0x6A, 0x7F, 0xD1, 0xD1, + 0xFB, 0x76, 0xFC, 0xEF, 0x5C, 0xBB, 0x0F, 0x16, 0x0C, 0x8B, 0xC2, 0x0B, 0x70, + 0x86, 0x5E, 0xD3, 0x52, 0xDD, 0x6E, 0x76, 0xB4, 0x7F, 0xBF, 0x50, 0x60, 0x38, + 0xF1, 0x64, 0xF5, 0xD6, 0x50, 0xEF, 0x43, 0xAD, 0x6B, 0x8C, 0xD4, 0x30, 0xB0, + 0xB3, 0xC9, 0x29, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root G2 +// SHA256 Fingerprint: CB:3C:CB:B7:60:31:E5:E0:13:8F:8D:D3:9A:23:F9:DE +// 47:FF:C3:5E:43:C1:14:4C:EA:27:D4:6A:5A:B1:CB:5F +// https://crt.sh/?id=8656329 (crt.sh ID=8656329) +static const uint8_t CADigiCertGlobalRootG2SPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBB, 0x37, 0xCD, 0x34, 0xDC, 0x7B, + 0x6B, 0xC9, 0xB2, 0x68, 0x90, 0xAD, 0x4A, 0x75, 0xFF, 0x46, 0xBA, 0x21, 0x0A, + 0x08, 0x8D, 0xF5, 0x19, 0x54, 0xC9, 0xFB, 0x88, 0xDB, 0xF3, 0xAE, 0xF2, 0x3A, + 0x89, 0x91, 0x3C, 0x7A, 0xE6, 0xAB, 0x06, 0x1A, 0x6B, 0xCF, 0xAC, 0x2D, 0xE8, + 0x5E, 0x09, 0x24, 0x44, 0xBA, 0x62, 0x9A, 0x7E, 0xD6, 0xA3, 0xA8, 0x7E, 0xE0, + 0x54, 0x75, 0x20, 0x05, 0xAC, 0x50, 0xB7, 0x9C, 0x63, 0x1A, 0x6C, 0x30, 0xDC, + 0xDA, 0x1F, 0x19, 0xB1, 0xD7, 0x1E, 0xDE, 0xFD, 0xD7, 0xE0, 0xCB, 0x94, 0x83, + 0x37, 0xAE, 0xEC, 0x1F, 0x43, 0x4E, 0xDD, 0x7B, 0x2C, 0xD2, 0xBD, 0x2E, 0xA5, + 0x2F, 0xE4, 0xA9, 0xB8, 0xAD, 0x3A, 0xD4, 0x99, 0xA4, 0xB6, 0x25, 0xE9, 0x9B, + 0x6B, 0x00, 0x60, 0x92, 0x60, 0xFF, 0x4F, 0x21, 0x49, 0x18, 0xF7, 0x67, 0x90, + 0xAB, 0x61, 0x06, 0x9C, 0x8F, 0xF2, 0xBA, 0xE9, 0xB4, 0xE9, 0x92, 0x32, 0x6B, + 0xB5, 0xF3, 0x57, 0xE8, 0x5D, 0x1B, 0xCD, 0x8C, 0x1D, 0xAB, 0x95, 0x04, 0x95, + 0x49, 0xF3, 0x35, 0x2D, 0x96, 0xE3, 0x49, 0x6D, 0xDD, 0x77, 0xE3, 0xFB, 0x49, + 0x4B, 0xB4, 0xAC, 0x55, 0x07, 0xA9, 0x8F, 0x95, 0xB3, 0xB4, 0x23, 0xBB, 0x4C, + 0x6D, 0x45, 0xF0, 0xF6, 0xA9, 0xB2, 0x95, 0x30, 0xB4, 0xFD, 0x4C, 0x55, 0x8C, + 0x27, 0x4A, 0x57, 0x14, 0x7C, 0x82, 0x9D, 0xCD, 0x73, 0x92, 0xD3, 0x16, 0x4A, + 0x06, 0x0C, 0x8C, 0x50, 0xD1, 0x8F, 0x1E, 0x09, 0xBE, 0x17, 0xA1, 0xE6, 0x21, + 0xCA, 0xFD, 0x83, 0xE5, 0x10, 0xBC, 0x83, 0xA5, 0x0A, 0xC4, 0x67, 0x28, 0xF6, + 0x73, 0x14, 0x14, 0x3D, 0x46, 0x76, 0xC3, 0x87, 0x14, 0x89, 0x21, 0x34, 0x4D, + 0xAF, 0x0F, 0x45, 0x0C, 0xA6, 0x49, 0xA1, 0xBA, 0xBB, 0x9C, 0xC5, 0xB1, 0x33, + 0x83, 0x29, 0x85, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +// /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root G3 +// SHA256 Fingerprint: 31:AD:66:48:F8:10:41:38:C7:38:F3:9E:A4:32:01:33 +// 39:3E:3A:18:CC:02:29:6E:F9:7C:2A:C9:EF:67:31:D0 +// https://crt.sh/?id=8568700 (crt.sh ID=8568700) +static const uint8_t CADigiCertGlobalRootG3SPKI[120] = { + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xDD, 0xA7, + 0xD9, 0xBB, 0x8A, 0xB8, 0x0B, 0xFB, 0x0B, 0x7F, 0x21, 0xD2, 0xF0, 0xBE, 0xBE, + 0x73, 0xF3, 0x33, 0x5D, 0x1A, 0xBC, 0x34, 0xEA, 0xDE, 0xC6, 0x9B, 0xBC, 0xD0, + 0x95, 0xF6, 0xF0, 0xCC, 0xD0, 0x0B, 0xBA, 0x61, 0x5B, 0x51, 0x46, 0x7E, 0x9E, + 0x2D, 0x9F, 0xEE, 0x8E, 0x63, 0x0C, 0x17, 0xEC, 0x07, 0x70, 0xF5, 0xCF, 0x84, + 0x2E, 0x40, 0x83, 0x9C, 0xE8, 0x3F, 0x41, 0x6D, 0x3B, 0xAD, 0xD3, 0xA4, 0x14, + 0x59, 0x36, 0x78, 0x9D, 0x03, 0x43, 0xEE, 0x10, 0x13, 0x6C, 0x72, 0xDE, 0xAE, + 0x88, 0xA7, 0xA1, 0x6B, 0xB5, 0x43, 0xCE, 0x67, 0xDC, 0x23, 0xFF, 0x03, 0x1C, + 0xA3, 0xE2, 0x3E, +}; + +// /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Transition ECC Root +// SHA256 Fingerprint: 45:BF:04:DC:A5:DE:7A:63:39:F1:DF:83:5B:C9:01:34 +// 57:B4:87:FD:B4:30:8E:40:80:C6:42:3C:8E:4B:27:05 +// https://crt.sh/?id=281399768 (crt.sh ID=281399768) +static const uint8_t CADigiCertTransitionECCRootSPKI[91] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, + 0x04, 0x5A, 0xFF, 0x46, 0xDC, 0xC9, 0xAE, 0xBD, 0x2C, 0xE7, 0x1C, 0x56, 0x97, + 0xE4, 0xFA, 0xEB, 0xD5, 0xC6, 0xFF, 0x75, 0x53, 0x23, 0x5E, 0xC6, 0xB0, 0x7D, + 0xAC, 0xAC, 0x57, 0x3A, 0x9F, 0x94, 0x50, 0x07, 0x0D, 0xF1, 0xF3, 0x4D, 0x51, + 0x0D, 0x7D, 0xFD, 0x88, 0x41, 0x82, 0x3F, 0x1C, 0x7F, 0xFB, 0xC3, 0x1E, 0xFA, + 0xF6, 0xEB, 0xD4, 0x37, 0xFF, 0xFE, 0x18, 0x9D, 0x01, 0x83, 0x2A, 0x80, 0x3A, +}; + +// /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Transition RSA Root +// SHA256 Fingerprint: E5:2B:44:CD:1E:6A:9A:DA:0A:04:09:D1:CC:5D:73:A6 +// F4:17:60:3D:70:E6:F5:DC:54:83:AB:8A:DA:EF:3C:A4 +// https://crt.sh/?id=281399766 (crt.sh ID=281399766) +static const uint8_t CADigiCertTransitionRSARootSPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB0, 0x3E, 0xD8, 0x46, 0x63, 0x32, + 0xDF, 0x49, 0x1F, 0x61, 0x6D, 0xAE, 0xDF, 0xC9, 0x7F, 0x2B, 0xB1, 0x63, 0xA1, + 0xA7, 0xE6, 0x46, 0x35, 0x34, 0x0E, 0xD4, 0xA5, 0x3D, 0x12, 0xAF, 0x04, 0x6A, + 0xD5, 0xF8, 0xBA, 0xA7, 0x65, 0x93, 0xEC, 0x66, 0xC5, 0xCA, 0xEB, 0x68, 0x01, + 0x24, 0x69, 0x1F, 0xAF, 0xB0, 0xA3, 0x59, 0xAF, 0x3C, 0x5B, 0x39, 0x44, 0x29, + 0x60, 0x6E, 0x8B, 0x41, 0x98, 0x49, 0x21, 0xD8, 0x18, 0x13, 0xD3, 0x41, 0x55, + 0xFE, 0xAA, 0x22, 0x7E, 0xA7, 0x51, 0x4A, 0xA6, 0xD0, 0x23, 0x5F, 0x73, 0x84, + 0xA2, 0x9C, 0xB4, 0xCB, 0x17, 0xD0, 0x65, 0x24, 0x87, 0xE9, 0x80, 0xCB, 0xB7, + 0x3C, 0xA1, 0x10, 0xF5, 0x97, 0xB5, 0x0D, 0x9D, 0xEC, 0xF7, 0xBA, 0x5B, 0xA3, + 0x0B, 0x65, 0xEB, 0x12, 0x75, 0xA9, 0x46, 0x74, 0x0D, 0x80, 0xD7, 0x08, 0x13, + 0x93, 0x21, 0x57, 0xC6, 0x38, 0x3D, 0xA8, 0x4B, 0x3B, 0x0B, 0x6F, 0x18, 0xE5, + 0xB3, 0x4C, 0xF7, 0xC2, 0xCD, 0x18, 0xF9, 0x58, 0x2D, 0x03, 0x33, 0x1B, 0xFC, + 0x16, 0xDD, 0x90, 0x4E, 0xC2, 0x1F, 0x37, 0x9C, 0xD6, 0x7B, 0x61, 0x96, 0xF1, + 0xC5, 0x26, 0x87, 0x52, 0xE3, 0xE2, 0xA4, 0xF8, 0x15, 0xE5, 0x4C, 0x22, 0xE9, + 0x09, 0x2B, 0x95, 0xD1, 0x93, 0xF9, 0x3A, 0x39, 0x76, 0x74, 0x2A, 0x0B, 0x80, + 0xBE, 0xBE, 0x0E, 0xD3, 0x10, 0x0B, 0xE2, 0xE1, 0x48, 0xA6, 0x24, 0x05, 0x69, + 0x3D, 0x17, 0xFD, 0xC7, 0x37, 0x21, 0xB2, 0xB0, 0xE3, 0x77, 0x47, 0x39, 0x87, + 0x01, 0xE0, 0x4E, 0xDB, 0x23, 0xE8, 0xF9, 0x39, 0x9F, 0x36, 0x46, 0x66, 0x23, + 0x1E, 0xC7, 0x22, 0x51, 0x44, 0x3F, 0x33, 0xC5, 0xF5, 0x76, 0xA9, 0xF8, 0x06, + 0xB0, 0x79, 0xCC, 0xEE, 0x41, 0xDC, 0x71, 0x8E, 0x0D, 0x50, 0x8E, 0xB0, 0x3C, + 0x48, 0xAB, 0xF5, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +static const DataAndLength RootAppleAndGoogleSPKIs[]= { + { CAGoogleInternetAuthorityG2SPKI, sizeof(CAGoogleInternetAuthorityG2SPKI) }, + { CAAppleISTCA2G1SPKI, sizeof(CAAppleISTCA2G1SPKI) }, + { CAAppleISTCA5G1SPKI, sizeof(CAAppleISTCA5G1SPKI) }, + { CAAppleISTCA4G1SPKI, sizeof(CAAppleISTCA4G1SPKI) }, + { CAAppleISTCA7G1SPKI, sizeof(CAAppleISTCA7G1SPKI) }, + { CAAppleISTCA8G1SPKI, sizeof(CAAppleISTCA8G1SPKI) }, + { CAAppleISTCA3G1SPKI, sizeof(CAAppleISTCA3G1SPKI) }, + { CAAppleISTCA6G1SPKI, sizeof(CAAppleISTCA6G1SPKI) }, + { CADigiCertGlobalRootG2SPKI, sizeof(CADigiCertGlobalRootG2SPKI) }, + { CADigiCertGlobalRootG3SPKI, sizeof(CADigiCertGlobalRootG3SPKI) }, + { CADigiCertTransitionECCRootSPKI, sizeof(CADigiCertTransitionECCRootSPKI) }, + { CADigiCertTransitionRSARootSPKI, sizeof(CADigiCertTransitionRSARootSPKI) }, +}; diff --git a/security/certverifier/TrustOverride-SymantecData.inc b/security/certverifier/TrustOverride-SymantecData.inc new file mode 100644 index 0000000000..d88bee2f66 --- /dev/null +++ b/security/certverifier/TrustOverride-SymantecData.inc @@ -0,0 +1,164 @@ +// Script from security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py +// Invocation: ../manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py -dn -listname RootSymantecDNs 12729019 8983600 12726040 8983601 8984570 68409 26682 + +// /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 1 Public Primary Certification Authority - G4 +// SHA256 Fingerprint: 36:3F:3C:84:9E:AB:03:B0:A2:A0:F6:36:D7:B8:6D:04 +// D3:AC:7F:CF:E2:6A:0A:91:21:AB:97:95:F6:E1:76:DF +// https://crt.sh/?id=12729019 (crt.sh ID=12729019) +static const uint8_t CASymantecClass1PublicPrimaryCertificationAuthorityG4DN[151] = { + 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x14, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6F, 0x72, + 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x1F, 0x30, 0x1D, 0x06, + 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, + 0x63, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, + 0x72, 0x6B, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3C, + 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6C, 0x61, 0x73, + 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x50, 0x72, + 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x34, +}; + +// /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 1 Public Primary Certification Authority - G6 +// SHA256 Fingerprint: 9D:19:0B:2E:31:45:66:68:5B:E8:A8:89:E2:7A:A8:C7 +// D7:AE:1D:8A:AD:DB:A3:C1:EC:F9:D2:48:63:CD:34:B9 +// https://crt.sh/?id=8983600 (crt.sh ID=8983600) +static const uint8_t CASymantecClass1PublicPrimaryCertificationAuthorityG6DN[151] = { + 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x14, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6F, 0x72, + 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x1F, 0x30, 0x1D, 0x06, + 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, + 0x63, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, + 0x72, 0x6B, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3C, + 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6C, 0x61, 0x73, + 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x50, 0x72, + 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x36, +}; + +// /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 2 Public Primary Certification Authority - G4 +// SHA256 Fingerprint: FE:86:3D:08:22:FE:7A:23:53:FA:48:4D:59:24:E8:75 +// 65:6D:3D:C9:FB:58:77:1F:6F:61:6F:9D:57:1B:C5:92 +// https://crt.sh/?id=12726040 (crt.sh ID=12726040) +static const uint8_t CASymantecClass2PublicPrimaryCertificationAuthorityG4DN[151] = { + 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x14, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6F, 0x72, + 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x1F, 0x30, 0x1D, 0x06, + 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, + 0x63, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, + 0x72, 0x6B, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3C, + 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6C, 0x61, 0x73, + 0x73, 0x20, 0x32, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x50, 0x72, + 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x34, +}; + +// /C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 2 Public Primary Certification Authority - G6 +// SHA256 Fingerprint: CB:62:7D:18:B5:8A:D5:6D:DE:33:1A:30:45:6B:C6:5C +// 60:1A:4E:9B:18:DE:DC:EA:08:E7:DA:AA:07:81:5F:F0 +// https://crt.sh/?id=8983601 (crt.sh ID=8983601) +static const uint8_t CASymantecClass2PublicPrimaryCertificationAuthorityG6DN[151] = { + 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x14, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6F, 0x72, + 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x1F, 0x30, 0x1D, 0x06, + 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, + 0x63, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, + 0x72, 0x6B, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3C, + 0x53, 0x79, 0x6D, 0x61, 0x6E, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6C, 0x61, 0x73, + 0x73, 0x20, 0x32, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x50, 0x72, + 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, + 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x36, +}; + +// /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 1 Public Primary Certification Authority - G3 +// SHA256 Fingerprint: CB:B5:AF:18:5E:94:2A:24:02:F9:EA:CB:C0:ED:5B:B8 +// 76:EE:A3:C1:22:36:23:D0:04:47:E4:F3:BA:55:4B:65 +// https://crt.sh/?id=8984570 (crt.sh ID=8984570) +static const uint8_t CAVeriSignClass1PublicPrimaryCertificationAuthorityG3DN[205] = { + 0x30, 0x81, 0xCA, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, + 0x63, 0x2E, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x31, 0x3A, 0x30, 0x38, + 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x39, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, + 0x49, 0x6E, 0x63, 0x2E, 0x20, 0x2D, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x6F, 0x6E, 0x6C, 0x79, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x3C, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x6C, + 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, + 0x50, 0x72, 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x33, +}; + +// /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 2 Public Primary Certification Authority - G3 +// SHA256 Fingerprint: 92:A9:D9:83:3F:E1:94:4D:B3:66:E8:BF:AE:7A:95:B6 +// 48:0C:2D:6C:6C:2A:1B:E6:5D:42:36:B6:08:FC:A1:BB +// https://crt.sh/?id=68409 (crt.sh ID=68409) +static const uint8_t CAVeriSignClass2PublicPrimaryCertificationAuthorityG3DN[205] = { + 0x30, 0x81, 0xCA, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, + 0x63, 0x2E, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x31, 0x3A, 0x30, 0x38, + 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x39, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, + 0x49, 0x6E, 0x63, 0x2E, 0x20, 0x2D, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x6F, 0x6E, 0x6C, 0x79, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x3C, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x6C, + 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, + 0x50, 0x72, 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x33, +}; + +// /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3 +// SHA256 Fingerprint: EB:04:CF:5E:B1:F3:9A:FA:76:2F:2B:B1:20:F2:96:CB +// A5:20:C1:B9:7D:B1:58:95:65:B8:1C:B9:A1:7B:72:44 +// https://crt.sh/?id=26682 (crt.sh ID=26682) +static const uint8_t CAVeriSignClass3PublicPrimaryCertificationAuthorityG3DN[205] = { + 0x30, 0x81, 0xCA, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, + 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, + 0x63, 0x2E, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x16, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x20, 0x4E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x31, 0x3A, 0x30, 0x38, + 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x39, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, + 0x49, 0x6E, 0x63, 0x2E, 0x20, 0x2D, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x61, 0x75, + 0x74, 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, + 0x6F, 0x6E, 0x6C, 0x79, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x3C, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x6C, + 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, + 0x50, 0x72, 0x69, 0x6D, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2D, 0x20, 0x47, 0x33, +}; + +static const DataAndLength RootSymantecDNs[]= { + { CASymantecClass1PublicPrimaryCertificationAuthorityG4DN, + sizeof(CASymantecClass1PublicPrimaryCertificationAuthorityG4DN) }, + { CASymantecClass1PublicPrimaryCertificationAuthorityG6DN, + sizeof(CASymantecClass1PublicPrimaryCertificationAuthorityG6DN) }, + { CASymantecClass2PublicPrimaryCertificationAuthorityG4DN, + sizeof(CASymantecClass2PublicPrimaryCertificationAuthorityG4DN) }, + { CASymantecClass2PublicPrimaryCertificationAuthorityG6DN, + sizeof(CASymantecClass2PublicPrimaryCertificationAuthorityG6DN) }, + { CAVeriSignClass1PublicPrimaryCertificationAuthorityG3DN, + sizeof(CAVeriSignClass1PublicPrimaryCertificationAuthorityG3DN) }, + { CAVeriSignClass2PublicPrimaryCertificationAuthorityG3DN, + sizeof(CAVeriSignClass2PublicPrimaryCertificationAuthorityG3DN) }, + { CAVeriSignClass3PublicPrimaryCertificationAuthorityG3DN, + sizeof(CAVeriSignClass3PublicPrimaryCertificationAuthorityG3DN) }, +}; diff --git a/security/certverifier/TrustOverrideUtils.h b/security/certverifier/TrustOverrideUtils.h new file mode 100644 index 0000000000..5c92cad45b --- /dev/null +++ b/security/certverifier/TrustOverrideUtils.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TrustOverrides_h +#define TrustOverrides_h + +#include "mozilla/ArrayUtils.h" +#include "mozpkix/pkix.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixutil.h" + +using namespace mozilla; +using namespace mozilla::pkix; + +struct DataAndLength { + const uint8_t* data; + uint32_t len; +}; + +template <size_t T> +static bool CertDNIsInList(const nsTArray<uint8_t>& aCert, + const DataAndLength (&aDnList)[T]) { + Input certInput; + mozilla::pkix::Result rv = certInput.Init(aCert.Elements(), aCert.Length()); + if (rv != Success) { + return false; + } + + // we don't use the certificate for path building, so this parameter doesn't + // matter + EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity; + BackCert cert(certInput, notUsedForPaths, nullptr); + rv = cert.Init(); + if (rv != Success) { + return false; + } + + Input subject(cert.GetSubject()); + + for (auto& dn : aDnList) { + Input dnInput; + rv = dnInput.Init(dn.data, dn.len); + if (rv != Success) { + return false; + } + + if (InputsAreEqual(subject, dnInput)) { + return true; + } + } + return false; +} + +template <size_t T> +static bool CertSPKIIsInList(Input aCertInput, + const DataAndLength (&aSpkiList)[T]) { + // we don't use the certificate for path building, so this parameter doesn't + // matter + EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity; + BackCert cert(aCertInput, notUsedForPaths, nullptr); + mozilla::pkix::Result rv = cert.Init(); + if (rv != Success) { + return false; + } + + Input publicKey(cert.GetSubjectPublicKeyInfo()); + + for (auto& spki : aSpkiList) { + Input spkiInput; + rv = spkiInput.Init(spki.data, spki.len); + if (rv != Success) { + return false; + } + + if (InputsAreEqual(publicKey, spkiInput)) { + return true; + } + } + return false; +} + +template <size_t T, size_t R> +static bool CertMatchesStaticData(const nsTArray<uint8_t>& aCert, + const unsigned char (&subject)[T], + const unsigned char (&spki)[R]) { + Input certInput; + mozilla::pkix::Result rv = certInput.Init(aCert.Elements(), aCert.Length()); + if (rv != Success) { + return false; + } + + // we don't use the certificate for path building, so this parameter doesn't + // matter + EndEntityOrCA notUsedForPaths = EndEntityOrCA::MustBeEndEntity; + BackCert cert(certInput, notUsedForPaths, nullptr); + rv = cert.Init(); + if (rv != Success) { + return false; + } + + Input certSubject(cert.GetSubject()); + Input certSPKI(cert.GetSubjectPublicKeyInfo()); + + Input subjectInput; + rv = subjectInput.Init(subject, T); + if (rv != Success) { + return false; + } + + Input spkiInput; + rv = spkiInput.Init(spki, R); + if (rv != Success) { + return false; + } + + return InputsAreEqual(certSubject, subjectInput) && + InputsAreEqual(certSPKI, spkiInput); +} + +// Implements the graduated Symantec distrust algorithm from Bug 1409257. +// This accepts a pre-segmented certificate chain (e.g. SegmentCertificateChain) +// as |intCerts|, and pre-assumes that the root has been identified +// as being affected (this is to avoid duplicate Segment operations in the +// NSSCertDBTrustDomain). Each of the |intCerts| is evaluated against a +// |allowlist| of SPKI entries, and if a match is found, then this returns +// "not distrusted." Otherwise, due to the precondition holding, the chain is +// "distrusted." +template <size_t T> +static nsresult CheckForSymantecDistrust(const nsTArray<Input>& intCerts, + const DataAndLength (&allowlist)[T], + /* out */ bool& isDistrusted) { + // PRECONDITION: The rootCert is already verified as being one of the + // affected Symantec roots + + isDistrusted = true; + + for (const auto& cert : intCerts) { + if (CertSPKIIsInList(cert, allowlist)) { + isDistrusted = false; + break; + } + } + return NS_OK; +} + +#endif // TrustOverrides_h diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build new file mode 100644 index 0000000000..cdf2a75814 --- /dev/null +++ b/security/certverifier/moz.build @@ -0,0 +1,54 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Security: PSM") + +EXPORTS += [ + "CertVerifier.h", + "OCSPCache.h", +] + +UNIFIED_SOURCES += [ + "CertVerifier.cpp", + "NSSCertDBTrustDomain.cpp", + "OCSPCache.cpp", +] + +if not CONFIG["NSS_NO_EV_CERTS"]: + UNIFIED_SOURCES += [ + "ExtendedValidation.cpp", + ] + +LOCAL_INCLUDES += [ + "/security/ct", + "/security/manager/ssl", +] + +DIRS += [ + "../ct", +] + +TEST_DIRS += [ + "tests/gtest", +] + +CXXFLAGS += [ + "-Wextra", + "-Wunreachable-code", +] + +# Gecko headers aren't warning-free enough for us to enable these warnings. +CXXFLAGS += [ + "-Wno-unused-parameter", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +if CONFIG["CC_TYPE"] == "clang-cl": + AllowCompilerWarnings() # workaround for bug 1090497 diff --git a/security/certverifier/tests/gtest/TrustOverrideTest.cpp b/security/certverifier/tests/gtest/TrustOverrideTest.cpp new file mode 100644 index 0000000000..a054085e61 --- /dev/null +++ b/security/certverifier/tests/gtest/TrustOverrideTest.cpp @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "nsCOMPtr.h" +#include "nsIPrefService.h" +#include "nsIX509CertDB.h" +#include "nsServiceManagerUtils.h" +#include "TrustOverrideUtils.h" + +// certspec (for pycert.py) +// +// issuer:ca +// subject:ca +// extension:basicConstraints:cA, +// extension:keyUsage:cRLSign,keyCertSign +// serialNumber:1 + +const uint8_t kOverrideCaDer[] = { + 0x30, 0x82, 0x02, 0xB2, 0x30, 0x82, 0x01, 0x9C, 0xA0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x30, 0x0D, 0x31, 0x0B, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0C, 0x02, 0x63, 0x61, 0x30, 0x22, 0x18, 0x0F, + 0x32, 0x30, 0x31, 0x35, 0x31, 0x31, 0x32, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x32, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x0D, 0x31, 0x0B, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x02, 0x63, 0x61, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, + 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x88, 0x51, 0xA8, + 0x44, 0x8E, 0x16, 0xD6, 0x41, 0xFD, 0x6E, 0xB6, 0x88, 0x06, 0x36, 0x10, + 0x3D, 0x3C, 0x13, 0xD9, 0xEA, 0xE4, 0x35, 0x4A, 0xB4, 0xEC, 0xF5, 0x68, + 0x57, 0x6C, 0x24, 0x7B, 0xC1, 0xC7, 0x25, 0xA8, 0xE0, 0xD8, 0x1F, 0xBD, + 0xB1, 0x9C, 0x06, 0x9B, 0x6E, 0x1A, 0x86, 0xF2, 0x6B, 0xE2, 0xAF, 0x5A, + 0x75, 0x6B, 0x6A, 0x64, 0x71, 0x08, 0x7A, 0xA5, 0x5A, 0xA7, 0x45, 0x87, + 0xF7, 0x1C, 0xD5, 0x24, 0x9C, 0x02, 0x7E, 0xCD, 0x43, 0xFC, 0x1E, 0x69, + 0xD0, 0x38, 0x20, 0x29, 0x93, 0xAB, 0x20, 0xC3, 0x49, 0xE4, 0xDB, 0xB9, + 0x4C, 0xC2, 0x6B, 0x6C, 0x0E, 0xED, 0x15, 0x82, 0x0F, 0xF1, 0x7E, 0xAD, + 0x69, 0x1A, 0xB1, 0xD3, 0x02, 0x3A, 0x8B, 0x2A, 0x41, 0xEE, 0xA7, 0x70, + 0xE0, 0x0F, 0x0D, 0x8D, 0xFD, 0x66, 0x0B, 0x2B, 0xB0, 0x24, 0x92, 0xA4, + 0x7D, 0xB9, 0x88, 0x61, 0x79, 0x90, 0xB1, 0x57, 0x90, 0x3D, 0xD2, 0x3B, + 0xC5, 0xE0, 0xB8, 0x48, 0x1F, 0xA8, 0x37, 0xD3, 0x88, 0x43, 0xEF, 0x27, + 0x16, 0xD8, 0x55, 0xB7, 0x66, 0x5A, 0xAA, 0x7E, 0x02, 0x90, 0x2F, 0x3A, + 0x7B, 0x10, 0x80, 0x06, 0x24, 0xCC, 0x1C, 0x6C, 0x97, 0xAD, 0x96, 0x61, + 0x5B, 0xB7, 0xE2, 0x96, 0x12, 0xC0, 0x75, 0x31, 0xA3, 0x0C, 0x91, 0xDD, + 0xB4, 0xCA, 0xF7, 0xFC, 0xAD, 0x1D, 0x25, 0xD3, 0x09, 0xEF, 0xB9, 0x17, + 0x0E, 0xA7, 0x68, 0xE1, 0xB3, 0x7B, 0x2F, 0x22, 0x6F, 0x69, 0xE3, 0xB4, + 0x8A, 0x95, 0x61, 0x1D, 0xEE, 0x26, 0xD6, 0x25, 0x9D, 0xAB, 0x91, 0x08, + 0x4E, 0x36, 0xCB, 0x1C, 0x24, 0x04, 0x2C, 0xBF, 0x16, 0x8B, 0x2F, 0xE5, + 0xF1, 0x8F, 0x99, 0x17, 0x31, 0xB8, 0xB3, 0xFE, 0x49, 0x23, 0xFA, 0x72, + 0x51, 0xC4, 0x31, 0xD5, 0x03, 0xAC, 0xDA, 0x18, 0x0A, 0x35, 0xED, 0x8D, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x1D, 0x30, 0x1B, 0x30, 0x0C, 0x06, + 0x03, 0x55, 0x1D, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, + 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, + 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, + 0x0B, 0x03, 0x82, 0x01, 0x01, 0x00, 0x72, 0x11, 0xDF, 0xD7, 0x25, 0x7E, + 0x6E, 0x51, 0x3A, 0x7E, 0xF4, 0xB9, 0xDC, 0xBD, 0xC3, 0x1A, 0x6B, 0xF1, + 0x12, 0x99, 0xFD, 0xFA, 0xDF, 0xB1, 0xC0, 0xEB, 0xD6, 0x73, 0x30, 0x66, + 0x2C, 0x51, 0x33, 0x50, 0xF7, 0x67, 0x71, 0xA9, 0x1D, 0x69, 0x69, 0x50, + 0xBF, 0xBA, 0xBC, 0xD4, 0x13, 0xF2, 0x8C, 0x1A, 0xA4, 0x61, 0xB4, 0x22, + 0xD7, 0x95, 0xAC, 0xF3, 0x58, 0x58, 0xDD, 0xD9, 0xD7, 0x9F, 0xC2, 0xD3, + 0xD5, 0x09, 0x2D, 0x08, 0xBF, 0x60, 0xBA, 0xF7, 0x37, 0xCF, 0x87, 0xCC, + 0x75, 0x06, 0xD1, 0x8E, 0x98, 0xDC, 0x21, 0x17, 0x82, 0x62, 0xDD, 0xC4, + 0x3C, 0x1D, 0xB6, 0x26, 0x5D, 0x85, 0x0A, 0x6E, 0xA2, 0x8B, 0x85, 0xCB, + 0xD2, 0xA0, 0xC5, 0x0F, 0x6E, 0x96, 0x2B, 0x7C, 0xCC, 0x7B, 0x65, 0x24, + 0xB3, 0xAB, 0x79, 0xE5, 0xFF, 0x15, 0x78, 0x34, 0xC1, 0x18, 0xDA, 0x0F, + 0xA7, 0x62, 0x4C, 0xB1, 0x66, 0x35, 0x3E, 0x2E, 0x2F, 0x47, 0xAC, 0x0A, + 0x01, 0x8F, 0x7A, 0x5A, 0x85, 0xD2, 0xFE, 0xAA, 0xD8, 0x92, 0x63, 0x04, + 0x3A, 0x01, 0x6E, 0xF7, 0x4E, 0xE9, 0x02, 0x97, 0x80, 0x78, 0x4F, 0x15, + 0x27, 0x35, 0xE3, 0xA8, 0xC2, 0xC5, 0xEB, 0xF5, 0xB5, 0x54, 0xDE, 0xC8, + 0x4A, 0x71, 0xB3, 0x59, 0x0F, 0x33, 0x29, 0x09, 0xE5, 0xA7, 0x76, 0x00, + 0x9A, 0xF5, 0x99, 0x41, 0xC0, 0xFA, 0x20, 0xD2, 0xBC, 0x9A, 0xFA, 0x59, + 0xC4, 0x87, 0xAE, 0xE8, 0xDB, 0x47, 0x68, 0xA1, 0x17, 0x1B, 0x0E, 0xED, + 0x94, 0x67, 0xA4, 0x9A, 0x45, 0x61, 0x6A, 0x93, 0xED, 0x41, 0x96, 0x27, + 0x59, 0xCB, 0x17, 0xC9, 0xB4, 0x6B, 0x65, 0xFE, 0x60, 0xDE, 0x13, 0xA1, + 0x24, 0x53, 0xE5, 0xF2, 0x45, 0xB8, 0xD9, 0x5A, 0x31, 0x5C, 0xBD, 0x75, + 0x3C, 0xFE, 0x4D, 0x5F, 0x6C, 0xA5, 0x4E, 0xC2, 0x7F, 0xFE}; + +// certspec (for pycert.py) +// +// issuer:ca +// subject:ca-intermediate +// extension:basicConstraints:cA, +// extension:keyUsage:cRLSign,keyCertSign +// subjectKey:secp384r1 +// serialNumber:2 + +const uint8_t kOverrideCaIntermediateDer[] = { + 0x30, 0x82, 0x02, 0x14, 0x30, 0x81, 0xFD, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x0D, 0x31, 0x0B, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x02, 0x63, 0x61, 0x30, 0x22, 0x18, + 0x0F, 0x32, 0x30, 0x31, 0x36, 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x39, 0x30, 0x32, + 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x1A, 0x31, + 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x63, 0x61, + 0x2D, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, + 0x65, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, + 0x04, 0xA1, 0x68, 0x72, 0x43, 0x36, 0x2B, 0x5C, 0x7B, 0x18, 0x89, 0xF3, + 0x79, 0x15, 0x46, 0x15, 0xA1, 0xC7, 0x3F, 0xB4, 0x8D, 0xEE, 0x86, 0x3E, + 0x02, 0x29, 0x15, 0xDB, 0x60, 0x8E, 0x25, 0x2D, 0xE4, 0xB7, 0x13, 0x2D, + 0xA8, 0xCE, 0x98, 0xE8, 0x31, 0x53, 0x4E, 0x6A, 0x9C, 0x0C, 0x0B, 0x09, + 0xC8, 0xD6, 0x39, 0xAD, 0xE8, 0x32, 0x06, 0xE5, 0xBA, 0x81, 0x34, 0x73, + 0xA1, 0x1F, 0xA3, 0x30, 0xE0, 0x5D, 0xA8, 0xC9, 0x6E, 0x43, 0x83, 0xFE, + 0x27, 0x87, 0x3D, 0xA9, 0x71, 0x03, 0xBE, 0x28, 0x88, 0xCF, 0xF0, 0x02, + 0xF0, 0x5A, 0xF7, 0x1A, 0x1F, 0xDD, 0xCC, 0x83, 0x74, 0xAA, 0x6E, 0xA9, + 0xCE, 0xA3, 0x1D, 0x30, 0x1B, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0B, 0x06, 0x03, 0x55, + 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0D, 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x0B, 0xC3, 0x68, 0x28, 0x89, 0x74, 0x5B, 0xEB, + 0xC2, 0x52, 0xC2, 0xA9, 0x33, 0x3B, 0xA8, 0x1F, 0x6C, 0x4B, 0xDB, 0xE3, + 0xCF, 0x79, 0x4B, 0xF3, 0x36, 0xD2, 0xB2, 0xC5, 0x25, 0xC7, 0x97, 0xD7, + 0xB4, 0x84, 0x00, 0x0A, 0x0D, 0x33, 0x25, 0x32, 0x94, 0xC8, 0xB2, 0x5F, + 0xFE, 0x74, 0xE5, 0x8A, 0xDF, 0xED, 0xC6, 0x13, 0xCE, 0xD1, 0x0E, 0xD3, + 0x3E, 0x32, 0xA9, 0x2A, 0x31, 0x38, 0xA2, 0x16, 0x0C, 0x10, 0x15, 0xBE, + 0x10, 0x7B, 0x3D, 0x53, 0x4F, 0xF2, 0x62, 0x8C, 0x55, 0xC2, 0x18, 0xDC, + 0x54, 0x65, 0x73, 0x11, 0x5B, 0x15, 0x41, 0x7C, 0xE0, 0x96, 0x5D, 0xF8, + 0xB7, 0x55, 0xB9, 0xAD, 0x5B, 0x71, 0xA1, 0xFE, 0x83, 0xAC, 0x7F, 0xE5, + 0x31, 0xCF, 0x58, 0xEE, 0xF8, 0x57, 0x15, 0x69, 0xD3, 0xF2, 0x3B, 0x22, + 0xA9, 0x7B, 0x5D, 0x21, 0x78, 0x77, 0x7E, 0x2F, 0x00, 0x8C, 0x2C, 0xD8, + 0x48, 0x08, 0xEC, 0x7D, 0x07, 0xF0, 0xA4, 0x74, 0x67, 0x33, 0xCB, 0x98, + 0xD8, 0x66, 0x80, 0x6F, 0xC4, 0xA8, 0xBD, 0x88, 0x86, 0x22, 0x4D, 0xAD, + 0x09, 0x27, 0x42, 0x06, 0x40, 0x3A, 0x57, 0xB8, 0x53, 0x79, 0x04, 0xB2, + 0x2C, 0x28, 0xF8, 0x26, 0xC0, 0xB3, 0x5F, 0x52, 0x73, 0x33, 0x1D, 0xF7, + 0x79, 0xBC, 0x37, 0x67, 0x5D, 0x0C, 0xAB, 0x60, 0x20, 0x46, 0x4D, 0xD4, + 0xC3, 0x9F, 0xCB, 0xF9, 0xBF, 0x8F, 0x29, 0x27, 0x19, 0x5F, 0x0E, 0x36, + 0x94, 0x93, 0x67, 0x89, 0x26, 0x5B, 0x1F, 0xB9, 0x47, 0xAF, 0x4F, 0x1B, + 0x5A, 0x15, 0x85, 0x5D, 0x52, 0xBC, 0x68, 0x3D, 0x95, 0x19, 0x47, 0x93, + 0xFC, 0x5B, 0x30, 0x4E, 0x5C, 0x77, 0x5C, 0x66, 0x15, 0x16, 0xB6, 0xD7, + 0x43, 0xC5, 0x83, 0x11, 0xED, 0x15, 0x90, 0x0F, 0xB8, 0x68, 0xDD, 0xF7, + 0xFA, 0x66, 0xD7, 0x3C, 0x19, 0xD6, 0xF1, 0xAA}; + +// /CN=ca +// SHA256 Fingerprint: A3:05:0C:44:CD:6D:1E:BE:A2:18:80:09:93:69:90:7F +// 8C:E3:9F:A4:33:CB:E3:E9:3C:D1:8E:8C:89:23:1B:4A + +// clang-format off +// Invocation: security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py -listname OverrideCaDNs -dn /tmp/overrideCa.pem +static const uint8_t CAcaDN[15] = { + 0x30, 0x0D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x02, + 0x63, 0x61, +}; +// clang-format on + +static const DataAndLength OverrideCaDNs[] = { + {CAcaDN, sizeof(CAcaDN)}, +}; + +// clang-format off +// Invocation: security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py -listname OverrideCaSPKIs -spki /tmp/overrideCa.pem +static const uint8_t CAcaSPKI[294] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, + 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0x88, 0x51, 0xA8, 0x44, 0x8E, + 0x16, 0xD6, 0x41, 0xFD, 0x6E, 0xB6, 0x88, 0x06, 0x36, 0x10, 0x3D, 0x3C, 0x13, + 0xD9, 0xEA, 0xE4, 0x35, 0x4A, 0xB4, 0xEC, 0xF5, 0x68, 0x57, 0x6C, 0x24, 0x7B, + 0xC1, 0xC7, 0x25, 0xA8, 0xE0, 0xD8, 0x1F, 0xBD, 0xB1, 0x9C, 0x06, 0x9B, 0x6E, + 0x1A, 0x86, 0xF2, 0x6B, 0xE2, 0xAF, 0x5A, 0x75, 0x6B, 0x6A, 0x64, 0x71, 0x08, + 0x7A, 0xA5, 0x5A, 0xA7, 0x45, 0x87, 0xF7, 0x1C, 0xD5, 0x24, 0x9C, 0x02, 0x7E, + 0xCD, 0x43, 0xFC, 0x1E, 0x69, 0xD0, 0x38, 0x20, 0x29, 0x93, 0xAB, 0x20, 0xC3, + 0x49, 0xE4, 0xDB, 0xB9, 0x4C, 0xC2, 0x6B, 0x6C, 0x0E, 0xED, 0x15, 0x82, 0x0F, + 0xF1, 0x7E, 0xAD, 0x69, 0x1A, 0xB1, 0xD3, 0x02, 0x3A, 0x8B, 0x2A, 0x41, 0xEE, + 0xA7, 0x70, 0xE0, 0x0F, 0x0D, 0x8D, 0xFD, 0x66, 0x0B, 0x2B, 0xB0, 0x24, 0x92, + 0xA4, 0x7D, 0xB9, 0x88, 0x61, 0x79, 0x90, 0xB1, 0x57, 0x90, 0x3D, 0xD2, 0x3B, + 0xC5, 0xE0, 0xB8, 0x48, 0x1F, 0xA8, 0x37, 0xD3, 0x88, 0x43, 0xEF, 0x27, 0x16, + 0xD8, 0x55, 0xB7, 0x66, 0x5A, 0xAA, 0x7E, 0x02, 0x90, 0x2F, 0x3A, 0x7B, 0x10, + 0x80, 0x06, 0x24, 0xCC, 0x1C, 0x6C, 0x97, 0xAD, 0x96, 0x61, 0x5B, 0xB7, 0xE2, + 0x96, 0x12, 0xC0, 0x75, 0x31, 0xA3, 0x0C, 0x91, 0xDD, 0xB4, 0xCA, 0xF7, 0xFC, + 0xAD, 0x1D, 0x25, 0xD3, 0x09, 0xEF, 0xB9, 0x17, 0x0E, 0xA7, 0x68, 0xE1, 0xB3, + 0x7B, 0x2F, 0x22, 0x6F, 0x69, 0xE3, 0xB4, 0x8A, 0x95, 0x61, 0x1D, 0xEE, 0x26, + 0xD6, 0x25, 0x9D, 0xAB, 0x91, 0x08, 0x4E, 0x36, 0xCB, 0x1C, 0x24, 0x04, 0x2C, + 0xBF, 0x16, 0x8B, 0x2F, 0xE5, 0xF1, 0x8F, 0x99, 0x17, 0x31, 0xB8, 0xB3, 0xFE, + 0x49, 0x23, 0xFA, 0x72, 0x51, 0xC4, 0x31, 0xD5, 0x03, 0xAC, 0xDA, 0x18, 0x0A, + 0x35, 0xED, 0x8D, 0x02, 0x03, 0x01, 0x00, 0x01, +}; +// clang-format on + +static const DataAndLength OverrideCaSPKIs[] = { + {CAcaSPKI, sizeof(CAcaSPKI)}, +}; + +class psm_TrustOverrideTest : public ::testing::Test { + protected: + void SetUp() override { + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + ASSERT_TRUE(prefs != nullptr) + << "couldn't get nsIPrefBranch"; + + // When PSM initializes, it attempts to get some localized strings. + // As a result, Android flips out if this isn't set. + nsresult rv = prefs->SetBoolPref("intl.locale.matchOS", true); + ASSERT_TRUE(NS_SUCCEEDED(rv)) + << "couldn't set pref 'intl.locale.matchOS'"; + + nsCOMPtr<nsIX509CertDB> certdb(do_GetService(NS_X509CERTDB_CONTRACTID)); + ASSERT_TRUE(certdb != nullptr) + << "couldn't get certdb"; + } +}; + +TEST_F(psm_TrustOverrideTest, CheckCertDNIsInList) { + nsTArray<uint8_t> caArray(kOverrideCaDer, sizeof(kOverrideCaDer)); + nsTArray<uint8_t> intermediateArray(kOverrideCaIntermediateDer, + sizeof(kOverrideCaIntermediateDer)); + + EXPECT_TRUE(CertDNIsInList(caArray, OverrideCaDNs)) + << "CA should be in the DN list"; + EXPECT_FALSE(CertDNIsInList(intermediateArray, OverrideCaDNs)) + << "Int should not be in the DN list"; +} + +TEST_F(psm_TrustOverrideTest, CheckCertSPKIIsInList) { + mozilla::pkix::Input caInput; + mozilla::pkix::Result rv = + caInput.Init(kOverrideCaDer, sizeof(kOverrideCaDer)); + ASSERT_TRUE(rv == Success); + + mozilla::pkix::Input intermediateInput; + rv = intermediateInput.Init(kOverrideCaIntermediateDer, + sizeof(kOverrideCaIntermediateDer)); + ASSERT_TRUE(rv == Success); + + EXPECT_TRUE(CertSPKIIsInList(caInput, OverrideCaSPKIs)) + << "CA should be in the SPKI list"; + EXPECT_FALSE(CertSPKIIsInList(intermediateInput, OverrideCaSPKIs)) + << "Int should not be in the SPKI list"; +} diff --git a/security/certverifier/tests/gtest/moz.build b/security/certverifier/tests/gtest/moz.build new file mode 100644 index 0000000000..e67d01b046 --- /dev/null +++ b/security/certverifier/tests/gtest/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + "TrustOverrideTest.cpp", +] + +LOCAL_INCLUDES += [ + "/security/certverifier", + "/security/manager/ssl", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" |