commit a39d9f886066d353c0c4efab177ffd862f03e56c parent b648af00a687c46a58c768670c203216bb91d52d Author: Dana Keeler <dkeeler@mozilla.com> Date: Mon, 13 Oct 2025 16:22:31 +0000 Bug 1992025 - implement API to verify 1-QWACs r=jschanck,supply-chain-reviewers Differential Revision: https://phabricator.services.mozilla.com/D267137 Diffstat:
44 files changed, 1135 insertions(+), 4 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock @@ -2645,6 +2645,7 @@ dependencies = [ "processtools", "profiler_helper", "qcms", + "qwac-trust-anchors", "rsdparsa_capi", "rure", "rusqlite 0.37.0", @@ -5711,6 +5712,16 @@ dependencies = [ ] [[package]] +name = "qwac-trust-anchors" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "rsclientcerts-util", + "static_prefs", + "thin-vec", +] + +[[package]] name = "r-efi" version = "5.999.999" diff --git a/Cargo.toml b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "security/manager/ssl/osclientcerts", "security/manager/ssl/tests/unit/pkcs11testmodule", "security/manager/ssl/tests/unit/test_trust_anchors", + "security/manager/ssl/qwac_trust_anchors", "security/manager/ssl/trust_anchors", "security/mls/mls_gk", "testing/geckodriver", diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -17320,6 +17320,14 @@ value: 128 mirror: always +# Whether or not to enable the test trust anchor list when verifying QWACs +# (qualified website authentication certificates) +- name: security.qwacs.enable_test_trust_anchors + type: RelaxedAtomicBool + value: false + mirror: always + rust: true + - name: security.tls.version.min type: RelaxedAtomicUint32 value: 3 diff --git a/security/manager/ssl/QWACTrustDomain.cpp b/security/manager/ssl/QWACTrustDomain.cpp @@ -0,0 +1,162 @@ +/* -*- 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 "QWACTrustDomain.h" + +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixutil.h" +#include "qwac_trust_anchors/qwac_trust_anchors_ffi_generated.h" + +using namespace mozilla::pkix; + +namespace mozilla { +namespace psm { + +QWACTrustDomain::QWACTrustDomain( + nsTArray<RefPtr<nsIX509Cert>>& collectedCerts) { + for (const auto& cert : collectedCerts) { + nsTArray<uint8_t> der; + if (NS_SUCCEEDED(cert->GetRawDER(der))) { + mIntermediates.AppendElement(std::move(der)); + } + } +} + +pkix::Result QWACTrustDomain::FindIssuer(Input encodedIssuerName, + IssuerChecker& checker, Time) { + nsTArray<Input> candidates; + + nsTArray<uint8_t> subject(encodedIssuerName.UnsafeGetData(), + encodedIssuerName.GetLength()); + nsTArray<nsTArray<uint8_t>> qwacTrustAnchors; + find_qwac_trust_anchors_by_subject(&subject, &qwacTrustAnchors); + + for (const auto& trustAnchor : qwacTrustAnchors) { + Input trustAnchorInput; + pkix::Result rv = + trustAnchorInput.Init(trustAnchor.Elements(), trustAnchor.Length()); + // This should never fail, since the possible trust anchors are all + // hard-coded and they should never be too long. + if (rv != Success) { + return rv; + } + candidates.AppendElement(std::move(trustAnchorInput)); + } + + for (const auto& intermediate : mIntermediates) { + Input intermediateInput; + pkix::Result rv = + intermediateInput.Init(intermediate.Elements(), intermediate.Length()); + // These intermediates are from the TLS handshake and could be too long. + if (rv != Success) { + continue; + } + candidates.AppendElement(std::move(intermediateInput)); + } + + for (const auto& candidate : candidates) { + bool keepGoing; + pkix::Result rv = checker.Check( + candidate, nullptr /*additionalNameConstraints*/, keepGoing); + if (rv != Success) { + return rv; + } + if (!keepGoing) { + break; + } + } + + return Success; +} + +pkix::Result QWACTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) { + BackCert backCert(candidateCertDER, endEntityOrCA, nullptr); + Result rv = backCert.Init(); + if (rv != Success) { + return rv; + } + Input subjectInput(backCert.GetSubject()); + nsTArray<uint8_t> subject(subjectInput.UnsafeGetData(), + subjectInput.GetLength()); + nsTArray<uint8_t> candidateCert(candidateCertDER.UnsafeGetData(), + candidateCertDER.GetLength()); + if (is_qwac_trust_anchor(&subject, &candidateCert)) { + trustLevel = TrustLevel::TrustAnchor; + } else { + trustLevel = TrustLevel::InheritsTrust; + } + + return Success; +} + +pkix::Result QWACTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) { + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); +} + +pkix::Result QWACTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, + Time, Duration, + /*optional*/ const Input*, + /*optional*/ const Input*) { + return Success; +} + +pkix::Result QWACTrustDomain::IsChainValid(const DERArray& certChain, Time time, + const CertPolicyId& requiredPolicy) { + return Success; +} + +pkix::Result QWACTrustDomain::CheckSignatureDigestAlgorithm( + DigestAlgorithm digestAlg, EndEntityOrCA, Time) { + return Success; +} + +pkix::Result QWACTrustDomain::CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits) { + return Success; +} + +pkix::Result QWACTrustDomain::VerifyRSAPKCS1SignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr); +} + +pkix::Result QWACTrustDomain::VerifyRSAPSSSignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr); +} + +pkix::Result QWACTrustDomain::CheckECDSACurveIsAcceptable( + EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve) { + return Success; +} + +pkix::Result QWACTrustDomain::VerifyECDSASignedData( + Input data, DigestAlgorithm digestAlgorithm, Input signature, + Input subjectPublicKeyInfo) { + return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, + subjectPublicKeyInfo, nullptr); +} + +pkix::Result QWACTrustDomain::CheckValidityIsAcceptable( + Time /*notBefore*/, Time /*notAfter*/, EndEntityOrCA /*endEntityOrCA*/, + KeyPurposeId /*keyPurpose*/) { + return Success; +} + +void QWACTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/, + Input /*extensionData*/) {} + +} // namespace psm +} // namespace mozilla diff --git a/security/manager/ssl/QWACTrustDomain.h b/security/manager/ssl/QWACTrustDomain.h @@ -0,0 +1,81 @@ +/* -*- 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 QWACTrustDomain_h +#define QWACTrustDomain_h + +#include "mozpkix/pkixtypes.h" +#include "nsIX509Cert.h" +#include "nsTArray.h" + +namespace mozilla { +namespace psm { + +class QWACTrustDomain final : public mozilla::pkix::TrustDomain { + public: + typedef mozilla::pkix::Result Result; + + explicit QWACTrustDomain(nsTArray<RefPtr<nsIX509Cert>>& collectedCerts); + + virtual Result GetCertTrust( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + const mozilla::pkix::CertPolicyId& policy, + mozilla::pkix::Input candidateCertDER, + /*out*/ mozilla::pkix::TrustLevel& trustLevel) override; + virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName, + IssuerChecker& checker, + mozilla::pkix::Time time) 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) override; + virtual Result IsChainValid( + const mozilla::pkix::DERArray& certChain, mozilla::pkix::Time time, + const mozilla::pkix::CertPolicyId& requiredPolicy) override; + virtual Result CheckSignatureDigestAlgorithm( + mozilla::pkix::DigestAlgorithm digestAlg, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::Time notBefore) override; + virtual Result CheckRSAPublicKeyModulusSizeInBits( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + unsigned int modulusSizeInBits) override; + virtual Result VerifyRSAPKCS1SignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result VerifyRSAPSSSignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result CheckECDSACurveIsAcceptable( + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::NamedCurve curve) override; + virtual Result VerifyECDSASignedData( + mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, + mozilla::pkix::Input signature, + mozilla::pkix::Input subjectPublicKeyInfo) override; + virtual Result CheckValidityIsAcceptable( + mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter, + mozilla::pkix::EndEntityOrCA endEntityOrCA, + mozilla::pkix::KeyPurposeId keyPurpose) override; + virtual void NoteAuxiliaryExtension( + mozilla::pkix::AuxiliaryExtension extension, + mozilla::pkix::Input extensionData) override; + virtual Result DigestBuf(mozilla::pkix::Input item, + mozilla::pkix::DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) override; + + private: + nsTArray<nsTArray<uint8_t>> mIntermediates; +}; + +} // namespace psm +} // namespace mozilla + +#endif // QWACTrustDomain_h diff --git a/security/manager/ssl/QWACs.cpp b/security/manager/ssl/QWACs.cpp @@ -0,0 +1,251 @@ +/* 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 "nsIX509CertDB.h" + +#include "CryptoTask.h" +#include "QWACTrustDomain.h" +#include "mozilla/dom/Promise.h" +#include "mozpkix/pkix.h" +#include "mozpkix/pkixder.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixtypes.h" +#include "mozpkix/pkixutil.h" +#include "nsIX509Cert.h" +#include "nsNSSCertificateDB.h" + +using namespace mozilla::pkix; +using namespace mozilla::psm; + +using mozilla::dom::Promise; + +class Verify1QWACTask : public mozilla::CryptoTask { + public: + Verify1QWACTask(nsIX509Cert* aCert, + const nsTArray<RefPtr<nsIX509Cert>>& aCollectedCerts, + RefPtr<Promise>& aPromise) + : mCert(aCert), + mCollectedCerts(aCollectedCerts.Clone()), + mPromise(new nsMainThreadPtrHolder<Promise>("Verify1QWACTask::mPromise", + aPromise)), + mVerifiedAs1QWAC(false) {} + + private: + virtual nsresult CalculateResult() override; + virtual void CallCallback(nsresult rv) override; + + RefPtr<nsIX509Cert> mCert; + nsTArray<RefPtr<nsIX509Cert>> mCollectedCerts; + nsMainThreadPtrHandle<Promise> mPromise; + + bool mVerifiedAs1QWAC; +}; + +// Does this certificate have the correct qcStatements ("qualified certificate +// statements") to be a QWAC ("qualified website authentication certificate")? +// ETSI EN 319 412-5 Clauses 4.2.1 and 4.2.3 state that a certificate issued in +// compliance with Annex IV of Regulation (EU) No 910/2014 (i.e. a QWAC) has +// 1) a QCStatement with statementId equal to id-etsi-qsc-QcCompliance and +// an omitted statementInfo, and +// 2) a QCStatement with statementId equal to id-etsi-qcs-QcType and a +// statementInfo of length one that contains the id-etsi-qct-web +// identifier. +bool CertHasQWACSQCStatements(const nsTArray<uint8_t>& certDER) { + using namespace mozilla::pkix::der; + + // python DottedOIDToCode.py id-etsi-qcs-QcCompliance 0.4.0.1862.1.1 + static const uint8_t id_etsi_qcs_QcCompliance[] = {0x04, 0x00, 0x8e, + 0x46, 0x01, 0x01}; + + // python DottedOIDToCode.py id-etsi-qcs-QcType 0.4.0.1862.1.6 + static const uint8_t id_etsi_qcs_QcType[] = {0x04, 0x00, 0x8e, + 0x46, 0x01, 0x06}; + + // python DottedOIDToCode.py id-etsi-qct-web 0.4.0.1862.1.6.3 + static const uint8_t id_etsi_qct_web[] = {0x04, 0x00, 0x8e, 0x46, + 0x01, 0x06, 0x03}; + + Input cert; + if (cert.Init(certDER.Elements(), certDER.Length()) != Success) { + return false; + } + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + if (backCert.Init() != Success) { + return false; + } + const Input* qcStatementsInput(backCert.GetQCStatements()); + if (!qcStatementsInput) { + return false; + } + Reader qcStatements(*qcStatementsInput); + // QCStatements ::= SEQUENCE OF QCStatement + // QCStatement ::= SEQUENCE { + // statementId QC-STATEMENT.&Id({SupportedStatements}), + // statementInfo QC-STATEMENT.&Type + // ({SupportedStatements}{@statementId}) OPTIONAL } + // + // SupportedStatements QC-STATEMENT ::= { qcStatement-1,...} + bool foundQCComplianceStatement = false; + bool foundQCTypeStatementWithWebType = false; + mozilla::pkix::Result rv = + NestedOf(qcStatements, SEQUENCE, SEQUENCE, EmptyAllowed::No, + [&](Reader& qcStatementContents) { + Reader statementId; + mozilla::pkix::Result rv = ExpectTagAndGetValue( + qcStatementContents, OIDTag, statementId); + if (rv != Success) { + return rv; + } + if (statementId.MatchRest(id_etsi_qcs_QcCompliance)) { + foundQCComplianceStatement = true; + return End(qcStatementContents); + } + if (statementId.MatchRest(id_etsi_qcs_QcType)) { + Reader supportedStatementsContents; + rv = ExpectTagAndGetValue(qcStatementContents, SEQUENCE, + supportedStatementsContents); + if (rv != Success) { + return rv; + } + Reader supportedStatementId; + rv = ExpectTagAndGetValue(supportedStatementsContents, + OIDTag, supportedStatementId); + if (supportedStatementId.MatchRest(id_etsi_qct_web)) { + foundQCTypeStatementWithWebType = true; + } + rv = End(supportedStatementsContents); + if (rv != Success) { + return rv; + } + return End(qcStatementContents); + } + // Ignore the contents of unknown qcStatements. + qcStatementContents.SkipToEnd(); + return Success; + }); + if (rv != Success) { + return false; + } + return foundQCComplianceStatement && foundQCTypeStatementWithWebType; +} + +// For 1-QWACs, ETSI TS 119 411-5 V2.1.1 clause 6.1.2 ("Validation of QWACs") +// item 5 references clause 4.1.2, which references clause 4.1.1, which states +// that such certificates must have either the QEVCP-w or QNCP-w policy as +// specified in ETSI EN 319 411-2. +bool CertHas1QWACPolicy(const nsTArray<uint8_t>& certDER) { + using namespace mozilla::pkix::der; + + // QEVCP-w is itu-t(0) identified-organization(4) etsi(0) + // qualified-certificate-policies(194112) policy-identifiers(1) qcp-web (4) + // python DottedOIDToCode.py qevcp-w 0.4.0.194112.1.4 + static const uint8_t qevcp_w[] = {0x04, 0x00, 0x8b, 0xec, 0x40, 0x01, 0x04}; + + // QNCP-w is itu-t(0) identified-organization(4) etsi(0) + // qualified-certificate-policies(194112) policy-identifiers(1) qncp-web (5) + // python DottedOIDToCode.py qncp-w 0.4.0.194112.1.5 + static const uint8_t qncp_w[] = {0x04, 0x00, 0x8b, 0xec, 0x40, 0x01, 0x05}; + + Input cert; + if (cert.Init(certDER.Elements(), certDER.Length()) != Success) { + return false; + } + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + if (backCert.Init() != Success) { + return false; + } + const Input* certificatePoliciesInput(backCert.GetCertificatePolicies()); + if (!certificatePoliciesInput) { + return false; + } + Reader certificatePolicies(*certificatePoliciesInput); + // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + // PolicyInformation ::= SEQUENCE { + // policyIdentifier CertPolicyId, + // ... + // } + // CertPolicyId ::= OBJECT IDENTIFIER + bool found1QWACPolicy = false; + mozilla::pkix::Result rv = + NestedOf(certificatePolicies, SEQUENCE, SEQUENCE, EmptyAllowed::No, + [&](Reader& policyInformationContents) { + Reader policyIdentifier; + mozilla::pkix::Result rv = ExpectTagAndGetValue( + policyInformationContents, OIDTag, policyIdentifier); + if (rv != Success) { + return rv; + } + if (policyIdentifier.MatchRest(qevcp_w) || + policyIdentifier.MatchRest(qncp_w)) { + found1QWACPolicy = true; + } + return Success; + }); + if (rv != Success) { + return false; + } + return found1QWACPolicy; +} + +nsresult Verify1QWACTask::CalculateResult() { + mozilla::psm::QWACTrustDomain trustDomain(mCollectedCerts); + nsTArray<uint8_t> certDER; + nsresult rv = mCert->GetRawDER(certDER); + if (NS_FAILED(rv)) { + return rv; + } + if (!CertHasQWACSQCStatements(certDER)) { + return NS_OK; + } + if (!CertHas1QWACPolicy(certDER)) { + return NS_OK; + } + Input cert; + if (cert.Init(certDER.Elements(), certDER.Length()) != Success) { + return NS_ERROR_FAILURE; + } + if (BuildCertChain(trustDomain, cert, Now(), EndEntityOrCA::MustBeEndEntity, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, + nullptr) != Success) { + return NS_OK; + } + mVerifiedAs1QWAC = true; + return NS_OK; +} + +void Verify1QWACTask::CallCallback(nsresult rv) { + if (NS_FAILED(rv)) { + mPromise->MaybeReject(rv); + } else { + mPromise->MaybeResolve(mVerifiedAs1QWAC); + } +} + +NS_IMETHODIMP +nsNSSCertificateDB::AsyncVerify1QWAC( + nsIX509Cert* aCert, const nsTArray<RefPtr<nsIX509Cert>>& aCollectedCerts, + JSContext* aCx, mozilla::dom::Promise** aPromise) { + NS_ENSURE_ARG_POINTER(aCx); + + nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx); + if (!globalObject) { + return NS_ERROR_UNEXPECTED; + } + mozilla::ErrorResult result; + RefPtr<Promise> promise = Promise::Create(globalObject, result); + if (result.Failed()) { + return result.StealNSResult(); + } + + RefPtr<Verify1QWACTask> task( + new Verify1QWACTask(aCert, aCollectedCerts, promise)); + nsresult rv = task->Dispatch(); + if (NS_FAILED(rv)) { + return rv; + } + + promise.forget(aPromise); + return NS_OK; +} diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build @@ -4,7 +4,7 @@ # 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/. -DIRS += ["abridged_certs"] +DIRS += ["abridged_certs", "qwac_trust_anchors"] TEST_DIRS += ["tests"] @@ -104,7 +104,6 @@ UNIFIED_SOURCES += [ "md4.c", "nsCertOverrideService.cpp", "nsCertTree.cpp", - "nsClientAuthRemember.cpp", "nsNSSCallbacks.cpp", "nsNSSCertHelper.cpp", "nsNSSCertificate.cpp", @@ -131,6 +130,8 @@ UNIFIED_SOURCES += [ "PKCS11ModuleDB.cpp", "PSMRunnable.cpp", "PublicKeyPinningService.cpp", + "QWACs.cpp", + "QWACTrustDomain.cpp", "RootCertificateTelemetryUtils.cpp", "SecretDecoderRing.cpp", "SSLServerCertVerification.cpp", @@ -141,10 +142,22 @@ UNIFIED_SOURCES += [ "X509CertValidity.cpp", ] +if CONFIG["OS_ARCH"] == "Darwin": + # On macOS this file includes CoreFoundation.h, which contains definitions + # that conflict with headers included in other unified source files. We + # compile this one independently to prevent that interference. + SOURCES += [ + "nsClientAuthRemember.cpp", + ] +else: + UNIFIED_SOURCES += [ + "nsClientAuthRemember.cpp", + ] + if CONFIG["OS_ARCH"] == "WINNT": # On Windows this file includes ntsecapi.h, which contains definitions that - # conflict with headers included in remaining source files. We compile this - # one independently to prevent that interferance. + # conflict with headers included in other unified source files. We compile + # this one independently to prevent that interference. SOURCES += [ "OSReauthenticator.cpp", ] diff --git a/security/manager/ssl/nsIX509CertDB.idl b/security/manager/ssl/nsIX509CertDB.idl @@ -368,4 +368,13 @@ interface nsIX509CertDB : nsISupports { */ [must_use] nsIX509Cert getAndroidCertificateFromAlias(in AString alias); + + /** + * Given a certificate and a list of other certificates that may be useful in + * path building, asynchronously determines whether or not the certificate in + * question is a 1-QWAC ("qualified website authentication certificate") as + * per ETSI TS 119 411-5 and related standards. + */ + [implicit_jscontext] + Promise asyncVerify1QWAC(in nsIX509Cert cert, in Array<nsIX509Cert> collectedCerts); }; diff --git a/security/manager/ssl/qwac_trust_anchors/Cargo.toml b/security/manager/ssl/qwac_trust_anchors/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "qwac-trust-anchors" +version = "0.1.0" +edition = "2021" + +[dependencies] +static_prefs = { path = "../../../../modules/libpref/init/static_prefs" } +thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } + +[build-dependencies] +base64 = "0.22" +rsclientcerts-util = { path = "../rsclientcerts-util" } diff --git a/security/manager/ssl/qwac_trust_anchors/build.rs b/security/manager/ssl/qwac_trust_anchors/build.rs @@ -0,0 +1,122 @@ +/* -*- Mode: rust; rust-indent-offset: 4 -*- */ +/* 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/. */ + +use base64::prelude::BASE64_STANDARD; +use base64::Engine; +use std::cmp::Ordering; +use std::fs::File; +use std::io::{BufRead, BufReader, BufWriter, Write}; +use std::path::PathBuf; + +#[derive(Eq, PartialEq)] +struct TrustAnchor { + bytes: Vec<u8>, + subject: Vec<u8>, +} + +impl PartialOrd for TrustAnchor { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.subject.partial_cmp(&other.subject) + } +} + +impl TrustAnchor { + fn new(bytes: Vec<u8>) -> TrustAnchor { + let (_, _, subject) = + rsclientcerts_util::read_encoded_certificate_identifiers(bytes.as_slice()) + .expect("Couldn't decode certificate."); + TrustAnchor { bytes, subject } + } +} + +fn read_trust_anchors(trust_anchors_file: &File) -> Vec<TrustAnchor> { + let reader = BufReader::new(trust_anchors_file); + let mut maybe_current_trust_anchor: Option<Vec<String>> = None; + let mut trust_anchors = Vec::new(); + for line in reader.lines() { + let line = line.expect("Couldn't read contents of trust anchors file."); + match line.as_str() { + "-----BEGIN CERTIFICATE-----" => { + maybe_current_trust_anchor.replace(Vec::new()); + } + "-----END CERTIFICATE-----" => { + let current_trust_anchor = maybe_current_trust_anchor + .take() + .expect("END CERTIFICATE without BEGIN CERTIFICATE?"); + let base64 = current_trust_anchor.join(""); + let bytes = BASE64_STANDARD + .decode(base64) + .expect("Couldn't base64-decode trust anchor."); + let trust_anchor = TrustAnchor::new(bytes); + trust_anchors.push(trust_anchor); + } + _ => { + if let Some(current_trust_anchor) = maybe_current_trust_anchor.as_mut() { + current_trust_anchor.push(line); + } + } + } + } + trust_anchors.sort_by_cached_key(|trust_anchor| trust_anchor.subject.clone()); + trust_anchors +} + +fn emit_trust_anchors( + out: &mut dyn Write, + prefix: &str, + trust_anchors_filename: &str, +) -> std::io::Result<()> { + let trust_anchors_file = File::open(trust_anchors_filename)?; + let trust_anchors = read_trust_anchors(&trust_anchors_file); + for (index, trust_anchor) in trust_anchors.iter().enumerate() { + writeln!( + out, + "static {prefix}TRUST_ANCHOR_{index:0>4}_BYTES: &[u8] = &{:?};", + trust_anchor.bytes + )?; + writeln!( + out, + "static {prefix}TRUST_ANCHOR_{index:0>4}_SUBJECT_BYTES: &[u8] = &{:?};", + trust_anchor.subject + )?; + } + + writeln!( + out, + "pub (crate) static {prefix}TRUST_ANCHORS: [TrustAnchor; {num_trust_anchors}] = [", + num_trust_anchors = trust_anchors.len() + )?; + for index in 0..trust_anchors.len() { + writeln!(out, " TrustAnchor {{")?; + writeln!( + out, + " bytes: {prefix}TRUST_ANCHOR_{index:0>4}_BYTES," + )?; + writeln!( + out, + " subject: {prefix}TRUST_ANCHOR_{index:0>4}_SUBJECT_BYTES," + )?; + writeln!(out, " }},")?; + } + writeln!(out, "];")?; + Ok(()) +} + +fn main() -> std::io::Result<()> { + let trust_anchors = "trust_anchors.pem"; + let test_trust_anchors = "test_trust_anchors.pem"; + println!("cargo:rerun-if-changed={}", trust_anchors); + println!("cargo:rerun-if-changed={}", test_trust_anchors); + + let out_path = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR should be set in env.")); + let mut out = BufWriter::new( + File::create(out_path.join("trust_anchors.rs")).expect("Could not write trust_anchors.rs."), + ); + + emit_trust_anchors(&mut out, "", trust_anchors)?; + emit_trust_anchors(&mut out, "TEST_", test_trust_anchors)?; + + Ok(()) +} diff --git a/security/manager/ssl/qwac_trust_anchors/cbindgen.toml b/security/manager/ssl/qwac_trust_anchors/cbindgen.toml @@ -0,0 +1,14 @@ +header = """/* 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/. */""" +autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */ +""" +include_version = true +braces = "SameLine" +line_length = 100 +tab_width = 2 +language = "C++" +include_guard = "qwac_trust_anchors_ffi_generated_h" + +[export.rename] +"ThinVec" = "nsTArray" diff --git a/security/manager/ssl/qwac_trust_anchors/moz.build b/security/manager/ssl/qwac_trust_anchors/moz.build @@ -0,0 +1,11 @@ +# -*- 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/. + +if CONFIG["COMPILE_ENVIRONMENT"]: + CbindgenHeader( + "qwac_trust_anchors_ffi_generated.h", + inputs=["/security/manager/ssl/qwac_trust_anchors"], + ) diff --git a/security/manager/ssl/qwac_trust_anchors/src/lib.rs b/security/manager/ssl/qwac_trust_anchors/src/lib.rs @@ -0,0 +1,62 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +mod trust_anchors; + +use thin_vec::ThinVec; +use trust_anchors::{TrustAnchor, TEST_TRUST_ANCHORS, TRUST_ANCHORS}; + +fn trust_anchors_with_subject_from<'a>( + subject: &[u8], + trust_anchor_list: &'static [TrustAnchor], +) -> Box<dyn Iterator<Item = &'static TrustAnchor>> { + let Ok(index) = trust_anchor_list.binary_search_by_key(&subject, |r| &r.subject()) else { + return Box::new(std::iter::empty::<&'static TrustAnchor>()); + }; + + // binary search returned a matching index, but maybe not the smallest + let mut min = index; + while min > 0 && subject.eq(trust_anchor_list[min - 1].subject()) { + min -= 1; + } + + // ... and maybe not the largest. + let mut max = index; + while max < trust_anchor_list.len() - 1 && subject.eq(trust_anchor_list[max + 1].subject()) { + max += 1; + } + Box::new(trust_anchor_list.iter().take(max + 1).skip(min)) +} + +#[no_mangle] +pub extern "C" fn find_qwac_trust_anchors_by_subject( + subject: &ThinVec<u8>, + trust_anchors_out: &mut ThinVec<ThinVec<u8>>, +) { + trust_anchors_out.clear(); + for trust_anchor in trust_anchors_with_subject_from(subject, &TRUST_ANCHORS) { + trust_anchors_out.push(trust_anchor.bytes().into()); + } + if static_prefs::pref!("security.qwacs.enable_test_trust_anchors") { + for trust_anchor in trust_anchors_with_subject_from(subject, &TEST_TRUST_ANCHORS) { + trust_anchors_out.push(trust_anchor.bytes().into()); + } + } +} + +#[no_mangle] +pub extern "C" fn is_qwac_trust_anchor(subject: &ThinVec<u8>, certificate: &ThinVec<u8>) -> bool { + if trust_anchors_with_subject_from(subject, &TRUST_ANCHORS) + .find(|trust_anchor| trust_anchor.bytes() == certificate.as_slice()) + .is_some() + { + return true; + } + if static_prefs::pref!("security.qwacs.enable_test_trust_anchors") { + return trust_anchors_with_subject_from(subject, &TEST_TRUST_ANCHORS) + .find(|trust_anchor| trust_anchor.bytes() == certificate.as_slice()) + .is_some(); + } + false +} diff --git a/security/manager/ssl/qwac_trust_anchors/src/trust_anchors.rs b/security/manager/ssl/qwac_trust_anchors/src/trust_anchors.rs @@ -0,0 +1,21 @@ +/* -*- Mode: rust; rust-indent-offset: 4 -*- */ +/* 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/. */ + +pub(crate) struct TrustAnchor { + bytes: &'static [u8], + subject: &'static [u8], +} + +impl TrustAnchor { + pub fn bytes(&self) -> &'static [u8] { + self.bytes + } + + pub fn subject(&self) -> &'static [u8] { + self.subject + } +} + +include!(concat!(env!("OUT_DIR"), "/trust_anchors.rs")); diff --git a/security/manager/ssl/qwac_trust_anchors/test_trust_anchors.pem b/security/manager/ssl/qwac_trust_anchors/test_trust_anchors.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC0zCCAbugAwIBAgIUbRl0jsaZB1HOw2TSFqJE/hUf4x8wDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjASMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB +xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT +qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5 +kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS +wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk +BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME +BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAHldkZY/n8EPM +hmKw7mLxMd66agv+HCe46PCv75LkAPh3CS7ovLFuU5C8IJUNMMRT2NXOtXwm/COb +bMPqk/5+jRnBxkCdRCEerIp6tPBmcxci+bGZFdI+Xnq+/4vf9gcISphlrfCukbve +7t/WNPIXMfu4yZnkMdQsoYWi2dE02GZNuaGj1oHTdWVgN/nRWtCo/cApupRN77GV +nN1uE1fB07PQU75meGwsQOu6HMgVz2kQD9nZhfaisSJlGElYUGZmE2ySPr+OhTmT +zHFD2cq6WQGCyWCH8cZY2a5pylOsG4auHwKCgbdSELMnIkUtWtmjGa6/2duQKOcr +XHn6ZozclA== +-----END CERTIFICATE----- diff --git a/security/manager/ssl/qwac_trust_anchors/test_trust_anchors.pem.certspec b/security/manager/ssl/qwac_trust_anchors/test_trust_anchors.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Test CA +extension:basicConstraints:cA, +extension:keyUsage:cRLSign,keyCertSign diff --git a/security/manager/ssl/qwac_trust_anchors/trust_anchors.pem b/security/manager/ssl/qwac_trust_anchors/trust_anchors.pem diff --git a/security/manager/ssl/tests/unit/test_qwacs.js b/security/manager/ssl/tests/unit/test_qwacs.js @@ -0,0 +1,50 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. + +"use strict"; + +do_get_profile(); // must be called before getting nsIX509CertDB +const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB +); + +async function verify_1_qwacs(filename, expectSuccess, extraCertNames = []) { + let cert = constructCertFromFile(filename); + let result = await certdb.asyncVerify1QWAC( + cert, + extraCertNames.map(filename => constructCertFromFile(filename)) + ); + equal( + result, + expectSuccess, + `${filename} ${expectSuccess ? "should" : "should not"} verify as 1-QWAC` + ); +} + +add_task(async function test_verify_1_qwacs() { + // By default, the QWACs test trust anchors are not used. + await verify_1_qwacs("test_qwacs/1-qwac.pem", false); + await verify_1_qwacs("test_qwacs/1-qwac-qevcpw.pem", false); + + Services.prefs.setBoolPref("security.qwacs.enable_test_trust_anchors", true); + + await verify_1_qwacs("test_qwacs/1-qwac.pem", true); + await verify_1_qwacs("test_qwacs/1-qwac-qevcpw.pem", true); + + await verify_1_qwacs("test_qwacs/1-qwac-other-optional-qcs.pem", true); + + // One or more intermediates may be necessary for path building. + await verify_1_qwacs("test_qwacs/1-qwac-via-intermediate.pem", false); + await verify_1_qwacs("test_qwacs/1-qwac-via-intermediate.pem", true, [ + "test_qwacs/test-int.pem", + ]); + + await verify_1_qwacs("test_qwacs/empty-qc-type-statement.pem", false); + await verify_1_qwacs("test_qwacs/missing-qc-type-statement.pem", false); + await verify_1_qwacs("test_qwacs/missing-qcs-compliance.pem", false); + await verify_1_qwacs("test_qwacs/wrong-qc-type.pem", false); + await verify_1_qwacs("test_qwacs/no-1-qwac-policies.pem", false); + await verify_1_qwacs("test_qwacs/no-policies.pem", false); +}); diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-other-optional-qcs.pem b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-other-optional-qcs.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSTCCAjGgAwIBAgIUaZ76rjPYP4T+dytmDJ7/oGjMu6wwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAyMTAwLgYDVQQDDCcxLVFXQUMgd2l0aCBvdGhlciBvcHRpb25h +bCBxY1N0YXRlbWVudHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6 +iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr +4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP +8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI +Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ +77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J +I/pyUcQx1QOs2hgKNe2NAgMBAAGjczBxMFkGCCsGAQUFBwEDBE0wSzAqBhIrBgEE +AetJhRqFGoUaAYN0CQEwFAYSKwYBBAHrSYUahRqFGgGDdAkBMAgGBgQAjkYBATAT +BgYEAI5GAQYwCQYHBACORgEGAzAUBgNVHSAEDTALMAkGBwQAi+xAAQUwDQYJKoZI +hvcNAQELBQADggEBAIksotr4cBkTAcLiJZltl8K0DpQE2uCXJmSuoRbvM+7dzUjQ +yT5mnNpY86+itom5xi+kVgobctZNB7qKNCelNLnF5xcQdvnSYfJgvHQGRZBVvtJ+ +3HprWWMFl3h2oADnyGftOGNtwFOSBja68uVY5R/hHyI518lxNcp6ON3BBamJMrTt +FLmEqCE9ixFlUY12XIc0Vw828hvP3WDkzrNqRTuSZwnM9J0wUO6te8PRIlVodsJU +T7WfRa/7/MLed5SkHo7HfUOgg/LGFPKWRTZCbbmf0T0r1nE5T8NgauqLaJQKCAH3 +xsWkQK2Jo+1hVmW5QKqRt68Cv8YoN/lPzWkzY+k= +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-other-optional-qcs.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-other-optional-qcs.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:1-QWAC with other optional qcStatements +extension:qcStatements:1.3.6.1.4.1.13769.666.666.666.1.500.9.1:1.3.6.1.4.1.13769.666.666.666.1.500.9.1,0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-qevcpw.pem b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-qevcpw.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe6gAwIBAgIUG1MjuksFls8P6XUPgmjbaz1pu8QwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAbMRkwFwYDVQQDDBAxLVFXQUMgKFFFVkNQLXcpMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVK +tOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7N +Q/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39Zgsr +sCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxs +l62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYl +nauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo0cw +RTAtBggrBgEFBQcBAwQhMB8wCAYGBACORgEBMBMGBgQAjkYBBjAJBgcEAI5GAQYD +MBQGA1UdIAQNMAswCQYHBACL7EABBDANBgkqhkiG9w0BAQsFAAOCAQEACEmrysb7 +7MPsWOwPKr8rbQzmEo149mKXrSMi9tDJk+DlPrkcGo8jz2zLMXEADbFoCBBv1YGO +41ro2r4MVHyH2f/AGk18h5aYL2jQTufLTCcuoPo7B078jMmAdivOICdzarjuMZrR +9yU2+6JHTpEUtnquSw7td/+dJqaVtT8wRe9GdnqPWnVdkfWkoNdY72gZ+4dWihnn +SacQYj6gL/OW8EGYl8JSL+c+lD6FsPRW8UH4UkgXQPzFSMlbq7QyLOi0EU9zqIxE +2LMUl3WXBi2k4ecVHI0gJGiXg1m8MxhToRDftiq79Tw+elqJwj1ychUNMERNAy0X +t2rLLMYA5DIieA== +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-qevcpw.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-qevcpw.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:1-QWAC (QEVCP-w) +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.4 diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-via-intermediate.pem b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-via-intermediate.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHDCCAgSgAwIBAgIUN3k8AnTEt+DKd7zpHnxNCcNWrj4wDQYJKoZIhvcNAQEL +BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAyMzExMjgwMDAw +MDBaGA8yMDI2MDIwNTAwMDAwMFowJzElMCMGA1UEAwwcMS1RV0FDIHZpYSBUZXN0 +IEludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI +UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi +r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x +fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD +7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv +uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj ++nJRxDHVA6zaGAo17Y0CAwEAAaNHMEUwLQYIKwYBBQUHAQMEITAfMAgGBgQAjkYB +ATATBgYEAI5GAQYwCQYHBACORgEGAzAUBgNVHSAEDTALMAkGBwQAi+xAAQUwDQYJ +KoZIhvcNAQELBQADggEBADogfTffZJ+lbfrIb4XjWIgW9/N8wfTK0wAkiynauW+K +Kz/DCwZtxof9WDoQnYNM8w4aBMA3EdCoHqbU94HHtRqLSsysuqrM4/FiMhOrdJyB +I7JOMODuCPTC937ec9DoHO17YvgM+Lr9ou+tixPa9ADTye7FmTq/tvWa9+qJWB1L +GNoMaDsOBiZRKkanNdsrN0KdidcMCg/5PWGf/r93VZJBa+O3UrJjqn+QbBrZxMUa +2UhOOdUN1ERAj1CC/dKnswB4gqNaZvLSvN4WhoDxgfVSe9tq8pbYwLfb/RwNOheH +TMoBXC6RWm6gucuiLbsCknjDKfRIF7q+R9Ewun5mxLs= +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac-via-intermediate.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/1-qwac-via-intermediate.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test Intermediate +subject:1-QWAC via Test Intermediate +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac.pem b/security/manager/ssl/tests/unit/test_qwacs/1-qwac.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC/DCCAeSgAwIBAgIUG6a58w0ViFDNph9sc09nWvAw2s8wDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjARMQ8wDQYDVQQDDAYxLVFXQUMwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH +Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr +IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ +sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA +dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE +LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjRzBFMC0GCCsGAQUF +BwEDBCEwHzAIBgYEAI5GAQEwEwYGBACORgEGMAkGBwQAjkYBBgMwFAYDVR0gBA0w +CzAJBgcEAIvsQAEFMA0GCSqGSIb3DQEBCwUAA4IBAQBdFd+lIMnM3+kH30yHcfQE +446VQxv+ZpjAixYn/yGl2U5feVTpuDC2yYlV5ehbfvefpl/NqCEwQNU+3DOslp31 +aAJIE4RsHbS/aXXVVhvaJJFpP16nzCMTLmtamUESMRzOYYbWMCsCpvy49ygc+C2Q +qKAew6UFYaBwtlH01U5gkbF2SNiuTKcw8Eb32jLoILn6uypEd7QVSRBvnRRVM+ZW +qgAP3/WPuSBVuBnGgD7xOzPyjti/Gyvv7t6eO54mBhZ4076WTda5E78GwNgS+Vnz +NgCwmmGf+pZsuFfMjF+rvjftOUCQHJkl+bEF+20dZF+4WbrXYqFjFZc09j9cjMrm +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/1-qwac.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/1-qwac.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:1-QWAC +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/empty-qc-type-statement.pem b/security/manager/ssl/tests/unit/test_qwacs/empty-qc-type-statement.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIUG/8itJaXGtCPAToj0JqD7QsK42wwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAiMSAwHgYDVQQDDBdFbXB0eSBRQyBUeXBlIFN0YXRlbWVudDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9 +PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3 +HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg +Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7 +EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK +lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C +AwEAAaM8MDowIgYIKwYBBQUHAQMEFjAUMAgGBgQAjkYBATAIBgYEAI5GAQYwFAYD +VR0gBA0wCzAJBgcEAIvsQAEFMA0GCSqGSIb3DQEBCwUAA4IBAQAo2MYcle2cwk6W +pTVzxh95bQ1yxBo6o6sDXekk+cuf8ym4vjBugJ2/WPC8kWrctlOqEVeuLJJ7Q2cS +6N4Sp+03jms5lJ98T6sl+OinunVXP+Uu1CTPxhxPWCvVZAdWh5HeNJ64XT0xcNEF +f+IMF13H74Od4rvKv0ukvJxI+Cgl72yg1eieSQ1NaJOuATUQMT/v+7MMdGTwxVMe +ZH2iXs4+3xulnBQmCGIJ6fVj3F3KLqkBU0/2pgYf4j/xT4FbvETVaE/VVqhM3tsR +Pue3QSeu9DvjbZCaMP1yWrEM/IOnDk6OpmMzgRLXAHIKe6yzB9EXEaal82tllc4G +UbFJrkB0 +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/empty-qc-type-statement.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/empty-qc-type-statement.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Empty QC Type Statement +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/missing-qc-type-statement.pem b/security/manager/ssl/tests/unit/test_qwacs/missing-qc-type-statement.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIUEzwCkT4XVYVO5bTzSiEpxRsgMMowDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAkMSIwIAYDVQQDDBlNaXNzaW5nIFFDIFR5cGUgU3RhdGVtZW50 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2 +ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF +h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n +cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv +OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj +tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt +jQIDAQABo1MwUTA5BggrBgEFBQcBAwQtMCswCAYGBACORgEBMB8GEisGAQQB60mF +GoUahRoBg3QJATAJBgcEAI5GAQYDMBQGA1UdIAQNMAswCQYHBACL7EABBTANBgkq +hkiG9w0BAQsFAAOCAQEAXxAyC4SBAqDdOUTXpg4dA5CAzROKiraYZydzpj+j73OQ +SsNikXrW2BUVfYxzra240lwDq6eav5Vf/c8BrKo7IdxQMnce9/gqzIc8JI3GbVok +QObdWlqRYeb2KWmk9bwY5JnM9YLRKzhbM6alzg11auqAFGxeEaU7VPG/WhEzcqF3 +mQLIpP4hwUHkyEXd3JeauttqaEEj/WEngr4ao5vVbDkJR1hW8y/wNBseosGMU6ic +LEVt/0vYWqol6RLN2zXQmsRVA6Mc2FS+blmR/mPInXyS6cSxWPHzWrYqybvWmK8z +Cp975qaVck+1IYHRvyOfMNw3TWnZj5lP/eQgqOCRyw== +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/missing-qc-type-statement.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/missing-qc-type-statement.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Missing QC Type Statement +extension:qcStatements:0.4.0.1862.1.1,1.3.6.1.4.1.13769.666.666.666.1.500.9.1:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/missing-qcs-compliance.pem b/security/manager/ssl/tests/unit/test_qwacs/missing-qcs-compliance.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIjCCAgqgAwIBAgIUaZvo2Y3hVFCeBwu8hzBI0oJHtNswDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjArMSkwJwYDVQQDDCBNaXNzaW5nIFFDUyBDb21wbGlhbmNlIFN0 +YXRlbWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW +Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk +cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT +AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3 +ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh +s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV +A6zaGAo17Y0CAwEAAaNTMFEwOQYIKwYBBQUHAQMELTArMBQGEisGAQQB60mFGoUa +hRoBg3QJATATBgYEAI5GAQYwCQYHBACORgEGAzAUBgNVHSAEDTALMAkGBwQAi+xA +AQUwDQYJKoZIhvcNAQELBQADggEBADcufbfoLrTniJeqUAgaRUevqbbl4J+w9tq0 +xcwjN1+hIrfCORT0gvI88Vl01U7cqoAwne15coe0oPXdcoLqY4iEtYuqi7wpOiOo +mzK3pUdRaM5XXbver5tm7cpQ7PtaSozYqXYxahSqqdVbmhwjzXCqa8ZURIK2optB +xly1CnGsd+3oiHVz6LBJV2d+sk7OAucwhhcHgO7XjRYiCaOeOFtmA1ZQ+4hlSifU +/ITGU8QC+P0h3NvJmTjHlK8yGn0z4c9CbHvAP2FbqXAbP1OlD+IAyWF4GwGrBAEq +GIXL79Q11oVlBSB9QmB9njOUF3keLA7Hcs8gqqph0idaTe8Zp5w= +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/missing-qcs-compliance.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/missing-qcs-compliance.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Missing QCS Compliance Statement +extension:qcStatements:1.3.6.1.4.1.13769.666.666.666.1.500.9.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/test_qwacs/no-1-qwac-policies.pem b/security/manager/ssl/tests/unit/test_qwacs/no-1-qwac-policies.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBzCCAe+gAwIBAgIUGzv6BXRxTNNy3E3lbxuIjV4kzsgwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjARMQ8wDQYDVQQDDAYxLVFXQUMwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH +Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr +IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ +sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA +dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE +LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjUjBQMC0GCCsGAQUF +BwEDBCEwHzAIBgYEAI5GAQEwEwYGBACORgEGMAkGBwQAjkYBBgMwHwYDVR0gBBgw +FjAUBhIrBgEEAetJhRqFGoUaAYN0CQEwDQYJKoZIhvcNAQELBQADggEBAEQ1JL7Q +k6l9ZqMbskPdZBZVedIgRBcAC2wVlHkjdMSnmenD3GcXtAh2XVJRSoh2tl9IutEJ +lVJReisdoXFlmdY8QO4G9vuNvMIIpU/6CeKGaFNYIjquo1q+FQVsoWzFM3TZLA36 +xDWTokhf3EFYuFcvNpn5xFwtiKY9OT94wyREVOdSvPV+AyheGEOD3Bqym0Du8644 +ypJzKTqxVz2W5j/lhNLY2J7+YfjqgaTyq73zdFjl+4UEzLeXIJRc19HysBNFeDd+ +I1loTkXyTOGv/mpmqAY+yi69JIw6nss9jkckvuyg/XPQX0ilBVNUUD0zWVp/vLou +ZufjNWk/k0Kq6nk= +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/no-1-qwac-policies.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/no-1-qwac-policies.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:1-QWAC +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 +extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1 diff --git a/security/manager/ssl/tests/unit/test_qwacs/no-policies.pem b/security/manager/ssl/tests/unit/test_qwacs/no-policies.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6zCCAdOgAwIBAgIUQfGUxOhuv1LSHMj4LnmvsWzMUGYwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAWMRQwEgYDVQQDDAtObyBQb2xpY2llczCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX +bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ +OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9 +uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb +t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO +NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMxMC8wLQYI +KwYBBQUHAQMEITAfMAgGBgQAjkYBATATBgYEAI5GAQYwCQYHBACORgEGAzANBgkq +hkiG9w0BAQsFAAOCAQEAdBHBIcFca3o2l8/guLJSx+m+8Vt5940Lt5oMxZM2P2SJ +nrBM+i6kGZ0vkXUICF9VoQQRpxCj3quYrt2c0KjjGjXRV6T5hFF7G3SSFzpTXX5V +jTIHrBEd09/g7juhY7KIxAbHYJecIC+PQNMJxCVzrYcao07w5QHOeSbWSjB+UDa7 +emVXrcs7UNk2wEYzGTnkdhTu40DbFRIou/wRBKOmiAYny4/lak/XnwLgX9UOV6Am +Boz3pOzY3w4iNHYLGqOldzhHwuCDHWDYYFPpNmA45yTpWaoGr6Gd4h7/bbwqOiXO +YotZEvSKufwkeBoKfdWRS38TVJnIyDs+2rgjmqGRWQ== +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/no-policies.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/no-policies.pem.certspec @@ -0,0 +1,3 @@ +issuer:Test CA +subject:No Policies +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.3 diff --git a/security/manager/ssl/tests/unit/test_qwacs/test-int.pem b/security/manager/ssl/tests/unit/test_qwacs/test-int.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC3TCCAcWgAwIBAgIUAxPrsRjtbFinLUfRzhtR8EeYh4YwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1 +SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+ +zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYL +K7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwc +bJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibW +JZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMd +MBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEB +AKMLQxpBsyCNjuzQZY6Y8dJVzmNwfa0mvzDCLI6ltjK3X4pzz7tCb+hXH+Z3lhf+ +t5N4eSMnXgobxb3tya8/2c+3kp6oxx+BwyvvA7zLPrTgHed8/G8z9tpZJrJxTcOB +83fDkvTE9/49KIffeSF7I/IedybWjqO93IZMqVVB5xfbD3WoYCe6SipiUqvVB3oy +4PBC5ONA1ZFGwqj7/6vgmgHukWIc6GogczKdLIR/Wu5laV8Wug+xP/GUUcuAOIuY +hk6WMVRikq8g+wf2FG0i0NcGDOAK0Z/1nFvKpIJomZ8Q9NYVs0tfhoSLPwtd7cDT +XYDC9Gn4ncbAQIRIAnNm2Ew= +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/test-int.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/test-int.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Test Intermediate +extension:basicConstraints:cA, +extension:keyUsage:cRLSign,keyCertSign diff --git a/security/manager/ssl/tests/unit/test_qwacs/wrong-qc-type.pem b/security/manager/ssl/tests/unit/test_qwacs/wrong-qc-type.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIUYegOCXCmPzhM1hjEkCosdeKOs2UwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDIzMTEyODAwMDAwMFoYDzIwMjYw +MjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1Xcm9uZyBRQyBUeXBlMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1 +aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we +adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS +pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W +YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR +CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo0cwRTAt +BggrBgEFBQcBAwQhMB8wCAYGBACORgEBMBMGBgQAjkYBBjAJBgcEAI5GAQYCMBQG +A1UdIAQNMAswCQYHBACL7EABBTANBgkqhkiG9w0BAQsFAAOCAQEAClyA/q0iAF1j +acwk1apKyp2b5O+fMJhPtx3ulZXkMXexJaF+f9uIgpYniHkRSYaTaYEPbCk8dWmq +NNSMbIhTJAwUloFzcxalzkSg3l9yDg/pEinmw0d+C4eUF/gKeiZ9nPY2hxOBDZHl +Lo0U7UOeqWlI92x+OpmbID+TR8ZAgl3tD8EPsl2Z32PzYpx/MPwHy1gAclgUnJz9 +4NTYqMra1KLYNzvPHB5kGXwewxIpWZF2Fj+6uP4aXeKyVsEYNUDAL60JyZA5UCQh +JDCPADRfuaSpLNWCPZJH5uSrovrUDTihT12SAdaIB/a0K4FeWL3IZJFb2OJHnr57 +oicH3X8XsA== +-----END CERTIFICATE----- diff --git a/security/manager/ssl/tests/unit/test_qwacs/wrong-qc-type.pem.certspec b/security/manager/ssl/tests/unit/test_qwacs/wrong-qc-type.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Wrong QC Type +extension:qcStatements:0.4.0.1862.1.1,0.4.0.1862.1.6:0.4.0.1862.1.6.2 +extension:certificatePolicies:0.4.0.194112.1.5 diff --git a/security/manager/ssl/tests/unit/xpcshell.toml b/security/manager/ssl/tests/unit/xpcshell.toml @@ -37,6 +37,7 @@ support-files = [ "test_name_constraints/**", "test_ocsp_url/**", "test_onecrl/**", + "test_qwacs/**", "test_sdr_preexisting/**", "test_sdr_preexisting_with_password/**", "test_self_signed_certs/**", @@ -270,6 +271,8 @@ run-if = [ ["test_pinning.js"] run-sequentially = ["true"] # hardcoded ports +["test_qwacs.js"] + ["test_sdr.js"] ["test_sdr_preexisting.js"] diff --git a/security/manager/tools/pycert.py b/security/manager/tools/pycert.py @@ -38,6 +38,7 @@ nsCertType:sslServer TLSFeature:[<TLSFeature>,...] embeddedSCTList:[<key specification>:<YYYYMMDD>[:<leaf index>],...] delegationUsage: +qcStatements:[<statement OID[:info OID]>,...] Where: [] indicates an optional field or component of a field @@ -513,6 +514,8 @@ class Certificate: self.savedEmbeddedSCTListData = (value, critical) elif extensionType == "delegationUsage": self.addDelegationUsage(critical) + elif extensionType == "qcStatements": + self.addQCStatements(value, critical) else: raise UnknownExtensionTypeError(extensionType) @@ -723,6 +726,29 @@ class Certificate: critical, ) + def addQCStatements(self, qcStatements, critical): + sequence = univ.Sequence() + for pos, qcStatement in enumerate(qcStatements.split(",")): + parts = qcStatement.split(":") + statementID = parts[0] + statementInfo = None + if len(parts) > 1: + statementInfo = parts[1] + qcStatementSequence = univ.Sequence() + qcStatementSequence.setComponentByPosition( + 0, univ.ObjectIdentifier(statementID) + ) + if statementInfo: + statementInfoSequence = univ.Sequence() + statementInfoSequence.setComponentByPosition( + 0, univ.ObjectIdentifier(statementInfo) + ) + qcStatementSequence.setComponentByPosition(1, statementInfoSequence) + sequence.setComponentByPosition(pos, qcStatementSequence) + self.addExtension( + univ.ObjectIdentifier("1.3.6.1.5.5.7.1.3"), sequence, critical + ) + def getVersion(self): return rfc2459.Version(self.versionValue).subtype( explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0) diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml @@ -82,6 +82,7 @@ crypto_hash = { path = "../../../../security/manager/ssl/crypto_hash" } data_storage = { path = "../../../../security/manager/ssl/data_storage" } ipcclientcerts = { path = "../../../../security/manager/ssl/ipcclientcerts" } mls_gk = { path = "../../../../security/mls/mls_gk" } +qwac-trust-anchors = { path = "../../../../security/manager/ssl/qwac_trust_anchors" } trust-anchors = { path = "../../../../security/manager/ssl/trust_anchors" } signature_cache = { path = "../../../../security/certverifier/signature_cache" } diff --git a/toolkit/library/rust/shared/lib.rs b/toolkit/library/rust/shared/lib.rs @@ -86,6 +86,7 @@ extern crate l10nregistry_ffi; extern crate localization_ffi; extern crate ipcclientcerts; +extern crate qwac_trust_anchors; extern crate trust_anchors; #[cfg(any(