summaryrefslogtreecommitdiffstats
path: root/security/certverifier
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /security/certverifier
parentInitial commit. (diff)
downloadthunderbird-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.h33
-rw-r--r--security/certverifier/CertVerifier.cpp943
-rw-r--r--security/certverifier/CertVerifier.h248
-rw-r--r--security/certverifier/ExtendedValidation.cpp1393
-rw-r--r--security/certverifier/ExtendedValidation.h43
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.cpp2029
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.h329
-rw-r--r--security/certverifier/OCSPCache.cpp352
-rw-r--r--security/certverifier/OCSPCache.h136
-rw-r--r--security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc307
-rw-r--r--security/certverifier/TrustOverride-SymantecData.inc164
-rw-r--r--security/certverifier/TrustOverrideUtils.h149
-rw-r--r--security/certverifier/moz.build54
-rw-r--r--security/certverifier/tests/gtest/TrustOverrideTest.cpp231
-rw-r--r--security/certverifier/tests/gtest/moz.build18
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, &notBeforeSeconds);
+ if (rv != Success) {
+ return rv;
+ }
+ uint64_t notAfterSeconds;
+ rv = SecondsSinceEpochFromTime(certNotAfter, &notAfterSeconds);
+ 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(), &notBefore, &notAfter);
+ 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"