/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef CertVerifier_h #define CertVerifier_h #include "CTPolicyEnforcer.h" #include "CTVerifyResult.h" #include "EnterpriseRoots.h" #include "OCSPCache.h" #include "RootCertificateTelemetryUtils.h" #include "ScopedNSSTypes.h" #include "mozilla/EnumSet.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "mozilla/glean/bindings/MetricTypes.h" #include "mozpkix/pkixder.h" #include "mozpkix/pkixtypes.h" #include "nsString.h" #include "signature_cache_ffi.h" #include "sslt.h" #if defined(_MSC_VER) # pragma warning(push) // Silence "RootingAPI.h(718): warning C4324: 'js::DispatchWrapper': // 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; } // namespace ct } // namespace mozilla namespace mozilla { namespace psm { typedef mozilla::pkix::Result Result; enum class EVStatus : uint8_t { NotEV = 0, EV = 1, }; // These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry. enum class KeySizeStatus { NeverChecked = 0, LargeMinimumSucceeded = 1, CompatibilityRisk = 2, AlreadyBad = 3, }; enum class CRLiteMode { Disabled = 0, TelemetryOnly = 1, Enforce = 2, ConfirmRevocations = 3, }; enum class VerifyUsage { TLSServer = 1, TLSServerCA = 2, TLSClient = 3, TLSClientCA = 4, EmailSigner = 5, EmailRecipient = 6, EmailCA = 7, }; 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() : certPinningResultBucket(0), rootBucket(ROOT_CERTIFICATE_UNKNOWN) { Reset(); } // Should we accumulate pinning telemetry for the result? bool accumulateResult; bool isMoz; bool testMode; int32_t certPinningResultBucket; // Should we accumulate telemetry for the root? bool accumulateForRoot; int32_t rootBucket; void Reset() { accumulateForRoot = false; accumulateResult = false; isMoz = false; testMode = false; } }; class CertificateTransparencyInfo { public: CertificateTransparencyInfo() : enabled(false), policyCompliance(Nothing()) { Reset(); } // Was CT enabled? bool enabled; // Verification result of the processed SCTs. mozilla::ct::CTVerifyResult verifyResult; // Connection compliance to the CT Policy. Maybe policyCompliance; void Reset(); }; class DelegatedCredentialInfo { public: DelegatedCredentialInfo() : scheme(ssl_sig_none), authKeyBits(0) {} DelegatedCredentialInfo(SSLSignatureScheme scheme, uint32_t authKeyBits) : scheme(scheme), authKeyBits(authKeyBits) {} // The signature scheme to be used in CertVerify. This tells us // whether to interpret |authKeyBits| in an RSA or ECDSA context. SSLSignatureScheme scheme; // The size of the key, in bits. uint32_t authKeyBits; }; class SkipInvalidSANsForNonBuiltInRootsPolicy : public pkix::NameMatchingPolicy { public: explicit SkipInvalidSANsForNonBuiltInRootsPolicy(bool rootIsBuiltIn) : mRootIsBuiltIn(rootIsBuiltIn) {} virtual pkix::Result FallBackToCommonName( pkix::Time, /*out*/ pkix::FallBackToSearchWithinSubject& fallBackToCommonName) override { fallBackToCommonName = pkix::FallBackToSearchWithinSubject::No; return pkix::Success; } virtual pkix::HandleInvalidSubjectAlternativeNamesBy HandleInvalidSubjectAlternativeNames() override { return mRootIsBuiltIn ? pkix::HandleInvalidSubjectAlternativeNamesBy::Halting : pkix::HandleInvalidSubjectAlternativeNamesBy::Skipping; } private: bool mRootIsBuiltIn; }; class NSSCertDBTrustDomain; class CertVerifier { public: typedef unsigned int Flags; // XXX: FLAG_LOCAL_ONLY is ignored in the classic verification case static const Flags FLAG_LOCAL_ONLY; // Don't perform fallback DV validation on EV validation failure. static const Flags FLAG_MUST_BE_EV; // TLS feature request_status should be ignored static const Flags FLAG_TLS_IGNORE_STATUS_REQUEST; // These values correspond to the SSL_OCSP_STAPLING telemetry. enum OCSPStaplingStatus { OCSP_STAPLING_NEVER_CHECKED = 0, OCSP_STAPLING_GOOD = 1, OCSP_STAPLING_NONE = 2, OCSP_STAPLING_EXPIRED = 3, OCSP_STAPLING_INVALID = 4, }; // *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV // Only one usage per verification is supported. mozilla::pkix::Result VerifyCert( const nsTArray& certBytes, VerifyUsage usage, mozilla::pkix::Time time, void* pinArg, const char* hostname, /*out*/ nsTArray>& builtChain, Flags flags = 0, /*optional in*/ const Maybe>>& extraCertificates = Nothing(), /*optional in*/ const Maybe>& stapledOCSPResponseArg = Nothing(), /*optional in*/ const Maybe>& sctsFromTLS = Nothing(), /*optional in*/ const OriginAttributes& originAttributes = OriginAttributes(), /*optional out*/ EVStatus* evStatus = nullptr, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, /*optional out*/ bool* madeOCSPRequests = nullptr, /*optional out*/ IssuerSources* = nullptr); mozilla::pkix::Result VerifySSLServerCert( const nsTArray& peerCert, mozilla::pkix::Time time, void* pinarg, const nsACString& hostname, /*out*/ nsTArray>& builtChain, /*optional*/ Flags flags = 0, /*optional*/ const Maybe>>& extraCertificates = Nothing(), /*optional*/ const Maybe>& stapledOCSPResponse = Nothing(), /*optional*/ const Maybe>& sctsFromTLS = Nothing(), /*optional*/ const Maybe& dcInfo = Nothing(), /*optional*/ const OriginAttributes& originAttributes = OriginAttributes(), /*optional out*/ EVStatus* evStatus = nullptr, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr, /*optional out*/ KeySizeStatus* keySizeStatus = nullptr, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr, /*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr, /*optional out*/ bool* isBuiltChainRootBuiltInRoot = nullptr, /*optional out*/ bool* madeOCSPRequests = nullptr, /*optional out*/ IssuerSources* = nullptr); enum OcspDownloadConfig { ocspOff = 0, ocspOn = 1, ocspEVOnly = 2 }; enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict }; enum class CertificateTransparencyMode { Disabled = 0, TelemetryOnly = 1, Enforce = 2, }; struct CertificateTransparencyConfig { CertificateTransparencyConfig( CertificateTransparencyMode mode, nsCString&& skipForHosts, nsTArray>&& skipForSPKIHashes) : mMode(mode), mSkipForHosts(std::move(skipForHosts)), mSkipForSPKIHashes(std::move(skipForSPKIHashes)) {} CertificateTransparencyMode mMode; nsCString mSkipForHosts; nsTArray> mSkipForSPKIHashes; }; CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, mozilla::TimeDuration ocspTimeoutSoft, mozilla::TimeDuration ocspTimeoutHard, uint32_t certShortLifetimeInDays, NetscapeStepUpPolicy netscapeStepUpPolicy, CertificateTransparencyConfig&& ctConfig, CRLiteMode crliteMode, const nsTArray& thirdPartyCerts); ~CertVerifier(); void ClearOCSPCache() { mOCSPCache.Clear(); } void ClearTrustCache() { trust_cache_clear(mTrustCache.get()); } const OcspDownloadConfig mOCSPDownloadConfig; const bool mOCSPStrict; const mozilla::TimeDuration mOCSPTimeoutSoft; const mozilla::TimeDuration mOCSPTimeoutHard; const uint32_t mCertShortLifetimeInDays; const NetscapeStepUpPolicy mNetscapeStepUpPolicy; const CertificateTransparencyConfig mCTConfig; const CRLiteMode mCRLiteMode; private: OCSPCache mOCSPCache; // We keep a copy of the bytes of each third party root to own. nsTArray 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. nsTArray mThirdPartyRootInputs; // Similarly, but with intermediates. nsTArray mThirdPartyIntermediateInputs; // We only have a forward declarations of these classes (see above) // so we must allocate dynamically. UniquePtr mCTVerifier; // If many connections are made to a site using a particular certificate, // this cache will speed up verifications after the first one by saving the // results of signature verification. // This will also be beneficial in situations where different sites use // different certificates that happen to be issued by the same intermediate. UniquePtr mSignatureCache; // Similarly, this caches the results of looking up the trust of a // certificate in NSS, which is slow. UniquePtr mTrustCache; void LoadKnownCTLogs(); mozilla::pkix::Result VerifyCertificateTransparencyPolicy( NSSCertDBTrustDomain& trustDomain, const nsTArray>& builtChain, mozilla::pkix::Input sctsFromTLS, mozilla::pkix::Time time, const char* hostname, /*optional out*/ CertificateTransparencyInfo* ctInfo); mozilla::pkix::Result VerifyCertificateTransparencyPolicyInner( NSSCertDBTrustDomain& trustDomain, const nsTArray>& builtChain, mozilla::pkix::Input sctsFromTLS, mozilla::pkix::Time time, /*optional out*/ CertificateTransparencyInfo* ctInfo); }; mozilla::pkix::Result IsCertBuiltInRoot(pkix::Input certInput, bool& result); mozilla::pkix::Result CertListContainsExpectedKeys(const CERTCertList* certList, const char* hostname, mozilla::pkix::Time time); // Verify signed data, making use of the given SignatureCache. That is, if the // (data, digestAlgorithm, signature, subjectPublicKeyInfo) tuple has already // been verified and is in the cache, this skips the work of verifying the // signature (which is slow) and returns the already-known result. mozilla::pkix::Result VerifySignedDataWithCache( mozilla::pkix::der::PublicKeyAlgorithm publicKeyAlg, mozilla::glean::impl::DenominatorMetric telemetryDenominator, mozilla::glean::impl::NumeratorMetric telemetryNumerator, mozilla::pkix::Input data, mozilla::pkix::DigestAlgorithm digestAlgorithm, mozilla::pkix::Input signature, mozilla::pkix::Input subjectPublicKeyInfo, SignatureCache* signatureCache, void* pinArg); } // namespace psm } // namespace mozilla #endif // CertVerifier_h