From 61cba1c84d4402fd11ae1608dc7f5209a9575dca Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Mon, 29 Jan 2024 18:28:00 +0000 Subject: [PATCH] Bug 1876435 - gather telemetry on the sources of issuer certificates r=jschanck Differential Revision: https://phabricator.services.mozilla.com/D199599 --- security/certverifier/CertVerifier.cpp | 28 +++- security/certverifier/CertVerifier.h | 18 +- .../certverifier/NSSCertDBTrustDomain.cpp | 156 +++++++++--------- security/certverifier/NSSCertDBTrustDomain.h | 15 ++ .../manager/ssl/SSLServerCertVerification.cpp | 31 +++- security/manager/ssl/metrics.yaml | 76 ++++++++- 6 files changed, 236 insertions(+), 88 deletions(-) diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index 8cda270dc59d..e124bae17116 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -452,7 +452,8 @@ Result CertVerifier::VerifyCert( /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, /*optional out*/ CertificateTransparencyInfo* ctInfo, /*optional out*/ bool* isBuiltChainRootBuiltInRoot, - /*optional out*/ bool* madeOCSPRequests) { + /*optional out*/ bool* madeOCSPRequests, + /*optional out*/ IssuerSources* issuerSources) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n")); MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV)); @@ -494,6 +495,10 @@ Result CertVerifier::VerifyCert( *madeOCSPRequests = false; } + if (issuerSources) { + issuerSources->clear(); + } + Input certDER; Result rv = certDER.Init(certBytes.Elements(), certBytes.Length()); if (rv != Success) { @@ -584,6 +589,9 @@ Result CertVerifier::VerifyCert( *madeOCSPRequests |= trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; } + if (issuerSources) { + *issuerSources = trustDomain.GetIssuerSources(); + } if (rv == Success) { rv = VerifyCertificateTransparencyPolicy( trustDomain, builtChain, sctsFromTLSInput, time, ctInfo); @@ -643,6 +651,9 @@ Result CertVerifier::VerifyCert( *madeOCSPRequests |= trustDomain.GetOCSPFetchStatus() == OCSPFetchStatus::Fetched; } + if (issuerSources) { + *issuerSources = trustDomain.GetIssuerSources(); + } if (rv != Success && !IsFatalError(rv) && rv != Result::ERROR_REVOKED_CERTIFICATE && trustDomain.GetIsErrorDueToDistrustedCAPolicy()) { @@ -807,7 +818,8 @@ Result CertVerifier::VerifySSLServerCert( /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo, /*optional out*/ CertificateTransparencyInfo* ctInfo, /*optional out*/ bool* isBuiltChainRootBuiltInRoot, - /*optional out*/ bool* madeOCSPRequests) { + /*optional out*/ bool* madeOCSPRequests, + /*optional out*/ IssuerSources* issuerSources) { // XXX: MOZ_ASSERT(pinarg); MOZ_ASSERT(!hostname.IsEmpty()); @@ -832,12 +844,12 @@ Result CertVerifier::VerifySSLServerCert( return rv; } bool isBuiltChainRootBuiltInRootLocal; - rv = VerifyCert(peerCertBytes, certificateUsageSSLServer, time, pinarg, - PromiseFlatCString(hostname).get(), builtChain, flags, - extraCertificates, stapledOCSPResponse, sctsFromTLS, - originAttributes, evStatus, ocspStaplingStatus, keySizeStatus, - pinningTelemetryInfo, ctInfo, - &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests); + rv = VerifyCert( + peerCertBytes, certificateUsageSSLServer, time, pinarg, + PromiseFlatCString(hostname).get(), builtChain, flags, extraCertificates, + stapledOCSPResponse, sctsFromTLS, originAttributes, evStatus, + ocspStaplingStatus, keySizeStatus, pinningTelemetryInfo, ctInfo, + &isBuiltChainRootBuiltInRootLocal, madeOCSPRequests, issuerSources); if (rv != Success) { // we don't use the certificate for path building, so this parameter doesn't // matter diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index 2ecead87c1ee..ddf42108ace3 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -13,6 +13,7 @@ #include "OCSPCache.h" #include "RootCertificateTelemetryUtils.h" #include "ScopedNSSTypes.h" +#include "mozilla/EnumSet.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" @@ -70,6 +71,17 @@ enum class CRLiteMode { enum class NetscapeStepUpPolicy : uint32_t; +// Describes the source of the associated issuer. +enum class IssuerSource { + TLSHandshake, // included by the peer in the TLS handshake + PreloadedIntermediates, // a preloaded intermediate (via remote settings) + ThirdPartyCertificates, // a third-party certificate gleaned from the OS + NSSCertDB, // a certificate found in the profile's NSS certificate DB + BuiltInRootsModule, // a root from the built-in roots module +}; + +using IssuerSources = EnumSet; + class PinningTelemetryInfo { public: PinningTelemetryInfo() @@ -163,7 +175,8 @@ class CertVerifier { /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, - /*optional out*/ bool* madeOCSPRequests = nullptr); + /*optional out*/ bool* madeOCSPRequests = nullptr, + /*optional out*/ IssuerSources* = nullptr); mozilla::pkix::Result VerifySSLServerCert( const nsTArray& peerCert, mozilla::pkix::Time time, void* pinarg, @@ -184,7 +197,8 @@ class CertVerifier { /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, - /*optional out*/ bool* madeOCSPRequests = nullptr); + /*optional out*/ bool* madeOCSPRequests = nullptr, + /*optional out*/ IssuerSources* = nullptr); enum OcspDownloadConfig { ocspOff = 0, ocspOn = 1, ocspEVOnly = 2 }; enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict }; diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp index aceb201bd93b..02a005f8b68a 100644 --- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -137,8 +137,7 @@ static void FindRootsWithSubject(UniqueSECMODModule& rootsModule, // 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) { +bool NSSCertDBTrustDomain::ShouldSkipSelfSignedNonTrustAnchor(Input certDER) { BackCert cert(certDER, EndEntityOrCA::MustBeCA, nullptr); if (cert.Init() != Success) { return false; // turn any failures into "don't skip trying this cert" @@ -148,8 +147,8 @@ static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain, return false; } TrustLevel trust; - if (trustDomain.GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, - certDER, trust) != Success) { + if (GetCertTrust(EndEntityOrCA::MustBeCA, CertPolicyId::anyPolicy, certDER, + trust) != Success) { return false; } // If the trust for this certificate is anything other than "inherit", we want @@ -157,7 +156,7 @@ static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain, if (trust != TrustLevel::InheritsTrust) { return false; } - if (VerifySignedData(trustDomain, cert.GetSignedData(), + if (VerifySignedData(*this, cert.GetSignedData(), cert.GetSubjectPublicKeyInfo()) != Success) { return false; } @@ -166,24 +165,25 @@ static bool ShouldSkipSelfSignedNonTrustAnchor(TrustDomain& trustDomain, return true; } -static Result CheckCandidates(TrustDomain& trustDomain, - TrustDomain::IssuerChecker& checker, - nsTArray& candidates, - Input* nameConstraintsInputPtr, bool& keepGoing) { - for (Input candidate : candidates) { +Result NSSCertDBTrustDomain::CheckCandidates( + IssuerChecker& checker, nsTArray& candidates, + Input* nameConstraintsInputPtr, bool& keepGoing) { + for (const auto& candidate : candidates) { // Stop path building if the program is shutting down. if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { keepGoing = false; return Success; } - if (ShouldSkipSelfSignedNonTrustAnchor(trustDomain, candidate)) { + if (ShouldSkipSelfSignedNonTrustAnchor(candidate.mDER)) { continue; } - Result rv = checker.Check(candidate, nameConstraintsInputPtr, keepGoing); + Result rv = + checker.Check(candidate.mDER, nameConstraintsInputPtr, keepGoing); if (rv != Success) { return rv; } if (!keepGoing) { + mIssuerSources += candidate.mIssuerSource; return Success; } } @@ -212,29 +212,8 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, // First try all relevant certificates known to Gecko, which avoids calling // CERT_CreateSubjectCertList, because that can be expensive. - nsTArray geckoRootCandidates; - nsTArray geckoIntermediateCandidates; - - if (!mCertStorage) { - return Result::FATAL_ERROR_LIBRARY_FAILURE; - } - nsTArray subject; - subject.AppendElements(encodedIssuerName.UnsafeGetData(), - encodedIssuerName.GetLength()); - nsTArray> certs; - nsresult rv = mCertStorage->FindCertsBySubject(subject, certs); - if (NS_FAILED(rv)) { - return Result::FATAL_ERROR_LIBRARY_FAILURE; - } - for (auto& cert : certs) { - Input certDER; - Result rv = certDER.Init(cert.Elements(), cert.Length()); - if (rv != Success) { - continue; // probably too big - } - // Currently we're only expecting intermediate certificates in cert storage. - geckoIntermediateCandidates.AppendElement(std::move(certDER)); - } + nsTArray geckoRootCandidates; + nsTArray geckoIntermediateCandidates; // We might not have this module if e.g. we're on a Linux distribution that // does something unexpected. @@ -248,45 +227,14 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, if (rv != Success) { continue; // probably too big } - geckoRootCandidates.AppendElement(rootInput); + geckoRootCandidates.AppendElement(IssuerCandidateWithSource{ + rootInput, IssuerSource::BuiltInRootsModule}); } } else { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("NSSCertDBTrustDomain::FindIssuer: no built-in roots module")); } - for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) { - BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr); - Result rv = root.Init(); - if (rv != Success) { - continue; - } - // Filter out 3rd party roots that can't be issuers we're looking for - // because the subject distinguished name doesn't match. This prevents - // mozilla::pkix from accumulating spurious errors during path building. - if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) { - continue; - } - geckoRootCandidates.AppendElement(thirdPartyRootInput); - } - - for (const auto& thirdPartyIntermediateInput : - mThirdPartyIntermediateInputs) { - BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA, - nullptr); - Result rv = intermediate.Init(); - if (rv != Success) { - continue; - } - // Filter out 3rd party intermediates that can't be issuers we're looking - // for because the subject distinguished name doesn't match. This prevents - // mozilla::pkix from accumulating spurious errors during path building. - if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) { - continue; - } - geckoIntermediateCandidates.AppendElement(thirdPartyIntermediateInput); - } - if (mExtraCertificates.isSome()) { for (const auto& extraCert : *mExtraCertificates) { Input certInput; @@ -307,15 +255,72 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, } // We assume that extra certificates (presumably from the TLS handshake) // are intermediates, since sending trust anchors would be superfluous. - geckoIntermediateCandidates.AppendElement(certInput); + geckoIntermediateCandidates.AppendElement( + IssuerCandidateWithSource{certInput, IssuerSource::TLSHandshake}); } } + for (const auto& thirdPartyRootInput : mThirdPartyRootInputs) { + BackCert root(thirdPartyRootInput, EndEntityOrCA::MustBeCA, nullptr); + Result rv = root.Init(); + if (rv != Success) { + continue; + } + // Filter out 3rd party roots that can't be issuers we're looking for + // because the subject distinguished name doesn't match. This prevents + // mozilla::pkix from accumulating spurious errors during path building. + if (!InputsAreEqual(encodedIssuerName, root.GetSubject())) { + continue; + } + geckoRootCandidates.AppendElement(IssuerCandidateWithSource{ + thirdPartyRootInput, IssuerSource::ThirdPartyCertificates}); + } + + for (const auto& thirdPartyIntermediateInput : + mThirdPartyIntermediateInputs) { + BackCert intermediate(thirdPartyIntermediateInput, EndEntityOrCA::MustBeCA, + nullptr); + Result rv = intermediate.Init(); + if (rv != Success) { + continue; + } + // Filter out 3rd party intermediates that can't be issuers we're looking + // for because the subject distinguished name doesn't match. This prevents + // mozilla::pkix from accumulating spurious errors during path building. + if (!InputsAreEqual(encodedIssuerName, intermediate.GetSubject())) { + continue; + } + geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{ + thirdPartyIntermediateInput, IssuerSource::ThirdPartyCertificates}); + } + + if (!mCertStorage) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + nsTArray subject; + subject.AppendElements(encodedIssuerName.UnsafeGetData(), + encodedIssuerName.GetLength()); + nsTArray> certs; + nsresult rv = mCertStorage->FindCertsBySubject(subject, certs); + if (NS_FAILED(rv)) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + for (auto& cert : certs) { + Input certDER; + Result rv = certDER.Init(cert.Elements(), cert.Length()); + if (rv != Success) { + continue; // probably too big + } + // Currently we're only expecting intermediate certificates in cert storage. + geckoIntermediateCandidates.AppendElement(IssuerCandidateWithSource{ + std::move(certDER), IssuerSource::PreloadedIntermediates}); + } + // Try all root certs first and then all (presumably) intermediates. geckoRootCandidates.AppendElements(std::move(geckoIntermediateCandidates)); bool keepGoing = true; - Result result = CheckCandidates(*this, checker, geckoRootCandidates, + Result result = CheckCandidates(checker, geckoRootCandidates, nameConstraintsInputPtr, keepGoing); if (result != Success) { return result; @@ -365,14 +370,15 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, return Result::FATAL_ERROR_LIBRARY_FAILURE; } - nsTArray nssCandidates; + nsTArray nssCandidates; for (const auto& rootCandidate : nssRootCandidates) { Input certDER; Result rv = certDER.Init(rootCandidate.Elements(), rootCandidate.Length()); if (rv != Success) { continue; // probably too big } - nssCandidates.AppendElement(std::move(certDER)); + nssCandidates.AppendElement( + IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB}); } for (const auto& intermediateCandidate : nssIntermediateCandidates) { Input certDER; @@ -381,10 +387,11 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName, if (rv != Success) { continue; // probably too big } - nssCandidates.AppendElement(std::move(certDER)); + nssCandidates.AppendElement( + IssuerCandidateWithSource{std::move(certDER), IssuerSource::NSSCertDB}); } - return CheckCandidates(*this, checker, nssCandidates, nameConstraintsInputPtr, + return CheckCandidates(checker, nssCandidates, nameConstraintsInputPtr, keepGoing); } @@ -1606,6 +1613,7 @@ void NSSCertDBTrustDomain::ResetAccumulatedState() { mSCTListFromCertificate = nullptr; mSawDistrustedCAByPolicyError = false; mIsBuiltChainRootBuiltInRoot = false; + mIssuerSources.clear(); } static Input SECItemToInput(const UniqueSECItem& item) { diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h index 511832753e59..129efd075fa9 100644 --- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -54,6 +54,13 @@ enum class OCSPFetchStatus : uint16_t { Fetched = 1, }; +// Helper struct to associate the DER bytes of a potential issuer certificate +// with its source (i.e. where it came from). +struct IssuerCandidateWithSource { + mozilla::pkix::Input mDER; // non-owning + IssuerSource mIssuerSource; +}; + SECStatus InitializeNSS(const nsACString& dir, NSSDBConfig nssDbConfig, PKCS11DBConfig pkcs11DbConfig); @@ -245,6 +252,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { bool GetIsErrorDueToDistrustedCAPolicy() const; OCSPFetchStatus GetOCSPFetchStatus() { return mOCSPFetchStatus; } + IssuerSources GetIssuerSources() { return mIssuerSources; } private: Result CheckCRLiteStash( @@ -290,6 +298,12 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { const Result error, /*out*/ bool& softFailure); + bool ShouldSkipSelfSignedNonTrustAnchor(mozilla::pkix::Input certDER); + Result CheckCandidates(IssuerChecker& checker, + nsTArray& candidates, + mozilla::pkix::Input* nameConstraintsInputPtr, + bool& keepGoing); + const SECTrustType mCertDBTrustType; const OCSPFetching mOCSPFetching; OCSPCache& mOCSPCache; // non-owning! @@ -321,6 +335,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain { UniqueSECMODModule mBuiltInRootsModule; OCSPFetchStatus mOCSPFetchStatus; + IssuerSources mIssuerSources; }; } // namespace psm diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index a5afaea7c498..1a0c6695798a 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -112,6 +112,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" +#include "mozilla/glean/GleanMetrics.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" #include "nsICertOverrideService.h" @@ -529,7 +530,8 @@ static void CollectCertTelemetry( KeySizeStatus aKeySizeStatus, const PinningTelemetryInfo& aPinningTelemetryInfo, const nsTArray>& aBuiltCertChain, - const CertificateTransparencyInfo& aCertificateTransparencyInfo) { + const CertificateTransparencyInfo& aCertificateTransparencyInfo, + const IssuerSources& issuerSources) { uint32_t evStatus = (aCertVerificationResult != Success) ? 0 // 0 = Failure : (aEVStatus != EVStatus::EV) ? 1 // 1 = DV : 2; // 2 = EV @@ -562,6 +564,28 @@ static void CollectCertTelemetry( rootCert); GatherCertificateTransparencyTelemetry(rootCert, aEVStatus == EVStatus::EV, aCertificateTransparencyInfo); + + mozilla::glean::tls::certificate_verifications.Add(1); + if (issuerSources.contains(IssuerSource::TLSHandshake)) { + mozilla::glean::verification_used_cert_from::tls_handshake.AddToNumerator( + 1); + } + if (issuerSources.contains(IssuerSource::PreloadedIntermediates)) { + mozilla::glean::verification_used_cert_from::preloaded_intermediates + .AddToNumerator(1); + } + if (issuerSources.contains(IssuerSource::ThirdPartyCertificates)) { + mozilla::glean::verification_used_cert_from::third_party_certificates + .AddToNumerator(1); + } + if (issuerSources.contains(IssuerSource::NSSCertDB)) { + mozilla::glean::verification_used_cert_from::nss_cert_db.AddToNumerator( + 1); + } + if (issuerSources.contains(IssuerSource::BuiltInRootsModule)) { + mozilla::glean::verification_used_cert_from::built_in_roots_module + .AddToNumerator(1); + } } } @@ -594,17 +618,18 @@ Result AuthCertificate( [](const auto& elementArray) { return elementArray.Clone(); }); } + IssuerSources issuerSources; Result rv = certVerifier.VerifySSLServerCert( certBytes, time, aPinArg, aHostName, builtCertChain, certVerifierFlags, Some(std::move(peerCertsBytes)), stapledOCSPResponse, sctsFromTLSExtension, dcInfo, aOriginAttributes, &evStatus, &ocspStaplingStatus, &keySizeStatus, &pinningTelemetryInfo, &certificateTransparencyInfo, &aIsBuiltCertChainRootBuiltInRoot, - &aMadeOCSPRequests); + &aMadeOCSPRequests, &issuerSources); CollectCertTelemetry(rv, evStatus, ocspStaplingStatus, keySizeStatus, pinningTelemetryInfo, builtCertChain, - certificateTransparencyInfo); + certificateTransparencyInfo, issuerSources); return rv; } diff --git a/security/manager/ssl/metrics.yaml b/security/manager/ssl/metrics.yaml index 9fdb3c60a981..f6126f87d9bd 100644 --- a/security/manager/ssl/metrics.yaml +++ b/security/manager/ssl/metrics.yaml @@ -1,7 +1,6 @@ # 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/. - # Adding a new metric? We have docs for that! # https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html @@ -73,3 +72,78 @@ oskeystore: - available - encrypt - decrypt + +tls: + certificate_verifications: + type: counter + description: > + The total number of successful TLS server certificate verifications. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + +verification_used_cert_from: + tls_handshake: + type: rate + description: > + How many successfully-built certificate chains used a certificate from the TLS handshake. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + denominator_metric: tls.certificate_verifications + preloaded_intermediates: + type: rate + description: > + How many successfully-built certificate chains used a certificate from preloaded intermediates. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + denominator_metric: tls.certificate_verifications + third_party_certificates: + type: rate + description: > + How many successfully-built certificate chains used a third-party certificate from the OS. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + denominator_metric: tls.certificate_verifications + nss_cert_db: + type: rate + description: > + How many successfully-built certificate chains used a certificate from the NSS cert DB. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + denominator_metric: tls.certificate_verifications + built_in_roots_module: + type: rate + description: > + How many successfully-built certificate chains used a certificate from the built-in roots module. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1876435 + notification_emails: + - dkeeler@mozilla.com + expires: never + denominator_metric: tls.certificate_verifications