summaryrefslogtreecommitdiffstats
path: root/security/certverifier
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /security/certverifier
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/certverifier')
-rw-r--r--security/certverifier/BRNameMatchingPolicy.cpp42
-rw-r--r--security/certverifier/BRNameMatchingPolicy.h57
-rw-r--r--security/certverifier/CertVerifier.cpp1047
-rw-r--r--security/certverifier/CertVerifier.h295
-rw-r--r--security/certverifier/ExtendedValidation.cpp1215
-rw-r--r--security/certverifier/ExtendedValidation.h47
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.cpp1848
-rw-r--r--security/certverifier/NSSCertDBTrustDomain.h281
-rw-r--r--security/certverifier/OCSPCache.cpp324
-rw-r--r--security/certverifier/OCSPCache.h136
-rw-r--r--security/certverifier/OCSPVerificationTrustDomain.cpp105
-rw-r--r--security/certverifier/OCSPVerificationTrustDomain.h90
-rw-r--r--security/certverifier/TrustOverride-AppleGoogleDigiCertData.inc307
-rw-r--r--security/certverifier/TrustOverride-StartComAndWoSignData.inc84
-rw-r--r--security/certverifier/TrustOverride-SymantecData.inc209
-rw-r--r--security/certverifier/TrustOverride-TestImminentDistrustData.inc21
-rw-r--r--security/certverifier/TrustOverrideUtils.h97
-rw-r--r--security/certverifier/moz.build63
-rw-r--r--security/certverifier/tests/gtest/TrustOverrideTest.cpp171
-rw-r--r--security/certverifier/tests/gtest/moz.build18
20 files changed, 6457 insertions, 0 deletions
diff --git a/security/certverifier/BRNameMatchingPolicy.cpp b/security/certverifier/BRNameMatchingPolicy.cpp
new file mode 100644
index 0000000000..c70801adcd
--- /dev/null
+++ b/security/certverifier/BRNameMatchingPolicy.cpp
@@ -0,0 +1,42 @@
+/* -*- 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 "BRNameMatchingPolicy.h"
+
+#include "mozilla/Assertions.h"
+
+using namespace mozilla::psm;
+using namespace mozilla::pkix;
+
+Result BRNameMatchingPolicy::FallBackToCommonName(
+ Time notBefore,
+ /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) {
+ // (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 (mMode) {
+ case Mode::Enforce:
+ fallBackToCommonName = FallBackToSearchWithinSubject::No;
+ break;
+ case Mode::EnforceAfter23August2015:
+ fallBackToCommonName = notBefore > AUGUST_23_2015
+ ? FallBackToSearchWithinSubject::No
+ : FallBackToSearchWithinSubject::Yes;
+ break;
+ case Mode::EnforceAfter23August2016:
+ fallBackToCommonName = notBefore > AUGUST_23_2016
+ ? FallBackToSearchWithinSubject::No
+ : FallBackToSearchWithinSubject::Yes;
+ break;
+ case Mode::DoNotEnforce:
+ fallBackToCommonName = FallBackToSearchWithinSubject::Yes;
+ break;
+ default:
+ MOZ_CRASH("Unexpected Mode");
+ }
+ return Success;
+}
diff --git a/security/certverifier/BRNameMatchingPolicy.h b/security/certverifier/BRNameMatchingPolicy.h
new file mode 100644
index 0000000000..1f9910cee2
--- /dev/null
+++ b/security/certverifier/BRNameMatchingPolicy.h
@@ -0,0 +1,57 @@
+/* -*- 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 BRNameMatchingPolicy_h
+#define BRNameMatchingPolicy_h
+
+#include "mozpkix/pkixtypes.h"
+
+namespace mozilla {
+namespace psm {
+
+// According to the Baseline Requirements version 1.3.3 section 7.1.4.2.2.a,
+// the requirements of the subject common name field are as follows:
+// "If present, this field MUST contain a single IP address or Fully‐Qualified
+// Domain Name that is one of the values contained in the Certificate’s
+// subjectAltName extension". Consequently, since any name information present
+// in the common name must be present in the subject alternative name extension,
+// when performing name matching, it should not be necessary to fall back to the
+// common name. Because this consequence has not commonly been enforced, this
+// implementation provides a mechanism to start enforcing it gradually while
+// maintaining some backwards compatibility. If configured with the mode
+// "EnforceAfter23August2016", name matching will only fall back to using the
+// subject common name for certificates where the notBefore field is before 23
+// August 2016. Similarly, the mode "EnforceAfter23August2015" is also
+// available. This is to provide a balance between allowing preexisting
+// long-lived certificates and detecting newly-issued problematic certificates.
+// Note that this implementation does not actually directly enforce that if the
+// subject common name is present, its value corresponds to a dNSName or
+// iPAddress entry in the subject alternative name extension.
+
+class BRNameMatchingPolicy : public mozilla::pkix::NameMatchingPolicy {
+ public:
+ enum class Mode {
+ DoNotEnforce = 0,
+ EnforceAfter23August2016 = 1,
+ EnforceAfter23August2015 = 2,
+ Enforce = 3,
+ };
+
+ explicit BRNameMatchingPolicy(Mode mode) : mMode(mode) {}
+
+ virtual mozilla::pkix::Result FallBackToCommonName(
+ mozilla::pkix::Time notBefore,
+ /*out*/ mozilla::pkix::FallBackToSearchWithinSubject&
+ fallBacktoCommonName) override;
+
+ private:
+ Mode mMode;
+};
+
+} // namespace psm
+} // namespace mozilla
+
+#endif // BRNameMatchingPolicy_h
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
new file mode 100644
index 0000000000..9a486e4426
--- /dev/null
+++ b/security/certverifier/CertVerifier.cpp
@@ -0,0 +1,1047 @@
+/* -*- 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 "CTDiversityPolicy.h"
+#include "CTKnownLogs.h"
+#include "CTLogVerifier.h"
+#include "CSTrustDomain.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 "nsPromiseFlatString.h"
+#include "nsServiceManagerUtils.h"
+#include "pk11pub.h"
+#include "mozpkix/pkix.h"
+#include "mozpkix/pkixnss.h"
+#include "mozpkix/pkixutil.h"
+#include "secmod.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(PRTime certNotBefore,
+ PRTime certNotAfter,
+ size_t& months) {
+ if (certNotBefore >= certNotAfter) {
+ MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter");
+ return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ PRExplodedTime explodedNotBefore;
+ PRExplodedTime explodedNotAfter;
+
+ PR_ExplodeTime(certNotBefore, PR_LocalTimeParameters, &explodedNotBefore);
+ PR_ExplodeTime(certNotAfter, 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,
+ PinningMode pinningMode, SHA1Mode sha1Mode,
+ BRNameMatchingPolicy::Mode nameMatchingMode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ CertificateTransparencyMode ctMode,
+ CRLiteMode crliteMode,
+ uint64_t crliteCTMergeDelaySeconds,
+ const Vector<EnterpriseCert>& thirdPartyCerts)
+ : mOCSPDownloadConfig(odc),
+ mOCSPStrict(osc == ocspStrict),
+ mOCSPTimeoutSoft(ocspTimeoutSoft),
+ mOCSPTimeoutHard(ocspTimeoutHard),
+ mCertShortLifetimeInDays(certShortLifetimeInDays),
+ mPinningMode(pinningMode),
+ mSHA1Mode(sha1Mode),
+ mNameMatchingMode(nameMatchingMode),
+ mNetscapeStepUpPolicy(netscapeStepUpPolicy),
+ mCTMode(ctMode),
+ mCRLiteMode(crliteMode),
+ mCRLiteCTMergeDelaySeconds(crliteCTMergeDelaySeconds) {
+ 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 IsCertChainRootBuiltInRoot(const UniqueCERTCertList& chain,
+ bool& result) {
+ if (!chain || CERT_LIST_EMPTY(chain)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
+ if (!rootNode) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertificate* root = rootNode->cert;
+ if (!root) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ return IsCertBuiltInRoot(root, result);
+}
+
+Result IsDelegatedCredentialAcceptable(const DelegatedCredentialInfo& dcInfo,
+ SECOidTag evOidPolicyTag) {
+ 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(CERTCertificate* cert, bool& result) {
+ if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ result = false;
+#ifdef DEBUG
+ nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
+ if (!component) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ nsresult rv = component->IsCertTestBuiltInRoot(cert, &result);
+ if (NS_FAILED(rv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (result) {
+ return Success;
+ }
+#endif // DEBUG
+ 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)) {
+ CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, cert, nullptr);
+ if (handle != CK_INVALID_HANDLE &&
+ 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 UniqueCERTCertList& 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 || CERT_LIST_EMPTY(builtChain)) {
+ 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())));
+ }
+
+ CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
+ if (!endEntityNode || CERT_LIST_END(endEntityNode, builtChain)) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
+ if (!issuerNode || CERT_LIST_END(issuerNode, builtChain)) {
+ // 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;
+ }
+
+ CERTCertificate* endEntity = endEntityNode->cert;
+ CERTCertificate* issuer = issuerNode->cert;
+ if (!endEntity || !issuer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ if (endEntity->subjectName) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Verifying CT Policy compliance of subject %s\n",
+ endEntity->subjectName));
+ }
+
+ Input endEntityDER;
+ Result rv =
+ endEntityDER.Init(endEntity->derCert.data, endEntity->derCert.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Input issuerPublicKeyDER;
+ rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
+ issuer->derPublicKey.len);
+ if (rv != Success) {
+ return rv;
+ }
+
+ CTVerifyResult result;
+ rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER, 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));
+ }
+
+ PRTime notBefore;
+ PRTime notAfter;
+ if (CERT_GetCertTimes(endEntity, &notBefore, &notAfter) != SECSuccess) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ 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.get(), 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;
+}
+
+bool CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode) {
+ switch (mSHA1Mode) {
+ case SHA1Mode::Forbidden:
+ return mode != SHA1Mode::Forbidden;
+ case SHA1Mode::ImportedRoot:
+ return mode != SHA1Mode::Forbidden && mode != SHA1Mode::ImportedRoot;
+ case SHA1Mode::ImportedRootOrBefore2016:
+ return mode == SHA1Mode::Allowed;
+ case SHA1Mode::Allowed:
+ return false;
+ // MSVC warns unless we explicitly handle this now-unused option.
+ case SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
+ default:
+ MOZ_ASSERT(false, "unexpected SHA1Mode type");
+ return true;
+ }
+}
+
+Result CertVerifier::VerifyCert(
+ CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg,
+ const char* hostname,
+ /*out*/ UniqueCERTCertList& 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*/ SECOidTag* evOidPolicy,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
+ /*optional out*/ KeySizeStatus* keySizeStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo,
+ /*optional out*/ CRLiteLookupResult* crliteLookupResult) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
+
+ MOZ_ASSERT(cert);
+ MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
+ MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
+ MOZ_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
+
+ if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (NS_FAILED(CheckForSmartCardChanges())) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ if (evOidPolicy) {
+ *evOidPolicy = SEC_OID_UNKNOWN;
+ }
+ 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 (sha1ModeResult) {
+ if (usage != certificateUsageSSLServer) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ *sha1ModeResult = SHA1ModeResult::NeverChecked;
+ }
+
+ if (!cert ||
+ (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ Input certDER;
+ Result rv = certDER.Init(cert->derCert.data, cert->derCert.len);
+ 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, pinningDisabled,
+ MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch, mCRLiteMode,
+ mCRLiteCTMergeDelaySeconds, originAttributes, mThirdPartyRootInputs,
+ mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(
+ trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
+ KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ 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.
+
+ // These configurations are in order of most restrictive to least
+ // restrictive. This enables us to gather telemetry on the expected
+ // results of setting the default policy to a particular configuration.
+ SHA1Mode sha1ModeConfigurations[] = {
+ SHA1Mode::Forbidden,
+ SHA1Mode::ImportedRoot,
+ SHA1Mode::ImportedRootOrBefore2016,
+ SHA1Mode::Allowed,
+ };
+
+ SHA1ModeResult sha1ModeResults[] = {
+ SHA1ModeResult::SucceededWithoutSHA1,
+ SHA1ModeResult::SucceededWithImportedRoot,
+ SHA1ModeResult::SucceededWithImportedRootOrSHA1Before2016,
+ SHA1ModeResult::SucceededWithSHA1,
+ };
+
+ size_t sha1ModeConfigurationsCount =
+ MOZ_ARRAY_LENGTH(sha1ModeConfigurations);
+
+ static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) ==
+ MOZ_ARRAY_LENGTH(sha1ModeResults),
+ "digestAlgorithm array lengths differ");
+
+ rv = Result::ERROR_UNKNOWN_ERROR;
+
+ // Try to validate for EV first.
+ NSSCertDBTrustDomain::OCSPFetching evOCSPFetching =
+ (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY)
+ ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
+ : NSSCertDBTrustDomain::FetchOCSPForEV;
+
+ CertPolicyId evPolicy;
+ SECOidTag evPolicyOidTag;
+ bool foundEVPolicy = GetFirstEVPolicy(*cert, evPolicy, evPolicyOidTag);
+ for (size_t i = 0;
+ i < sha1ModeConfigurationsCount && rv != Success && foundEVPolicy;
+ i++) {
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) {
+ continue;
+ }
+
+ // Because of the try-strict and fallback approach, we have to clear any
+ // previously noted telemetry information.
+ if (pinningTelemetryInfo) {
+ pinningTelemetryInfo->Reset();
+ }
+ if (crliteLookupResult) {
+ *crliteLookupResult = CRLiteLookupResult::NeverChecked;
+ }
+
+ NSSCertDBTrustDomain trustDomain(
+ trustSSL, evOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
+ mOCSPTimeoutHard, mCertShortLifetimeInDays, mPinningMode,
+ MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
+ sha1ModeConfigurations[i], mNetscapeStepUpPolicy, mCRLiteMode,
+ mCRLiteCTMergeDelaySeconds, originAttributes, mThirdPartyRootInputs,
+ mThirdPartyIntermediateInputs, extraCertificates, builtChain,
+ pinningTelemetryInfo, crliteLookupResult, 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 (rv == Success &&
+ sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
+ if (rv == Success) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("cert is EV with status %i\n",
+ static_cast<int>(sha1ModeResults[i])));
+ if (evOidPolicy) {
+ *evOidPolicy = evPolicyOidTag;
+ }
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[i];
+ }
+ rv = VerifyCertificateTransparencyPolicy(
+ trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
+ if (rv != Success) {
+ 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++) {
+ for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success;
+ j++) {
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) {
+ continue;
+ }
+
+ // invalidate any telemetry info relating to failed chains
+ if (pinningTelemetryInfo) {
+ pinningTelemetryInfo->Reset();
+ }
+ if (crliteLookupResult) {
+ *crliteLookupResult = CRLiteLookupResult::NeverChecked;
+ }
+
+ NSSCertDBTrustDomain trustDomain(
+ trustSSL, defaultOCSPFetching, mOCSPCache, pinArg,
+ mOCSPTimeoutSoft, mOCSPTimeoutHard, mCertShortLifetimeInDays,
+ mPinningMode, keySizeOptions[i],
+ ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j],
+ mNetscapeStepUpPolicy, mCRLiteMode, mCRLiteCTMergeDelaySeconds,
+ originAttributes, mThirdPartyRootInputs,
+ mThirdPartyIntermediateInputs, extraCertificates, builtChain,
+ pinningTelemetryInfo, crliteLookupResult, 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 (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 &&
+ sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
+ if (rv == Success) {
+ if (keySizeStatus) {
+ *keySizeStatus = keySizeStatuses[i];
+ }
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[j];
+ }
+ rv = VerifyCertificateTransparencyPolicy(
+ trustDomain, builtChain, sctsFromTLSInput, time, ctInfo);
+ if (rv != Success) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (rv == Success) {
+ break;
+ }
+
+ if (keySizeStatus) {
+ *keySizeStatus = KeySizeStatus::AlreadyBad;
+ }
+ // The telemetry probe CERT_CHAIN_SHA1_POLICY_STATUS gives us feedback on
+ // the result of setting a specific policy. However, we don't want noise
+ // from users who have manually set the policy to something other than the
+ // default, so we only collect for ImportedRoot (which is the default).
+ if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) {
+ *sha1ModeResult = SHA1ModeResult::Failed;
+ }
+
+ break;
+ }
+
+ case certificateUsageSSLCA: {
+ NSSCertDBTrustDomain trustDomain(
+ trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
+ mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
+ MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed, mNetscapeStepUpPolicy, mCRLiteMode,
+ mCRLiteCTMergeDelaySeconds, originAttributes, mThirdPartyRootInputs,
+ mThirdPartyIntermediateInputs, extraCertificates, builtChain, nullptr,
+ nullptr);
+ rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
+ KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
+ CertPolicyId::anyPolicy, stapledOCSPResponse);
+ break;
+ }
+
+ case certificateUsageEmailSigner: {
+ NSSCertDBTrustDomain trustDomain(
+ trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, mOCSPTimeoutSoft,
+ mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
+ MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch, mCRLiteMode,
+ mCRLiteCTMergeDelaySeconds, 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);
+ }
+ 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, pinningDisabled,
+ MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
+ SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch, mCRLiteMode,
+ mCRLiteCTMergeDelaySeconds, 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);
+ }
+ break;
+ }
+
+ default:
+ rv = Result::FATAL_ERROR_INVALID_ARGS;
+ }
+
+ if (rv != Success) {
+ return rv;
+ }
+
+ return Success;
+}
+
+static bool CertIsSelfSigned(const UniqueCERTCertificate& cert, void* pinarg) {
+ Input certInput;
+ Result rv = certInput.Init(cert->derCert.data, cert->derCert.len);
+ 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 backCert(certInput, notUsedForPaths, nullptr);
+ rv = backCert.Init();
+ if (rv != Success) {
+ return false;
+ }
+ if (!InputsAreEqual(backCert.GetIssuer(), backCert.GetSubject())) {
+ return false;
+ }
+
+ nsTArray<nsTArray<uint8_t>> emptyCertList;
+ // CSTrustDomain is only used for the signature verification callbacks
+ mozilla::psm::CSTrustDomain trustDomain(emptyCertList);
+ rv = VerifySignedData(trustDomain, backCert.GetSignedData(),
+ backCert.GetSubjectPublicKeyInfo());
+ return rv == Success;
+}
+
+Result CertVerifier::VerifySSLServerCert(
+ const UniqueCERTCertificate& peerCert, Time time,
+ /*optional*/ void* pinarg, const nsACString& hostname,
+ /*out*/ UniqueCERTCertList& 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*/ bool saveIntermediatesInPermanentDatabase,
+ /*optional out*/ SECOidTag* evOidPolicy,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
+ /*optional out*/ KeySizeStatus* keySizeStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo,
+ /*optional out*/ CRLiteLookupResult* crliteLookupResult,
+ /*optional out*/ bool* isBuiltCertChainRootBuiltInRoot) {
+ MOZ_ASSERT(peerCert);
+ // XXX: MOZ_ASSERT(pinarg);
+ MOZ_ASSERT(!hostname.IsEmpty());
+
+ SECOidTag evPolicyOidTag = SEC_OID_UNKNOWN;
+
+ if (isBuiltCertChainRootBuiltInRoot) {
+ *isBuiltCertChainRootBuiltInRoot = false;
+ }
+
+ if (evOidPolicy) {
+ *evOidPolicy = evPolicyOidTag;
+ }
+
+ if (hostname.IsEmpty()) {
+ return Result::ERROR_BAD_CERT_DOMAIN;
+ }
+
+ // CreateCertErrorRunnable assumes that CheckCertHostname is only called
+ // if VerifyCert succeeded.
+ Result rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
+ pinarg, PromiseFlatCString(hostname).get(), builtChain,
+ flags, extraCertificates, stapledOCSPResponse,
+ sctsFromTLS, originAttributes, &evPolicyOidTag,
+ ocspStaplingStatus, keySizeStatus, sha1ModeResult,
+ pinningTelemetryInfo, ctInfo, crliteLookupResult);
+ if (rv != Success) {
+ if (rv == Result::ERROR_UNKNOWN_ISSUER &&
+ CertIsSelfSigned(peerCert, pinarg)) {
+ // In this case we didn't find any issuer for the certificate 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.
+ nsresult rv = component->IssuerMatchesMitmCanary(peerCert->issuerName);
+ if (NS_SUCCEEDED(rv)) {
+ return Result::ERROR_MITM_DETECTED;
+ }
+ }
+ return rv;
+ }
+
+ if (dcInfo) {
+ rv = IsDelegatedCredentialAcceptable(*dcInfo, evPolicyOidTag);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ Input peerCertInput;
+ rv = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len);
+ 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;
+ }
+ }
+
+ Input hostnameInput;
+ rv = hostnameInput.Init(
+ BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
+ hostname.Length());
+ if (rv != Success) {
+ return Result::FATAL_ERROR_INVALID_ARGS;
+ }
+ bool isBuiltInRoot;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (isBuiltCertChainRootBuiltInRoot) {
+ *isBuiltCertChainRootBuiltInRoot = isBuiltInRoot;
+ }
+
+ BRNameMatchingPolicy nameMatchingPolicy(
+ isBuiltInRoot ? mNameMatchingMode
+ : BRNameMatchingPolicy::Mode::DoNotEnforce);
+ rv = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy);
+ if (rv != Success) {
+ // Treat malformed name information as a domain mismatch.
+ if (rv == Result::ERROR_BAD_DER) {
+ return Result::ERROR_BAD_CERT_DOMAIN;
+ }
+
+ return rv;
+ }
+
+ if (saveIntermediatesInPermanentDatabase) {
+ SaveIntermediateCerts(builtChain);
+ }
+
+ if (evOidPolicy) {
+ *evOidPolicy = evPolicyOidTag;
+ }
+
+ return Success;
+}
+
+} // namespace psm
+} // namespace mozilla
diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h
new file mode 100644
index 0000000000..3a72c13e1c
--- /dev/null
+++ b/security/certverifier/CertVerifier.h
@@ -0,0 +1,295 @@
+/* -*- 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 "BRNameMatchingPolicy.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;
+
+// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry.
+enum class KeySizeStatus {
+ NeverChecked = 0,
+ LargeMinimumSucceeded = 1,
+ CompatibilityRisk = 2,
+ AlreadyBad = 3,
+};
+
+// These values correspond to the CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
+enum class SHA1ModeResult {
+ NeverChecked = 0,
+ SucceededWithoutSHA1 = 1,
+ SucceededWithImportedRoot = 2,
+ SucceededWithImportedRootOrSHA1Before2016 = 3,
+ SucceededWithSHA1 = 4,
+ Failed = 5,
+};
+
+enum class CRLiteMode {
+ Disabled = 0,
+ TelemetryOnly = 1,
+ Enforce = 2,
+};
+
+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;
+};
+
+enum class CRLiteLookupResult {
+ NeverChecked = 0,
+ FilterNotAvailable = 1,
+ IssuerNotEnrolled = 2,
+ CertificateTooNew = 3,
+ CertificateValid = 4,
+ CertificateRevoked = 5,
+ LibraryFailure = 6,
+ CertRevokedByStash = 7,
+};
+
+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(
+ CERTCertificate* cert, SECCertificateUsage usage,
+ mozilla::pkix::Time time, void* pinArg, const char* hostname,
+ /*out*/ UniqueCERTCertList& 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*/ SECOidTag* evOidPolicy = nullptr,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
+ /*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr,
+ /*optional out*/ CRLiteLookupResult* crliteLookupResult = nullptr);
+
+ mozilla::pkix::Result VerifySSLServerCert(
+ const UniqueCERTCertificate& peerCert, mozilla::pkix::Time time,
+ void* pinarg, const nsACString& hostname,
+ /*out*/ UniqueCERTCertList& 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*/ bool saveIntermediatesInPermanentDatabase = false,
+ /*optional out*/ SECOidTag* evOidPolicy = nullptr,
+ /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
+ /*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
+ /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr,
+ /*optional out*/ CRLiteLookupResult* crliteLookupResult = nullptr,
+ /*optional out*/ bool* isBuiltCertChainRootBuiltInRoot = nullptr);
+
+ enum PinningMode {
+ pinningDisabled = 0,
+ pinningAllowUserCAMITM = 1,
+ pinningStrict = 2,
+ pinningEnforceTestMode = 3
+ };
+
+ enum class SHA1Mode {
+ Allowed = 0,
+ Forbidden = 1,
+ // There used to be a policy that only allowed SHA1 for certificates issued
+ // before 2016. This is no longer available. If a user has selected this
+ // policy in about:config, it now maps to Forbidden.
+ UsedToBeBefore2016ButNowIsForbidden = 2,
+ ImportedRoot = 3,
+ ImportedRootOrBefore2016 = 4,
+ };
+
+ 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, PinningMode pinningMode,
+ SHA1Mode sha1Mode, BRNameMatchingPolicy::Mode nameMatchingMode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy,
+ CertificateTransparencyMode ctMode, CRLiteMode crliteMode,
+ uint64_t crliteCTMergeDelaySeconds,
+ 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 PinningMode mPinningMode;
+ const SHA1Mode mSHA1Mode;
+ const BRNameMatchingPolicy::Mode mNameMatchingMode;
+ const NetscapeStepUpPolicy mNetscapeStepUpPolicy;
+ const CertificateTransparencyMode mCTMode;
+ const CRLiteMode mCRLiteMode;
+ const uint64_t mCRLiteCTMergeDelaySeconds;
+
+ 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 UniqueCERTCertList& builtChain,
+ mozilla::pkix::Input sctsFromTLS, mozilla::pkix::Time time,
+ /*optional out*/ CertificateTransparencyInfo* ctInfo);
+
+ // Returns true if the configured SHA1 mode is more restrictive than the given
+ // mode. SHA1Mode::Forbidden is more restrictive than any other mode except
+ // Forbidden. Next is ImportedRoot, then ImportedRootOrBefore2016, then
+ // Allowed. (A mode is never more restrictive than itself.)
+ bool SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode);
+};
+
+mozilla::pkix::Result IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
+mozilla::pkix::Result CertListContainsExpectedKeys(
+ const CERTCertList* certList, const char* hostname,
+ mozilla::pkix::Time time, CertVerifier::PinningMode pinningMode);
+
+} // 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..e931f20227
--- /dev/null
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -0,0 +1,1215 @@
+/* -*- 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 "nsDependentString.h"
+#include "nsString.h"
+#include "pk11pub.h"
+#include "mozpkix/pkixtypes.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=Cybertrust Global Root,O=Cybertrust, Inc
+ "1.3.6.1.4.1.6334.1.100.1",
+ "Cybertrust EV OID",
+ { 0x96, 0x0A, 0xDF, 0x00, 0x63, 0xE9, 0x63, 0x56, 0x75, 0x0C, 0x29,
+ 0x65, 0xDD, 0x0A, 0x08, 0x67, 0xDA, 0x0B, 0x9C, 0xBD, 0x6E, 0x77,
+ 0x71, 0x4A, 0xEA, 0xFB, 0x23, 0x49, 0xAB, 0x39, 0x3D, 0xA3 },
+ "MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVz"
+ "dCBHbG9iYWwgUm9vdA==",
+ "BAAAAAABD4WqLUg=",
+ },
+ {
+ // 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=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
+ "1.3.6.1.4.1.782.1.2.1.8.1",
+ "Network Solutions EV OID",
+ { 0x15, 0xF0, 0xBA, 0x00, 0xA3, 0xAC, 0x7A, 0xF3, 0xAC, 0x88, 0x4C,
+ 0x07, 0x2B, 0x10, 0x11, 0xA0, 0x77, 0xBD, 0x77, 0xC0, 0x97, 0xF4,
+ 0x01, 0x64, 0xB2, 0xF8, 0x59, 0x8A, 0xBD, 0x83, 0x86, 0x0C },
+ "MGIxCzAJBgNVBAYTAlVTMSEwHwYDVQQKExhOZXR3b3JrIFNvbHV0aW9ucyBMLkwu"
+ "Qy4xMDAuBgNVBAMTJ05ldHdvcmsgU29sdXRpb25zIENlcnRpZmljYXRlIEF1dGhv"
+ "cml0eQ==",
+ "V8szb8JcFuZHFhfjkDFo4A==",
+ },
+ {
+ // 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=Chambers of Commerce Root - 2008,O=AC Camerfirma S.A.,serialNumber=A82743287,L=Madrid (see current address at www.camerfirma.com/address),C=EU
+ "1.3.6.1.4.1.17326.10.14.2.1.2",
+ "Camerfirma EV OID a",
+ { 0x06, 0x3E, 0x4A, 0xFA, 0xC4, 0x91, 0xDF, 0xD3, 0x32, 0xF3, 0x08,
+ 0x9B, 0x85, 0x42, 0xE9, 0x46, 0x17, 0xD8, 0x93, 0xD7, 0xFE, 0x94,
+ 0x4E, 0x10, 0xA7, 0x93, 0x7E, 0xE2, 0x9D, 0x96, 0x93, 0xC0 },
+ "MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBh"
+ "ZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ"
+ "QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMT"
+ "IENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4",
+ "AKPaQn6ksa7a",
+ },
+ {
+ // 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 Certification Authority, OU = E-Tugra Sertifikasyon Merkezi, O = E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş., L = Ankara, C = TR
+ "2.16.792.3.0.4.1.1.4",
+ "ETugra EV OID",
+ { 0xB0, 0xBF, 0xD5, 0x2B, 0xB0, 0xD7, 0xD9, 0xBD, 0x92, 0xBF, 0x5D,
+ 0x4D, 0xC1, 0x3D, 0xA2, 0x55, 0xC0, 0x2C, 0x54, 0x2F, 0x37, 0x83,
+ 0x65, 0xEA, 0x89, 0x39, 0x11, 0xF5, 0x5E, 0x55, 0xF2, 0x3C },
+ "MIGyMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMUAwPgYDVQQKDDdFLVR1"
+ "xJ9yYSBFQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEu"
+ "xZ4uMSYwJAYDVQQLDB1FLVR1Z3JhIFNlcnRpZmlrYXN5b24gTWVya2V6aTEoMCYG"
+ "A1UEAwwfRS1UdWdyYSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==",
+ "amg+nFGby1M=",
+ },
+ {
+ // 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=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=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
+ "2.16.528.1.1003.1.2.7",
+ "Staat der Nederlanden EV OID",
+ { 0x4D, 0x24, 0x91, 0x41, 0x4C, 0xFE, 0x95, 0x67, 0x46, 0xEC, 0x4C,
+ 0xEF, 0xA6, 0xCF, 0x6F, 0x72, 0xE2, 0x8A, 0x13, 0x29, 0x43, 0x2F,
+ 0x9D, 0x8A, 0x90, 0x7A, 0xC4, 0xCB, 0x5D, 0xAD, 0xC1, 0x5A },
+ "MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4x"
+ "KTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB",
+ "AJiWjQ==",
+ },
+ {
+ // 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",
+ },
+ // clang-format on
+};
+
+static SECOidTag sEVInfoOIDTags[ArrayLength(kEVInfos)];
+
+static_assert(SEC_OID_UNKNOWN == 0,
+ "We depend on zero-initialized globals being interpreted as "
+ "SEC_OID_UNKNOWN.");
+static_assert(
+ ArrayLength(sEVInfoOIDTags) == ArrayLength(kEVInfos),
+ "These arrays are used in parallel and must have the same length.");
+
+static SECOidTag RegisterOID(const SECItem& oidItem, const char* oidName) {
+ SECOidData od;
+ od.oid.len = oidItem.len;
+ od.oid.data = oidItem.data;
+ od.offset = SEC_OID_UNKNOWN;
+ od.desc = oidName;
+ od.mechanism = CKM_INVALID_MECHANISM;
+ od.supportedExtension = INVALID_CERT_EXTENSION;
+ return SECOID_AddEntry(&od);
+}
+
+static SECOidTag sCABForumEVOIDTag = SEC_OID_UNKNOWN;
+
+static bool isEVPolicy(SECOidTag policyOIDTag) {
+ if (policyOIDTag != SEC_OID_UNKNOWN && policyOIDTag == sCABForumEVOIDTag) {
+ return true;
+ }
+
+ for (const SECOidTag& oidTag : sEVInfoOIDTags) {
+ if (policyOIDTag == oidTag) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
+ const mozilla::pkix::CertPolicyId& policy) {
+ MOZ_ASSERT(cert);
+ if (!cert) {
+ return false;
+ }
+
+ unsigned char fingerprint[SHA256_LENGTH];
+ SECStatus srv = PK11_HashBuf(SEC_OID_SHA256, fingerprint, cert->derCert.data,
+ AssertedCast<int32_t>(cert->derCert.len));
+ if (srv != SECSuccess) {
+ return false;
+ }
+
+ const SECOidData* cabforumOIDData = SECOID_FindOIDByTag(sCABForumEVOIDTag);
+ 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, entry.sha256Fingerprint)) {
+ continue;
+ }
+
+ if (cabforumOIDData && cabforumOIDData->oid.len == policy.numBytes &&
+ ArrayEqual(cabforumOIDData->oid.data, policy.bytes, policy.numBytes)) {
+ return true;
+ }
+ const SECOidData* oidData = SECOID_FindOIDByTag(sEVInfoOIDTags[i]);
+ if (oidData && oidData->oid.len == policy.numBytes &&
+ ArrayEqual(oidData->oid.data, policy.bytes, policy.numBytes)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+nsresult LoadExtendedValidationInfo() {
+ static const char* sCABForumOIDString = "2.23.140.1.1";
+ static const char* sCABForumOIDDescription = "CA/Browser Forum EV OID";
+
+ ScopedAutoSECItem cabforumOIDItem;
+ if (SEC_StringToOID(nullptr, &cabforumOIDItem, sCABForumOIDString, 0) !=
+ SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ sCABForumEVOIDTag = RegisterOID(cabforumOIDItem, sCABForumOIDDescription);
+ if (sCABForumEVOIDTag == SEC_OID_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ 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;
+ }
+ sEVInfoOIDTags[i] = RegisterOID(evOIDItem, entry.oidName);
+ if (sEVInfoOIDTags[i] == SEC_OID_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+// Helper function for GetFirstEVPolicy(): returns the first suitable policy
+// from the given list of policies.
+bool GetFirstEVPolicyFromPolicyList(
+ const UniqueCERTCertificatePolicies& policies,
+ /*out*/ mozilla::pkix::CertPolicyId& policy,
+ /*out*/ SECOidTag& policyOidTag) {
+ for (size_t i = 0; policies->policyInfos[i]; i++) {
+ const CERTPolicyInfo* policyInfo = policies->policyInfos[i];
+ SECOidTag policyInfoOID = policyInfo->oid;
+ if (policyInfoOID == SEC_OID_UNKNOWN || !isEVPolicy(policyInfoOID)) {
+ continue;
+ }
+
+ const SECOidData* oidData = SECOID_FindOIDByTag(policyInfoOID);
+ MOZ_ASSERT(oidData);
+ MOZ_ASSERT(oidData->oid.data);
+ MOZ_ASSERT(oidData->oid.len > 0);
+ MOZ_ASSERT(oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES);
+ if (!oidData || !oidData->oid.data || oidData->oid.len == 0 ||
+ oidData->oid.len > mozilla::pkix::CertPolicyId::MAX_BYTES) {
+ continue;
+ }
+
+ policy.numBytes = AssertedCast<uint16_t>(oidData->oid.len);
+ PodCopy(policy.bytes, oidData->oid.data, policy.numBytes);
+ policyOidTag = policyInfoOID;
+ return true;
+ }
+
+ return false;
+}
+
+bool GetFirstEVPolicy(CERTCertificate& cert,
+ /*out*/ mozilla::pkix::CertPolicyId& policy,
+ /*out*/ SECOidTag& policyOidTag) {
+ if (!cert.extensions) {
+ return false;
+ }
+
+ for (size_t i = 0; cert.extensions[i]; i++) {
+ const CERTCertExtension* extension = cert.extensions[i];
+ if (SECOID_FindOIDTag(&extension->id) !=
+ SEC_OID_X509_CERTIFICATE_POLICIES) {
+ continue;
+ }
+
+ UniqueCERTCertificatePolicies policies(
+ CERT_DecodeCertificatePoliciesExtension(&extension->value));
+ if (!policies) {
+ continue;
+ }
+
+ if (GetFirstEVPolicyFromPolicyList(policies, policy, policyOidTag)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace psm
+} // namespace mozilla
diff --git a/security/certverifier/ExtendedValidation.h b/security/certverifier/ExtendedValidation.h
new file mode 100644
index 0000000000..42b9524f02
--- /dev/null
+++ b/security/certverifier/ExtendedValidation.h
@@ -0,0 +1,47 @@
+/* -*- 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 mozilla
+
+namespace mozilla {
+namespace psm {
+
+nsresult LoadExtendedValidationInfo();
+
+/**
+ * Finds the first policy OID in the given cert that is known to be an EV policy
+ * OID.
+ *
+ * @param cert
+ * The cert to find the first EV policy of.
+ * @param policy
+ * The found policy.
+ * @param policyOidTag
+ * The OID tag of the found policy.
+ * @return true if a suitable policy was found, false otherwise.
+ */
+bool GetFirstEVPolicy(CERTCertificate& cert,
+ /*out*/ mozilla::pkix::CertPolicyId& policy,
+ /*out*/ SECOidTag& policyOidTag);
+
+// CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted
+// or distrusted.
+bool CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& 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..d2833a0307
--- /dev/null
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -0,0 +1,1848 @@
+/* -*- 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 "ExtendedValidation.h"
+#include "MultiLogCTVerifier.h"
+#include "NSSErrorsService.h"
+#include "OCSPVerificationTrustDomain.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/PodOperations.h"
+#include "mozilla/Services.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 "nsNSSCertHelper.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSCertificateDB.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-StartComAndWoSignData.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,
+ CertVerifier::PinningMode pinningMode, unsigned int minRSABits,
+ ValidityCheckingMode validityCheckingMode, CertVerifier::SHA1Mode sha1Mode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode,
+ uint64_t crliteCTMergeDelaySeconds,
+ const OriginAttributes& originAttributes,
+ const Vector<Input>& thirdPartyRootInputs,
+ const Vector<Input>& thirdPartyIntermediateInputs,
+ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
+ /*out*/ UniqueCERTCertList& builtChain,
+ /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
+ /*optional*/ CRLiteLookupResult* crliteLookupResult,
+ /*optional*/ const char* hostname)
+ : mCertDBTrustType(certDBTrustType),
+ mOCSPFetching(ocspFetching),
+ mOCSPCache(ocspCache),
+ mPinArg(pinArg),
+ mOCSPTimeoutSoft(ocspTimeoutSoft),
+ mOCSPTimeoutHard(ocspTimeoutHard),
+ mCertShortLifetimeInDays(certShortLifetimeInDays),
+ mPinningMode(pinningMode),
+ mMinRSABits(minRSABits),
+ mValidityCheckingMode(validityCheckingMode),
+ mSHA1Mode(sha1Mode),
+ mNetscapeStepUpPolicy(netscapeStepUpPolicy),
+ mCRLiteMode(crliteMode),
+ mCRLiteCTMergeDelaySeconds(crliteCTMergeDelaySeconds),
+ mSawDistrustedCAByPolicyError(false),
+ mOriginAttributes(originAttributes),
+ mThirdPartyRootInputs(thirdPartyRootInputs),
+ mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
+ mExtraCertificates(extraCertificates),
+ mBuiltChain(builtChain),
+ mPinningTelemetryInfo(pinningTelemetryInfo),
+ mCRLiteLookupResult(crliteLookupResult),
+ mHostname(hostname),
+ mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
+ mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED),
+ mSCTListFromCertificate(),
+ mSCTListFromOCSPStapling(),
+ mBuiltInRootsModule(SECMOD_FindModule(kRootModuleName)) {}
+
+static Result FindRootsWithSubject(UniqueSECMODModule& rootsModule,
+ SECItem subject,
+ /*out*/ Vector<Vector<uint8_t>>& roots) {
+ MOZ_ASSERT(rootsModule);
+ for (int slotIndex = 0; slotIndex < rootsModule->slotCount; slotIndex++) {
+ CERTCertificateList* rawResults = nullptr;
+ if (PK11_FindRawCertsWithSubject(rootsModule->slots[slotIndex], &subject,
+ &rawResults) != SECSuccess) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ // rawResults == nullptr means we didn't find any matching certificates
+ if (!rawResults) {
+ continue;
+ }
+ UniqueCERTCertificateList results(rawResults);
+ for (int certIndex = 0; certIndex < results->len; certIndex++) {
+ Vector<uint8_t> root;
+ if (!root.append(results->certs[certIndex].data,
+ results->certs[certIndex].len)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ if (!roots.append(std::move(root))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+ }
+ return Success;
+}
+
+// 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;
+ }
+ uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
+ pkix::der::PublicKeyAlgorithm publicKeyAlg;
+ SignedDigest signature;
+ if (DigestSignedData(trustDomain, cert.GetSignedData(), digestBuf,
+ publicKeyAlg, signature) != Success) {
+ return false;
+ }
+ if (VerifySignedDigest(trustDomain, publicKeyAlg, signature,
+ 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,
+ Vector<Input>& candidates,
+ Input* nameConstraintsInputPtr, bool& keepGoing) {
+ for (Input candidate : candidates) {
+ 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.
+ Vector<Input> geckoRootCandidates;
+ Vector<Input> geckoIntermediateCandidates;
+
+ if (!mCertStorage) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ nsTArray<uint8_t> subject;
+ // XXX(Bug 1631371) Check if this should use a fallible operation as it
+ // pretended earlier.
+ 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.
+ if (!geckoIntermediateCandidates.append(certDER)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+
+ // We might not have this module if e.g. we're on a Linux distribution that
+ // does something unexpected.
+ Vector<Vector<uint8_t>> builtInRoots;
+ if (mBuiltInRootsModule) {
+ Result rv = FindRootsWithSubject(mBuiltInRootsModule, encodedIssuerNameItem,
+ builtInRoots);
+ if (rv != Success) {
+ return rv;
+ }
+ for (const auto& root : builtInRoots) {
+ Input rootInput;
+ rv = rootInput.Init(root.begin(), root.length());
+ if (rv != Success) {
+ continue; // probably too big
+ }
+ if (!geckoRootCandidates.append(rootInput)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+ } 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;
+ }
+ if (!geckoRootCandidates.append(thirdPartyRootInput)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+
+ 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;
+ }
+ if (!geckoIntermediateCandidates.append(thirdPartyIntermediateInput)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+
+ 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.
+ if (!geckoIntermediateCandidates.append(certInput)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+ }
+
+ // Try all root certs first and then all (presumably) intermediates.
+ if (!geckoRootCandidates.appendAll(std::move(geckoIntermediateCandidates))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ bool keepGoing = true;
+ Result result = CheckCandidates(*this, checker, geckoRootCandidates,
+ nameConstraintsInputPtr, keepGoing);
+ if (result != Success) {
+ return result;
+ }
+ if (!keepGoing) {
+ return Success;
+ }
+
+ // 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));
+ Vector<Input> nssRootCandidates;
+ Vector<Input> nssIntermediateCandidates;
+ if (candidates) {
+ for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
+ !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
+ Input certDER;
+ Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
+ if (rv != Success) {
+ continue; // probably too big
+ }
+ if (n->cert->isRoot) {
+ if (!nssRootCandidates.append(certDER)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ } else {
+ if (!nssIntermediateCandidates.append(certDER)) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+ }
+ }
+ if (!nssRootCandidates.appendAll(std::move(nssIntermediateCandidates))) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ return CheckCandidates(*this, checker, nssRootCandidates,
+ 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"));
+ 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;
+ }
+ }
+
+ // XXX: 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) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ // XXX: 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;
+ return Success;
+ }
+
+ // For TRUST, we use the CERTDB_TRUSTED_CA bit.
+ if (flags & CERTDB_TRUSTED_CA) {
+ if (policy.IsAnyPolicy()) {
+ trustLevel = TrustLevel::TrustAnchor;
+ return Success;
+ }
+ if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
+ trustLevel = TrustLevel::TrustAnchor;
+ return Success;
+ }
+ }
+ }
+
+ trustLevel = TrustLevel::InheritsTrust;
+ return Success;
+}
+
+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;
+}
+
+Result GetEarliestSCTTimestamp(Input sctExtension,
+ Maybe<uint64_t>& earliestTimestamp) {
+ earliestTimestamp.reset();
+
+ 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& scts : decodedSCTs) {
+ if (!earliestTimestamp.isSome() || scts.timestamp < *earliestTimestamp) {
+ earliestTimestamp = Some(scts.timestamp);
+ }
+ }
+ return Success;
+}
+
+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.
+
+ // TODO: need to verify that IsRevoked isn't called for trust anchors AND
+ // that that fact is documented in mozillapkix.
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
+
+ Maybe<uint64_t> earliestSCTTimestamp = Nothing();
+ if (sctExtension) {
+ Result rv = GetEarliestSCTTimestamp(*sctExtension, earliestSCTTimestamp);
+ if (rv != Success) {
+ MOZ_LOG(
+ gCertVerifierLog, LogLevel::Debug,
+ ("decoding SCT extension failed - CRLite will be not be consulted"));
+ }
+ }
+
+ if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
+ mCRLiteMode != CRLiteMode::Disabled && earliestSCTTimestamp.isSome()) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: checking CRLite"));
+ nsTArray<uint8_t> issuerBytes;
+ issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
+ certID.issuer.GetLength());
+ nsTArray<uint8_t> issuerSubjectPublicKeyInfoBytes;
+ issuerSubjectPublicKeyInfoBytes.AppendElements(
+ certID.issuerSubjectPublicKeyInfo.UnsafeGetData(),
+ certID.issuerSubjectPublicKeyInfo.GetLength());
+ nsTArray<uint8_t> serialNumberBytes;
+ serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
+ certID.serialNumber.GetLength());
+ uint64_t filterTimestamp;
+ int16_t crliteRevocationState;
+ nsresult rv = mCertStorage->GetCRLiteRevocationState(
+ issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
+ &filterTimestamp, &crliteRevocationState);
+ bool certificateFoundValidInCRLiteFilter = false;
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: CRLite call failed"));
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::LibraryFailure;
+ }
+ if (mCRLiteMode == CRLiteMode::Enforce) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ } else {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: CRLite check returned "
+ "state=%hd filter timestamp=%llu",
+ crliteRevocationState,
+ // The cast is to silence warnings on compilers where uint64_t is
+ // an unsigned long as opposed to an unsigned long long.
+ static_cast<unsigned long long>(filterTimestamp)));
+ Time filterTimestampTime(TimeFromEpochInSeconds(filterTimestamp));
+ // We can only use this result if the earliest embedded signed
+ // certificate timestamp from the certificate is older than what cert
+ // storage returned for its CRLite timestamp. Otherwise, the CRLite
+ // filter cascade may have been created before this certificate existed,
+ // and if it would create a false positive, it hasn't been accounted for.
+ // SCT timestamps are milliseconds since the epoch.
+ Time earliestCertificateTimestamp(
+ TimeFromEpochInSeconds(*earliestSCTTimestamp / 1000));
+ Result result =
+ earliestCertificateTimestamp.AddSeconds(mCRLiteCTMergeDelaySeconds);
+ if (result != Success) {
+ // This shouldn't happen - the merge delay is at most a year in seconds,
+ // and the SCT timestamp is supposed to be in the past.
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: integer overflow "
+ "calculating sct timestamp + merge delay (%llu + %llu)",
+ static_cast<unsigned long long>(*earliestSCTTimestamp / 1000),
+ static_cast<unsigned long long>(mCRLiteCTMergeDelaySeconds)));
+ if (mCRLiteMode == CRLiteMode::Enforce) {
+ // While we do have control over the possible values of the CT merge
+ // delay parameter, we don't have control over the SCT timestamp.
+ // Thus, if we've reached this point, the CA has probably made a
+ // mistake and we should treat this certificate as revoked.
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ // If Time::AddSeconds fails, the original value is unchanged. Since in
+ // this case `earliestCertificateTimestamp` must represent a value far
+ // in the future, any CRLite result will be discarded.
+ }
+ if (earliestCertificateTimestamp <= filterTimestampTime &&
+ crliteRevocationState == nsICertStorage::STATE_ENFORCE) {
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::CertificateRevoked;
+ }
+ if (mCRLiteMode == CRLiteMode::Enforce) {
+ MOZ_LOG(
+ gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: certificate revoked via "
+ "CRLite"));
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ MOZ_LOG(
+ gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: certificate revoked via "
+ "CRLite (not enforced - telemetry only)"));
+ }
+
+ if (crliteRevocationState == nsICertStorage::STATE_NOT_ENROLLED) {
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::IssuerNotEnrolled;
+ }
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: issuer not enrolled"));
+ }
+ if (filterTimestamp == 0) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: no timestamp"));
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::FilterNotAvailable;
+ }
+ } else if (earliestCertificateTimestamp > filterTimestampTime) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: cert too new"));
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::CertificateTooNew;
+ }
+ } else if (crliteRevocationState == nsICertStorage::STATE_UNSET) {
+ certificateFoundValidInCRLiteFilter = true;
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::CertificateValid;
+ }
+ }
+ }
+
+ // Also check stashed CRLite revocations. 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;
+ rv = mCertStorage->IsCertRevokedByStash(
+ issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: IsCertRevokedByStash "
+ "failed"));
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::LibraryFailure;
+ }
+ if (mCRLiteMode == CRLiteMode::Enforce) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ } else if (isRevokedByStash) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: IsCertRevokedByStash "
+ "returned true"));
+ if (mCRLiteLookupResult) {
+ *mCRLiteLookupResult = CRLiteLookupResult::CertRevokedByStash;
+ }
+ if (mCRLiteMode == CRLiteMode::Enforce) {
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ } else if (certificateFoundValidInCRLiteFilter &&
+ mCRLiteMode == CRLiteMode::Enforce) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain::CheckRevocation: certificate covered by "
+ "CRLite, found to be valid -> skipping OCSP processing"));
+ return Success;
+ }
+ }
+
+ // Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
+ // Since this affects EV there is no reason why DV should be more strict
+ // so all intermediates are allowed to have OCSP responses up to one year
+ // old.
+ uint16_t maxOCSPLifetimeInDays = 10;
+ if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+ maxOCSPLifetimeInDays = 365;
+ }
+
+ // 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.
+ //
+ // We only set the OCSP stapling status if we're validating the end-entity
+ // certificate. Non-end-entity certificates would always be
+ // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
+ Result stapledOCSPResponseResult = Success;
+ if (stapledOCSPResponse) {
+ MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
+ bool expired;
+ stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
+ certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
+ ResponseWasStapled, expired);
+ 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 if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
+ // 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) {
+ 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));
+
+ // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
+ bool blocklistIsFresh;
+ nsresult nsrv = mCertStorage->IsBlocklistFresh(&blocklistIsFresh);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ // 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 ((mOCSPFetching == NeverFetchOCSP) || (validityDuration < shortLifetime) ||
+ (endEntityOrCA == EndEntityOrCA::MustBeCA &&
+ (mOCSPFetching == FetchOCSPForDVHardFail ||
+ mOCSPFetching == FetchOCSPForDVSoftFail || blocklistIsFresh))) {
+ // 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;
+ }
+
+ return Success;
+ }
+
+ if (mOCSPFetching == LocalOnlyOCSPForEV) {
+ if (cachedResponseResult != Success) {
+ return cachedResponseResult;
+ }
+ return Result::ERROR_OCSP_UNKNOWN_CERT;
+ }
+
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ return Result::FATAL_ERROR_NO_MEMORY;
+ }
+
+ Result rv;
+ nsCString aiaLocation(VoidCString());
+
+ if (aiaExtension) {
+ rv = GetOCSPAuthorityInfoAccessLocation(arena, *aiaExtension, aiaLocation);
+ if (rv != Success) {
+ return rv;
+ }
+ }
+
+ 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.
+ 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);
+ }
+
+ return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
+ cachedResponseResult);
+}
+
+Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
+ const CertID& certID, const nsCString& aiaLocation, Time time,
+ uint16_t maxOCSPLifetimeInDays, const Result cachedResponseResult,
+ const Result stapledOCSPResponseResult) {
+ 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;
+ rv = DoOCSPRequest(aiaLocation, mOriginAttributes, ocspRequestBytes,
+ ocspRequestLength, GetOCSPTimeout(), ocspResponse);
+ 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;
+ }
+
+ return HandleOCSPFailure(cachedResponseResult, stapledOCSPResponseResult,
+ rv);
+ }
+
+ // 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;
+ rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
+ maxOCSPLifetimeInDays, response,
+ ResponseIsFromNetwork, expired);
+ 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;
+ }
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: end of CheckRevocation"));
+
+ return Success; // Soft fail -> success :(
+}
+
+Result NSSCertDBTrustDomain::HandleOCSPFailure(
+ const Result cachedResponseResult, const Result stapledOCSPResponseResult,
+ const Result error) {
+ 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"));
+ return Success; // Soft fail -> success :(
+}
+
+Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
+ const CertID& certID, Time time, uint16_t maxLifetimeInDays,
+ Input encodedResponse, EncodedResponseSource responseSource,
+ /*out*/ bool& expired) {
+ Time thisUpdate(Time::uninitialized);
+ Time validThrough(Time::uninitialized);
+
+ // We use a try and fallback approach which first mandates good signature
+ // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
+ // OCSP response signing certificate was issued with a SHA-1 signature,
+ // verification initially fails. We cache the failure and then re-use that
+ // result even when doing fallback (i.e. when weak signature digest algorithms
+ // should succeed). To address this we use an OCSPVerificationTrustDomain
+ // here, rather than using *this, to ensure verification succeeds for all
+ // allowed signature digest algorithms.
+ OCSPVerificationTrustDomain trustDomain(*this);
+ Result rv = VerifyEncodedOCSPResponse(trustDomain, 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
+ }
+ }
+ 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;
+}
+
+// If a certificate in the given chain appears to have been issued by one of
+// seven roots operated by StartCom and WoSign that are not trusted to issue new
+// certificates, verify that the end-entity has a notBefore date before 21
+// October 2016. If the value of notBefore is after this time, the chain is not
+// valid.
+// (NB: While there are seven distinct roots being checked for, two of them
+// share distinguished names, resulting in six distinct distinguished names to
+// actually look for.)
+static Result CheckForStartComOrWoSign(const UniqueCERTCertList& certChain) {
+ if (CERT_LIST_EMPTY(certChain)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ const CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certChain);
+ if (!endEntityNode || !endEntityNode->cert) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ PRTime notBefore;
+ PRTime notAfter;
+ if (CERT_GetCertTimes(endEntityNode->cert, &notBefore, &notAfter) !=
+ SECSuccess) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
+ // (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
+ static const PRTime OCTOBER_21_2016 = 1477008000000000;
+ if (notBefore <= OCTOBER_21_2016) {
+ return Success;
+ }
+
+ for (const CERTCertListNode* node = CERT_LIST_HEAD(certChain);
+ !CERT_LIST_END(node, certChain); node = CERT_LIST_NEXT(node)) {
+ if (!node || !node->cert) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ if (CertDNIsInList(node->cert, StartComAndWoSignDNs)) {
+ return Result::ERROR_REVOKED_CERTIFICATE;
+ }
+ }
+ return Success;
+}
+
+nsresult isDistrustedCertificateChain(const UniqueCERTCertList& certList,
+ bool& isDistrusted) {
+ // Set the default result to be distrusted.
+ isDistrusted = true;
+
+ // Allocate objects and retreive the root and end-entity certificates.
+ const CERTCertificate* certRoot = CERT_LIST_TAIL(certList)->cert;
+ const CERTCertificate* certLeaf = CERT_LIST_HEAD(certList)->cert;
+
+ // Check if the distrust field of the root is filled.
+ if (!certRoot->distrust) {
+ isDistrusted = false;
+ return NS_OK;
+ }
+
+ // Get validity for the current end-entity certificate
+ // and get the distrust field for the root certificate.
+ PRTime certRootDistrustAfter;
+ PRTime certLeafNotBefore;
+
+ SECStatus rv1 = DER_DecodeTimeChoice(
+ &certRootDistrustAfter, &certRoot->distrust->serverDistrustAfter);
+ SECStatus rv2 =
+ DER_DecodeTimeChoice(&certLeafNotBefore, &certLeaf->validity.notBefore);
+
+ if ((rv1 != SECSuccess) || (rv2 != SECSuccess)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Compare the validity of the end-entity certificate with
+ // the distrust value of the root.
+ if (certLeafNotBefore <= certRootDistrustAfter) {
+ isDistrusted = false;
+ }
+ return NS_OK;
+}
+
+Result NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
+ const CertPolicyId& requiredPolicy) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: IsChainValid"));
+
+ UniqueCERTCertList certList;
+ SECStatus srv =
+ ConstructCERTCertListFromReversedDERArray(certArray, certList);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ if (CERT_LIST_EMPTY(certList)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+
+ Result rv = CheckForStartComOrWoSign(certList);
+ if (rv != Success) {
+ return rv;
+ }
+
+ // Modernization in-progress: Keep certList as a CERTCertList for storage into
+ // the mBuiltChain variable at the end.
+ nsTArray<RefPtr<nsIX509Cert>> nssCertList;
+ nsresult nsrv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
+ certList, nssCertList);
+
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ nsCOMPtr<nsIX509Cert> rootCert;
+ nsrv = nsNSSCertificate::GetRootCertificate(nssCertList, rootCert);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ UniqueCERTCertificate root(rootCert->GetCert());
+ if (!root) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ bool isBuiltInRoot = false;
+ nsrv = rootCert->GetIsBuiltInRoot(&isBuiltInRoot);
+ if (NS_FAILED(nsrv)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ bool skipPinningChecksBecauseOfMITMMode =
+ (!isBuiltInRoot && mPinningMode == CertVerifier::pinningAllowUserCAMITM);
+ // If mHostname isn't set, we're not verifying in the context of a TLS
+ // handshake, so don't verify HPKP in those cases.
+ if (mHostname && (mPinningMode != CertVerifier::pinningDisabled) &&
+ !skipPinningChecksBecauseOfMITMMode) {
+ bool enforceTestMode =
+ (mPinningMode == CertVerifier::pinningEnforceTestMode);
+ bool chainHasValidPins;
+
+ nsTArray<Span<const uint8_t>> derCertSpanList;
+ size_t numCerts = certArray.GetLength();
+ for (size_t i = numCerts; i > 0; --i) {
+ const Input* der = certArray.GetDER(i - 1);
+ if (!der) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ derCertSpanList.EmplaceBack(der->UnsafeGetData(), der->GetLength());
+ }
+
+ nsrv = PublicKeyPinningService::ChainHasValidPins(
+ derCertSpanList, mHostname, time, enforceTestMode, mOriginAttributes,
+ 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 (isBuiltInRoot) {
+ bool isDistrusted;
+ nsrv = isDistrustedCertificateChain(certList, 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.
+ if (mHostname && CertDNIsInList(root.get(), RootSymantecDNs)) {
+ rootCert = nullptr; // Clear the state for Segment...
+ nsTArray<RefPtr<nsIX509Cert>> intCerts;
+ nsCOMPtr<nsIX509Cert> eeCert;
+
+ nsrv = nsNSSCertificate::SegmentCertificateChain(nssCertList, rootCert,
+ intCerts, eeCert);
+ if (NS_FAILED(nsrv)) {
+ // This chain is supposed to be complete, so this is an error.
+ return Result::ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED;
+ }
+
+ 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(certList);
+
+ return Success;
+}
+
+Result NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(
+ DigestAlgorithm aAlg, EndEntityOrCA endEntityOrCA, Time notBefore) {
+ // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
+ static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
+
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
+ if (aAlg == DigestAlgorithm::sha1) {
+ switch (mSHA1Mode) {
+ case CertVerifier::SHA1Mode::Forbidden:
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("SHA-1 certificate rejected"));
+ return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ case CertVerifier::SHA1Mode::ImportedRootOrBefore2016:
+ if (JANUARY_FIRST_2016 <= notBefore) {
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ ("Post-2015 SHA-1 certificate rejected"));
+ return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ break;
+ case CertVerifier::SHA1Mode::Allowed:
+ // Enforcing that the resulting chain uses an imported root is only
+ // possible at a higher level. This is done in CertVerifier::VerifyCert.
+ case CertVerifier::SHA1Mode::ImportedRoot:
+ default:
+ break;
+ // MSVC warns unless we explicitly handle this now-unused option.
+ case CertVerifier::SHA1Mode::UsedToBeBefore2016ButNowIsForbidden:
+ MOZ_ASSERT_UNREACHABLE("unexpected SHA1Mode type");
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ }
+
+ return Success;
+}
+
+Result NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) {
+ if (modulusSizeInBits < mMinRSABits) {
+ return Result::ERROR_INADEQUATE_KEY_SIZE;
+ }
+ return Success;
+}
+
+Result NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
+ const SignedDigest& signedDigest, Input subjectPublicKeyInfo) {
+ return VerifyRSAPKCS1SignedDigestNSS(signedDigest, 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::VerifyECDSASignedDigest(
+ const SignedDigest& signedDigest, Input subjectPublicKeyInfo) {
+ return VerifyECDSASignedDigestNSS(signedDigest, 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;
+}
+
+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::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);
+}
+
+bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
+ const nsCString& dir) {
+ // 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("\"");
+
+ UniqueSECMODModule userModule(SECMOD_LoadUserModule(
+ const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
+ if (!userModule) {
+ return false;
+ }
+
+ if (!userModule->loaded) {
+ return false;
+ }
+
+ 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
+ return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir);
+}
+
+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);
+}
+
+void UnloadUserModules() {
+ UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName));
+ if (rootsModule) {
+ SECMOD_UnloadUserModule(rootsModule.get());
+ }
+ UniqueSECMODModule osClientCertsModule(
+ SECMOD_FindModule(kOSClientCertsModuleName));
+ if (osClientCertsModule) {
+ SECMOD_UnloadUserModule(osClientCertsModule.get());
+ }
+}
+
+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(CERTCertificate* cert, nsICertStorage* certStorage) {
+ MOZ_ASSERT(cert);
+ MOZ_ASSERT(certStorage);
+ if (!cert || !certStorage) {
+ return false;
+ }
+ nsTArray<uint8_t> subject;
+ subject.AppendElements(cert->derSubject.data, cert->derSubject.len);
+ nsTArray<nsTArray<uint8_t>> certStorageCerts;
+ nsresult rv = certStorage->FindCertsBySubject(subject, certStorageCerts);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ for (const auto& certStorageCert : certStorageCerts) {
+ if (certStorageCert.Length() != cert->derCert.len) {
+ continue;
+ }
+ if (memcmp(certStorageCert.Elements(), cert->derCert.data,
+ 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 UniqueCERTCertList& certList) {
+ if (!certList) {
+ return;
+ }
+
+ UniqueCERTCertList intermediates(CERT_NewCertList());
+ if (!intermediates) {
+ return;
+ }
+
+ bool isEndEntity = true;
+ size_t numIntermediates = 0;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) {
+ if (isEndEntity) {
+ // Skip the end-entity; we only want to store intermediates
+ isEndEntity = false;
+ continue;
+ }
+
+ if (node->cert->slot) {
+ // This cert was found on a token; no need to remember it in the permanent
+ // database.
+ continue;
+ }
+
+ if (node->cert->isperm) {
+ // We don't need to remember certs already stored in perm db.
+ continue;
+ }
+
+ // 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).
+ if (node == CERT_LIST_TAIL(certList)) {
+ continue;
+ }
+
+ UniqueCERTCertificate certHandle(CERT_DupCertificate(node->cert));
+ if (CERT_AddCertToListTail(intermediates.get(), certHandle.get()) !=
+ SECSuccess) {
+ // If this fails, we're probably out of memory. Just return.
+ return;
+ }
+ certHandle.release(); // intermediates now owns the reference
+ numIntermediates++;
+ }
+
+ if (numIntermediates > 0) {
+ nsCOMPtr<nsIRunnable> importCertsRunnable(NS_NewRunnableFunction(
+ "IdleSaveIntermediateCerts",
+ [intermediates = std::move(intermediates)]() -> void {
+ if (AppShutdown::IsShuttingDown()) {
+ return;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ return;
+ }
+ nsCOMPtr<nsICertStorage> certStorage(
+ do_GetService(NS_CERT_STORAGE_CID));
+ size_t numCertsImported = 0;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(intermediates);
+ !CERT_LIST_END(node, intermediates);
+ node = CERT_LIST_NEXT(node)) {
+ if (AppShutdown::IsShuttingDown()) {
+ return;
+ }
+
+ if (CertIsInCertStorage(node->cert, certStorage)) {
+ 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(node->cert, nickname))) {
+ continue;
+ }
+ Unused << PK11_ImportCert(slot.get(), node->cert, 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..d16434fe4c
--- /dev/null
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -0,0 +1,281 @@
+/* -*- 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 "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,
+};
+
+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;
+
+/**
+ * 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 UniqueCERTCertList& 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,
+ CertVerifier::PinningMode pinningMode, unsigned int minRSABits,
+ ValidityCheckingMode validityCheckingMode,
+ CertVerifier::SHA1Mode sha1Mode,
+ NetscapeStepUpPolicy netscapeStepUpPolicy, CRLiteMode crliteMode,
+ uint64_t crliteCTMergeDelaySeconds,
+ const OriginAttributes& originAttributes,
+ const Vector<mozilla::pkix::Input>& thirdPartyRootInputs,
+ const Vector<mozilla::pkix::Input>& thirdPartyIntermediateInputs,
+ const Maybe<nsTArray<nsTArray<uint8_t>>>& extraCertificates,
+ /*out*/ UniqueCERTCertList& builtChain,
+ /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
+ /*optional*/ CRLiteLookupResult* crliteLookupResult = 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 VerifyRSAPKCS1SignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual Result VerifyECDSASignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ 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 GetIsErrorDueToDistrustedCAPolicy() const;
+
+ private:
+ 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);
+ TimeDuration GetOCSPTimeout() const;
+
+ Result SynchronousCheckRevocationWithServer(
+ const mozilla::pkix::CertID& certID, const nsCString& aiaLocation,
+ mozilla::pkix::Time time, uint16_t maxOCSPLifetimeInDays,
+ const Result cachedResponseResult,
+ const Result stapledOCSPResponseResult);
+ Result HandleOCSPFailure(const Result cachedResponseResult,
+ const Result stapledOCSPResponseResult,
+ const Result error);
+
+ 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;
+ CertVerifier::PinningMode mPinningMode;
+ const unsigned int mMinRSABits;
+ ValidityCheckingMode mValidityCheckingMode;
+ CertVerifier::SHA1Mode mSHA1Mode;
+ NetscapeStepUpPolicy mNetscapeStepUpPolicy;
+ CRLiteMode mCRLiteMode;
+ uint64_t mCRLiteCTMergeDelaySeconds;
+ 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
+ UniqueCERTCertList& mBuiltChain; // non-owning
+ PinningTelemetryInfo* mPinningTelemetryInfo;
+ CRLiteLookupResult* mCRLiteLookupResult;
+ 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;
+};
+
+} // 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..2036c0e202
--- /dev/null
+++ b/security/certverifier/OCSPCache.cpp
@@ -0,0 +1,324 @@
+/* -*- 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 "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.
+// The value calculated is SHA384(derIssuer || derPublicKey || serialNumberLen
+// || serialNumber || firstPartyDomainLen || firstPartyDomain).
+// 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;
+ }
+
+ // OCSP should not be isolated by containers.
+ NS_ConvertUTF16toUTF8 firstPartyDomain(originAttributes.mFirstPartyDomain);
+ if (!firstPartyDomain.IsEmpty()) {
+ rv = DigestLength(context, firstPartyDomain.Length());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ rv =
+ PK11_DigestOp(context.get(),
+ BitwiseCast<const unsigned char*>(firstPartyDomain.get()),
+ firstPartyDomain.Length());
+ 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 */) {
+ 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) {
+ NS_ConvertUTF16toUTF8 firstPartyDomain(aOriginAttributes.mFirstPartyDomain);
+ MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
+ (aMessage, &aCertID, firstPartyDomain.get()));
+}
+
+void OCSPCache::MakeMostRecentlyUsed(size_t aIndex,
+ const MutexAutoLock& /* aProofOfLock */) {
+ 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..26278c1a1d
--- /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;
+};
+
+} // namespace psm
+} // namespace mozilla
+
+#endif // mozilla_psm_OCSPCache_h
diff --git a/security/certverifier/OCSPVerificationTrustDomain.cpp b/security/certverifier/OCSPVerificationTrustDomain.cpp
new file mode 100644
index 0000000000..0a23821e4c
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -0,0 +1,105 @@
+/* -*- 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 "OCSPVerificationTrustDomain.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+
+namespace mozilla {
+namespace psm {
+
+OCSPVerificationTrustDomain::OCSPVerificationTrustDomain(
+ NSSCertDBTrustDomain& certDBTrustDomain)
+ : mCertDBTrustDomain(certDBTrustDomain) {}
+
+Result OCSPVerificationTrustDomain::GetCertTrust(
+ EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
+ Input candidateCertDER,
+ /*out*/ TrustLevel& trustLevel) {
+ return mCertDBTrustDomain.GetCertTrust(endEntityOrCA, policy,
+ candidateCertDER, trustLevel);
+}
+
+Result OCSPVerificationTrustDomain::FindIssuer(Input, IssuerChecker&, Time) {
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result OCSPVerificationTrustDomain::IsChainValid(const DERArray&, Time,
+ const CertPolicyId&) {
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result OCSPVerificationTrustDomain::CheckRevocation(EndEntityOrCA,
+ const CertID&, Time,
+ Duration, const Input*,
+ const Input*,
+ const Input*) {
+ // We do not expect this to be called for OCSP signers
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result OCSPVerificationTrustDomain::CheckSignatureDigestAlgorithm(
+ DigestAlgorithm aAlg, EndEntityOrCA aEEOrCA, Time notBefore) {
+ // The reason for wrapping the NSSCertDBTrustDomain in an
+ // OCSPVerificationTrustDomain is to allow us to bypass the weaker signature
+ // algorithm check - thus all allowable signature digest algorithms should
+ // always be accepted. This is only needed while we gather telemetry on SHA-1.
+ return Success;
+}
+
+Result OCSPVerificationTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+ EndEntityOrCA aEEOrCA, unsigned int aModulusSizeInBits) {
+ return mCertDBTrustDomain.CheckRSAPublicKeyModulusSizeInBits(
+ aEEOrCA, aModulusSizeInBits);
+}
+
+Result OCSPVerificationTrustDomain::VerifyRSAPKCS1SignedDigest(
+ const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo) {
+ return mCertDBTrustDomain.VerifyRSAPKCS1SignedDigest(aSignedDigest,
+ aSubjectPublicKeyInfo);
+}
+
+Result OCSPVerificationTrustDomain::CheckECDSACurveIsAcceptable(
+ EndEntityOrCA aEEOrCA, NamedCurve aCurve) {
+ return mCertDBTrustDomain.CheckECDSACurveIsAcceptable(aEEOrCA, aCurve);
+}
+
+Result OCSPVerificationTrustDomain::VerifyECDSASignedDigest(
+ const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo) {
+ return mCertDBTrustDomain.VerifyECDSASignedDigest(aSignedDigest,
+ aSubjectPublicKeyInfo);
+}
+
+Result OCSPVerificationTrustDomain::CheckValidityIsAcceptable(
+ Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
+ KeyPurposeId keyPurpose) {
+ return mCertDBTrustDomain.CheckValidityIsAcceptable(
+ notBefore, notAfter, endEntityOrCA, keyPurpose);
+}
+
+Result OCSPVerificationTrustDomain::NetscapeStepUpMatchesServerAuth(
+ Time notBefore,
+ /*out*/ bool& matches) {
+ return mCertDBTrustDomain.NetscapeStepUpMatchesServerAuth(notBefore, matches);
+}
+
+void OCSPVerificationTrustDomain::NoteAuxiliaryExtension(
+ AuxiliaryExtension extension, Input extensionData) {
+ mCertDBTrustDomain.NoteAuxiliaryExtension(extension, extensionData);
+}
+
+Result OCSPVerificationTrustDomain::DigestBuf(Input item,
+ DigestAlgorithm digestAlg,
+ /*out*/ uint8_t* digestBuf,
+ size_t digestBufLen) {
+ return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
+}
+
+} // namespace psm
+} // namespace mozilla
diff --git a/security/certverifier/OCSPVerificationTrustDomain.h b/security/certverifier/OCSPVerificationTrustDomain.h
new file mode 100644
index 0000000000..be16a581cc
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -0,0 +1,90 @@
+/* -*- 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 mozilla_psm__OCSPVerificationTrustDomain_h
+#define mozilla_psm__OCSPVerificationTrustDomain_h
+
+#include "mozpkix/pkixtypes.h"
+#include "NSSCertDBTrustDomain.h"
+
+namespace mozilla {
+namespace psm {
+
+typedef mozilla::pkix::Result Result;
+
+class OCSPVerificationTrustDomain : public mozilla::pkix::TrustDomain {
+ public:
+ explicit OCSPVerificationTrustDomain(NSSCertDBTrustDomain& certDBTrustDomain);
+
+ 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 VerifyRSAPKCS1SignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+ virtual Result CheckECDSACurveIsAcceptable(
+ mozilla::pkix::EndEntityOrCA endEntityOrCA,
+ mozilla::pkix::NamedCurve curve) override;
+
+ virtual Result VerifyECDSASignedDigest(
+ const mozilla::pkix::SignedDigest& signedDigest,
+ 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;
+
+ private:
+ NSSCertDBTrustDomain& mCertDBTrustDomain;
+};
+
+} // namespace psm
+} // namespace mozilla
+
+#endif // mozilla_psm__OCSPVerificationTrustDomain_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-StartComAndWoSignData.inc b/security/certverifier/TrustOverride-StartComAndWoSignData.inc
new file mode 100644
index 0000000000..0f303836a8
--- /dev/null
+++ b/security/certverifier/TrustOverride-StartComAndWoSignData.inc
@@ -0,0 +1,84 @@
+// /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6
+// Using a consistent naming convention, this would actually be called
+// 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8
+// identifiers, this will have to do.
+static const uint8_t CAWoSignRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0,
+ 0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root
+static const uint8_t CAWoSignECCRootDN[72] = {
+ 0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45,
+ 0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign
+static const uint8_t CertificationAuthorityofWoSignDN[87] = {
+ 0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E,
+};
+
+// /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2
+static const uint8_t CertificationAuthorityofWoSignG2DN[90] = {
+ 0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
+ 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32,
+};
+
+// /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
+static const uint8_t StartComCertificationAuthorityDN[127] = {
+ 0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
+ 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79,
+};
+
+// /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2
+static const uint8_t StartComCertificationAuthorityG2DN[85] = {
+ 0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
+ 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 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, 0x47, 0x32,
+};
+
+static const DataAndLength StartComAndWoSignDNs[]= {
+ { CAWoSignRootDN,
+ sizeof(CAWoSignRootDN) },
+ { CAWoSignECCRootDN,
+ sizeof(CAWoSignECCRootDN) },
+ { CertificationAuthorityofWoSignDN,
+ sizeof(CertificationAuthorityofWoSignDN) },
+ { CertificationAuthorityofWoSignG2DN,
+ sizeof(CertificationAuthorityofWoSignG2DN) },
+ { StartComCertificationAuthorityDN,
+ sizeof(StartComCertificationAuthorityDN) },
+ { StartComCertificationAuthorityG2DN,
+ sizeof(StartComCertificationAuthorityG2DN) },
+};
diff --git a/security/certverifier/TrustOverride-SymantecData.inc b/security/certverifier/TrustOverride-SymantecData.inc
new file mode 100644
index 0000000000..0e0e8eb7ed
--- /dev/null
+++ b/security/certverifier/TrustOverride-SymantecData.inc
@@ -0,0 +1,209 @@
+// Script from security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
+// Invocation: crtshToIdentifyingStruct.py -dn -listname RootSymantecDNs 3381895 12729019 8983600 12726040 8983601 8984570 68409 26682 1039083
+
+// /C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2
+// SHA256 Fingerprint: 5E:DB:7A:C4:3B:82:A0:6A:87:61:E8:D7:BE:49:79:EB
+// F2:61:1F:7D:D7:9B:F9:1C:1C:6B:56:6A:21:9E:D7:66
+// https://crt.sh/?id=3381895 (crt.sh ID=3381895)
+static const uint8_t CAGeoTrustPrimaryCertificationAuthorityG2DN[155] = {
+ 0x30, 0x81, 0x98, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13,
+ 0x0D, 0x47, 0x65, 0x6F, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6E, 0x63,
+ 0x2E, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x30, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x37, 0x20, 0x47, 0x65, 0x6F, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 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, 0x36, 0x30, 0x34, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x2D, 0x47, 0x65, 0x6F, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 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, 0x32,
+};
+
+// /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,
+};
+
+// /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2008 VeriSign, Inc. - For authorized use only/CN=VeriSign Universal Root Certification Authority
+// SHA256 Fingerprint: 23:99:56:11:27:A5:71:25:DE:8C:EF:EA:61:0D:DF:2F
+// A0:78:B5:C8:06:7F:4E:82:82:90:BF:B8:60:E8:4B:3C
+// https://crt.sh/?id=1039083 (crt.sh ID=1039083)
+static const uint8_t CAVeriSignUniversalRootCertificationAuthorityDN[192] = {
+ 0x30, 0x81, 0xBD, 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, 0x32, 0x30,
+ 0x30, 0x38, 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, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x2F, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x55, 0x6E,
+ 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79,
+};
+
+static const DataAndLength RootSymantecDNs[]= {
+ { CAGeoTrustPrimaryCertificationAuthorityG2DN,
+ sizeof(CAGeoTrustPrimaryCertificationAuthorityG2DN) },
+ { CASymantecClass1PublicPrimaryCertificationAuthorityG4DN,
+ sizeof(CASymantecClass1PublicPrimaryCertificationAuthorityG4DN) },
+ { CASymantecClass1PublicPrimaryCertificationAuthorityG6DN,
+ sizeof(CASymantecClass1PublicPrimaryCertificationAuthorityG6DN) },
+ { CASymantecClass2PublicPrimaryCertificationAuthorityG4DN,
+ sizeof(CASymantecClass2PublicPrimaryCertificationAuthorityG4DN) },
+ { CASymantecClass2PublicPrimaryCertificationAuthorityG6DN,
+ sizeof(CASymantecClass2PublicPrimaryCertificationAuthorityG6DN) },
+ { CAVeriSignClass1PublicPrimaryCertificationAuthorityG3DN,
+ sizeof(CAVeriSignClass1PublicPrimaryCertificationAuthorityG3DN) },
+ { CAVeriSignClass2PublicPrimaryCertificationAuthorityG3DN,
+ sizeof(CAVeriSignClass2PublicPrimaryCertificationAuthorityG3DN) },
+ { CAVeriSignClass3PublicPrimaryCertificationAuthorityG3DN,
+ sizeof(CAVeriSignClass3PublicPrimaryCertificationAuthorityG3DN) },
+ { CAVeriSignUniversalRootCertificationAuthorityDN,
+ sizeof(CAVeriSignUniversalRootCertificationAuthorityDN) },
+};
diff --git a/security/certverifier/TrustOverride-TestImminentDistrustData.inc b/security/certverifier/TrustOverride-TestImminentDistrustData.inc
new file mode 100644
index 0000000000..7b51a6d0aa
--- /dev/null
+++ b/security/certverifier/TrustOverride-TestImminentDistrustData.inc
@@ -0,0 +1,21 @@
+// Script from security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
+// Invocation: crtshToIdentifyingStruct.py -dn -listname TestImminentDistrustEndEntityDNs ../../ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem
+
+// This file is used by test_imminent_distrust.js and by
+// browser_console_certificate_imminent_distrust.js to ensure that the UI for
+// alerting users to an upcoming CA distrust action continues to function.
+
+// /CN=Imminently Distrusted End Entity
+// SHA256 Fingerprint: DB:4E:B0:BA:38:93:02:E8:32:87:03:FA:C3:C8:F7:6A
+// 88:77:03:B2:9E:CE:C0:C8:27:26:CC:8F:F5:64:E6:B5
+static const uint8_t CAImminentlyDistrustedEndEntityDN[45] = {
+ 0x30, 0x2B, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20,
+ 0x49, 0x6D, 0x6D, 0x69, 0x6E, 0x65, 0x6E, 0x74, 0x6C, 0x79, 0x20, 0x44, 0x69,
+ 0x73, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x45, 0x6E, 0x64, 0x20,
+ 0x45, 0x6E, 0x74, 0x69, 0x74, 0x79,
+};
+
+static const DataAndLength TestImminentDistrustEndEntityDNs[]= {
+ { CAImminentlyDistrustedEndEntityDN,
+ sizeof(CAImminentlyDistrustedEndEntityDN) },
+};
diff --git a/security/certverifier/TrustOverrideUtils.h b/security/certverifier/TrustOverrideUtils.h
new file mode 100644
index 0000000000..d8b2146e04
--- /dev/null
+++ b/security/certverifier/TrustOverrideUtils.h
@@ -0,0 +1,97 @@
+/* -*- 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 "X509CertValidity.h"
+#include "nsNSSCertificate.h"
+#include "mozilla/ArrayUtils.h"
+
+using namespace mozilla;
+
+struct DataAndLength {
+ const uint8_t* data;
+ uint32_t len;
+};
+
+template <size_t T>
+static bool CertDNIsInList(const CERTCertificate* aCert,
+ const DataAndLength (&aDnList)[T]) {
+ MOZ_ASSERT(aCert);
+ if (!aCert) {
+ return false;
+ }
+
+ for (auto& dn : aDnList) {
+ if (aCert->derSubject.len == dn.len &&
+ mozilla::ArrayEqual(aCert->derSubject.data, dn.data, dn.len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <size_t T>
+static bool CertSPKIIsInList(const CERTCertificate* aCert,
+ const DataAndLength (&aSpkiList)[T]) {
+ MOZ_ASSERT(aCert);
+ if (!aCert) {
+ return false;
+ }
+
+ for (auto& spki : aSpkiList) {
+ if (aCert->derPublicKey.len == spki.len &&
+ mozilla::ArrayEqual(aCert->derPublicKey.data, spki.data, spki.len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <size_t T, size_t R>
+static bool CertMatchesStaticData(const CERTCertificate* cert,
+ const unsigned char (&subject)[T],
+ const unsigned char (&spki)[R]) {
+ MOZ_ASSERT(cert);
+ if (!cert) {
+ return false;
+ }
+ return cert->derSubject.len == T &&
+ mozilla::ArrayEqual(cert->derSubject.data, subject, T) &&
+ cert->derPublicKey.len == R &&
+ mozilla::ArrayEqual(cert->derPublicKey.data, spki, R);
+}
+
+// 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<RefPtr<nsIX509Cert>>& 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) {
+ UniqueCERTCertificate nssCert(cert->GetCert());
+ if (CertSPKIIsInList(nssCert.get(), 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..e7135c2c0d
--- /dev/null
+++ b/security/certverifier/moz.build
@@ -0,0 +1,63 @@
+# -*- 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 += [
+ "BRNameMatchingPolicy.h",
+ "CertVerifier.h",
+ "OCSPCache.h",
+]
+
+UNIFIED_SOURCES += [
+ "BRNameMatchingPolicy.cpp",
+ "CertVerifier.cpp",
+ "NSSCertDBTrustDomain.cpp",
+ "OCSPCache.cpp",
+ "OCSPVerificationTrustDomain.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",
+]
+
+if CONFIG["CC_TYPE"] == "clang-cl":
+ # -Wall on clang-cl maps to -Weverything, which turns on way too
+ # much, so we're passing through -Wall using -Xclang.
+ CXXFLAGS += ["-Xclang"]
+CXXFLAGS += ["-Wall"]
+
+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..6909d210d9
--- /dev/null
+++ b/security/certverifier/tests/gtest/TrustOverrideTest.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 "nsIX509Cert.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 char* kOverrideCaPem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICsjCCAZygAwIBAgIBATALBgkqhkiG9w0BAQswDTELMAkGA1UEAwwCY2EwIhgP\n"
+ "MjAxNTExMjgwMDAwMDBaGA8yMDE4MDIwNTAwMDAwMFowDTELMAkGA1UEAwwCY2Ew\n"
+ "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ\n"
+ "PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH\n"
+ "9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw\n"
+ "4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86\n"
+ "exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0\n"
+ "ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N\n"
+ "AgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAsGCSqGSIb3DQEB\n"
+ "CwOCAQEAchHf1yV+blE6fvS53L3DGmvxEpn9+t+xwOvWczBmLFEzUPdncakdaWlQ\n"
+ "v7q81BPyjBqkYbQi15Ws81hY3dnXn8LT1QktCL9guvc3z4fMdQbRjpjcIReCYt3E\n"
+ "PB22Jl2FCm6ii4XL0qDFD26WK3zMe2Uks6t55f8VeDTBGNoPp2JMsWY1Pi4vR6wK\n"
+ "AY96WoXS/qrYkmMEOgFu907pApeAeE8VJzXjqMLF6/W1VN7ISnGzWQ8zKQnlp3YA\n"
+ "mvWZQcD6INK8mvpZxIeu6NtHaKEXGw7tlGekmkVhapPtQZYnWcsXybRrZf5g3hOh\n"
+ "JFPl8kW42VoxXL11PP5NX2ylTsJ//g==\n"
+ "-----END CERTIFICATE-----";
+
+// certspec (for pycert.py)
+//
+// issuer:ca
+// subject:ca-intermediate
+// extension:basicConstraints:cA,
+// extension:keyUsage:cRLSign,keyCertSign
+// subjectKey:secp384r1
+// serialNumber:2
+const char* kOverrideCaIntermediatePem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICFDCB/aADAgECAgECMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMMAmNhMCIY\n"
+ "DzIwMTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD2Nh\n"
+ "LWludGVybWVkaWF0ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABKFockM2K1x7GInz\n"
+ "eRVGFaHHP7SN7oY+AikV22COJS3ktxMtqM6Y6DFTTmqcDAsJyNY5regyBuW6gTRz\n"
+ "oR+jMOBdqMluQ4P+J4c9qXEDviiIz/AC8Fr3Gh/dzIN0qm6pzqMdMBswDAYDVR0T\n"
+ "BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAAvDaCiJdFvr\n"
+ "wlLCqTM7qB9sS9vjz3lL8zbSssUlx5fXtIQACg0zJTKUyLJf/nTlit/txhPO0Q7T\n"
+ "PjKpKjE4ohYMEBW+EHs9U0/yYoxVwhjcVGVzEVsVQXzgll34t1W5rVtxof6DrH/l\n"
+ "Mc9Y7vhXFWnT8jsiqXtdIXh3fi8AjCzYSAjsfQfwpHRnM8uY2GaAb8SovYiGIk2t\n"
+ "CSdCBkA6V7hTeQSyLCj4JsCzX1JzMx33ebw3Z10Mq2AgRk3Uw5/L+b+PKScZXw42\n"
+ "lJNniSZbH7lHr08bWhWFXVK8aD2VGUeT/FswTlx3XGYVFrbXQ8WDEe0VkA+4aN33\n"
+ "+mbXPBnW8ao=\n"
+ "-----END CERTIFICATE-----";
+
+// /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)},
+};
+
+static mozilla::UniqueCERTCertificate CertFromString(const char* aPem) {
+ nsCOMPtr<nsIX509Cert> cert =
+ nsNSSCertificate::ConstructFromDER(const_cast<char*>(aPem), strlen(aPem));
+ if (!cert) {
+ return nullptr;
+ }
+
+ mozilla::UniqueCERTCertificate nssCert(cert->GetCert());
+ return nssCert;
+}
+
+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) {
+ mozilla::UniqueCERTCertificate caObj = CertFromString(kOverrideCaPem);
+ ASSERT_TRUE(caObj != nullptr)
+ << "Should have parsed";
+ mozilla::UniqueCERTCertificate intObj =
+ CertFromString(kOverrideCaIntermediatePem);
+ ASSERT_TRUE(intObj != nullptr)
+ << "Should have parsed";
+
+ EXPECT_TRUE(CertDNIsInList(caObj.get(), OverrideCaDNs))
+ << "CA should be in the DN list";
+ EXPECT_FALSE(CertDNIsInList(intObj.get(), OverrideCaDNs))
+ << "Int should not be in the DN list";
+}
+
+TEST_F(psm_TrustOverrideTest, CheckCertSPKIIsInList) {
+ mozilla::UniqueCERTCertificate caObj = CertFromString(kOverrideCaPem);
+ ASSERT_TRUE(caObj != nullptr)
+ << "Should have parsed";
+ mozilla::UniqueCERTCertificate intObj =
+ CertFromString(kOverrideCaIntermediatePem);
+ ASSERT_TRUE(intObj != nullptr)
+ << "Should have parsed";
+
+ EXPECT_TRUE(CertSPKIIsInList(caObj.get(), OverrideCaSPKIs))
+ << "CA should be in the SPKI list";
+ EXPECT_FALSE(CertSPKIIsInList(intObj.get(), 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"