Bug 1252882 - Add a Content Signature Service r=keeler,r=franziskus,r=Cykesiopka
authorMark Goodwin <mgoodwin@mozilla.com>
Wed, 13 Apr 2016 13:26:01 +0100
changeset 317026 cb6b876450fb64170ba9d4b287351401c0b06c4a
parent 317025 8206ecdf17e8c08cf6127d1b2bcc572c83f40bbe
child 317027 3c3401eaf3a24301d9f29234f72b859d28cfdfe1
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, franziskus, Cykesiopka
bugs1252882
milestone48.0a1
Bug 1252882 - Add a Content Signature Service r=keeler,r=franziskus,r=Cykesiopka MozReview-Commit-ID: 2nS6vN3iDKe
security/certverifier/NSSCertDBTrustDomain.cpp
security/manager/ssl/CSTrustDomain.cpp
security/manager/ssl/CSTrustDomain.h
security/manager/ssl/ContentSignatureVerifier.cpp
security/manager/ssl/ContentSignatureVerifier.h
security/manager/ssl/moz.build
security/manager/ssl/nsCertTree.cpp
security/manager/ssl/nsIContentSignatureVerifier.idl
security/manager/ssl/nsNSSCertificate.cpp
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/nsNSSComponent.h
security/manager/ssl/nsNSSModule.cpp
security/manager/ssl/nsSDR.cpp
security/manager/ssl/nsSDR.h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/UniquePtr.h"
 #include "nsNSSCertificate.h"
 #include "nss.h"
 #include "NSSErrorsService.h"
 #include "nsServiceManagerUtils.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
 #include "pkix/pkixnss.h"
+#include "pkix/Result.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "PublicKeyPinningService.h"
 #include "ScopedNSSTypes.h"
 #include "secerr.h"
 
 #include "CNNICHashWhitelist.inc"
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/CSTrustDomain.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CSTrustDomain.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Preferences.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "pkix/pkixnss.h"
+
+using namespace mozilla::pkix;
+
+namespace mozilla { namespace psm {
+
+static LazyLogModule gTrustDomainPRLog("CSTrustDomain");
+#define CSTrust_LOG(args) MOZ_LOG(gTrustDomainPRLog, LogLevel::Debug, args)
+
+CSTrustDomain::CSTrustDomain(ScopedCERTCertList& certChain)
+  : mCertChain(certChain)
+  , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
+{
+}
+
+Result
+CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+                            const CertPolicyId& policy, Input candidateCertDER,
+                            /*out*/ TrustLevel& trustLevel)
+{
+  MOZ_ASSERT(policy.IsAnyPolicy());
+  if (!policy.IsAnyPolicy()) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+
+  SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER);
+  ScopedCERTCertificate candidateCert(
+    CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem,
+                            nullptr, false, true));
+  if (!candidateCert) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+
+  bool isCertRevoked;
+  nsresult nsrv = mCertBlocklist->IsCertRevoked(
+                    candidateCert->derIssuer.data,
+                    candidateCert->derIssuer.len,
+                    candidateCert->serialNumber.data,
+                    candidateCert->serialNumber.len,
+                    candidateCert->derSubject.data,
+                    candidateCert->derSubject.len,
+                    candidateCert->derPublicKey.data,
+                    candidateCert->derPublicKey.len,
+                    &isCertRevoked);
+  if (NS_FAILED(nsrv)) {
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
+  if (isCertRevoked) {
+    CSTrust_LOG(("CSTrustDomain: certificate is revoked\n"));
+    return Result::ERROR_REVOKED_CERTIFICATE;
+  }
+
+  // Is this cert our built-in content signing root?
+  bool isRoot = false;
+  nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
+  if (!component) {
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+  nsrv = component->IsCertContentSigningRoot(candidateCert.get(), isRoot);
+  if (NS_FAILED(nsrv)) {
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+  if (isRoot) {
+    CSTrust_LOG(("CSTrustDomain: certificate is a trust anchor\n"));
+    trustLevel = TrustLevel::TrustAnchor;
+    return Success;
+  }
+  CSTrust_LOG(("CSTrustDomain: certificate is *not* a trust anchor\n"));
+
+  trustLevel = TrustLevel::InheritsTrust;
+  return Success;
+}
+
+Result
+CSTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
+                          Time time)
+{
+  // Loop over the chain, look for a matching subject
+  for (CERTCertListNode* n = CERT_LIST_HEAD(mCertChain);
+      !CERT_LIST_END(n, mCertChain); 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 the subject does not match, try the next certificate
+    Input subjectDER;
+    rv = subjectDER.Init(n->cert->derSubject.data, n->cert->derSubject.len);
+    if (rv != Success) {
+      continue; // just try the next one
+    }
+    if (!InputsAreEqual(subjectDER, encodedIssuerName)) {
+      CSTrust_LOG(("CSTrustDomain: subjects don't match\n"));
+      continue;
+    }
+
+    // If the subject does match, try the next step
+    bool keepGoing;
+    rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/,
+                       keepGoing);
+    if (rv != Success) {
+      return rv;
+    }
+    if (!keepGoing) {
+      CSTrust_LOG(("CSTrustDomain: don't keep going\n"));
+      break;
+    }
+  }
+
+  return Success;
+}
+
+Result
+CSTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
+                               const CertID& certID, Time time,
+                               Duration validityDuration,
+                               /*optional*/ const Input* stapledOCSPresponse,
+                               /*optional*/ const Input* aiaExtension)
+{
+  // We're relying solely on the CertBlocklist for revocation - and we're
+  // performing checks on this in GetCertTrust (as per nsNSSCertDBTrustDomain)
+  return Success;
+}
+
+Result
+CSTrustDomain::IsChainValid(const DERArray& certChain, Time time)
+{
+  // Check that our chain is not empty
+  if (certChain.GetLength() == 0) {
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
+  return Success;
+}
+
+Result
+CSTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
+                                             EndEntityOrCA endEntityOrCA,
+                                             Time notBefore)
+{
+  if (digestAlg == DigestAlgorithm::sha1) {
+    return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+  }
+  return Success;
+}
+
+Result
+CSTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+  EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits)
+{
+  if (modulusSizeInBits < 2048) {
+    return Result::ERROR_INADEQUATE_KEY_SIZE;
+  }
+  return Success;
+}
+
+Result
+CSTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                          Input subjectPublicKeyInfo)
+{
+  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                       nullptr);
+}
+
+Result
+CSTrustDomain::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
+CSTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                       Input subjectPublicKeyInfo)
+{
+  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                    nullptr);
+}
+
+Result
+CSTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
+                                         EndEntityOrCA endEntityOrCA,
+                                         KeyPurposeId keyPurpose)
+{
+  return Success;
+}
+
+Result
+CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
+                         /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+{
+  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
+}
+
+} } // end namespace mozilla::psm
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/CSTrustDomain.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CSTrustDomain_h
+#define CSTrustDomain_h
+
+#include "pkix/pkixtypes.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "nsDebug.h"
+#include "nsICertBlocklist.h"
+#include "nsIX509CertDB.h"
+#include "ScopedNSSTypes.h"
+
+namespace mozilla { namespace psm {
+
+class CSTrustDomain final : public mozilla::pkix::TrustDomain
+{
+public:
+  typedef mozilla::pkix::Result Result;
+
+  explicit CSTrustDomain(ScopedCERTCertList& certChain);
+
+  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) 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 CheckValidityIsAcceptable(
+    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+    mozilla::pkix::EndEntityOrCA endEntityOrCA,
+    mozilla::pkix::KeyPurposeId keyPurpose) override;
+  virtual Result DigestBuf(mozilla::pkix::Input item,
+                           mozilla::pkix::DigestAlgorithm digestAlg,
+                           /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen) override;
+
+private:
+  /*out*/ ScopedCERTCertList& mCertChain;
+  nsCOMPtr<nsICertBlocklist> mCertBlocklist;
+};
+
+} } // namespace mozilla::psm
+
+#endif // CSTrustDomain_h
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/ContentSignatureVerifier.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContentSignatureVerifier.h"
+
+#include "BRNameMatchingPolicy.h"
+#include "cryptohi.h"
+#include "keyhi.h"
+#include "nsCOMPtr.h"
+#include "nsNSSComponent.h"
+#include "nssb64.h"
+#include "nsWhitespaceTokenizer.h"
+#include "nsXPCOMStrings.h"
+#include "pkix/pkix.h"
+#include "pkix/pkixtypes.h"
+#include "secerr.h"
+#include "SharedCertVerifier.h"
+#include "nsSecurityHeaderParser.h"
+
+NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier)
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+using namespace mozilla::psm;
+
+static LazyLogModule gCSVerifierPRLog("ContentSignatureVerifier");
+#define CSVerifier_LOG(args) MOZ_LOG(gCSVerifierPRLog, LogLevel::Debug, args)
+
+// Content-Signature prefix
+const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
+
+ContentSignatureVerifier::~ContentSignatureVerifier()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+  destructorSafeDestroyNSSReference();
+  shutdown(calledFromObject);
+}
+
+nsresult
+ContentSignatureVerifier::VerifyContentSignature(
+  const nsACString& aData, const nsACString& aCSHeader,
+  const nsACString& aCertChain, const uint32_t aSource, bool* _retval)
+{
+  nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aSource);
+  if (NS_FAILED(rv)) {
+    *_retval = false;
+    CSVerifier_LOG(("CSVerifier: Signature verification failed\n"));
+    if (rv == NS_ERROR_INVALID_SIGNATURE) {
+      return NS_OK;
+    }
+    return rv;
+  }
+
+  return End(_retval);
+}
+
+bool
+IsNewLine(char16_t c)
+{
+  return c == '\n' || c == '\r';
+}
+
+nsresult
+ReadChainIntoCertList(const nsACString& aCertChain, CERTCertList* aCertList,
+                      const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  bool inBlock = false;
+  bool certFound = false;
+
+  const nsCString header = NS_LITERAL_CSTRING("-----BEGIN CERTIFICATE-----");
+  const nsCString footer = NS_LITERAL_CSTRING("-----END CERTIFICATE-----");
+
+  nsCWhitespaceTokenizerTemplate<IsNewLine> tokenizer(aCertChain);
+
+  nsAutoCString blockData;
+  while (tokenizer.hasMoreTokens()) {
+    nsDependentCSubstring token = tokenizer.nextToken();
+    if (token.IsEmpty()) {
+      continue;
+    }
+    if (inBlock) {
+      if (token.Equals(footer)) {
+        inBlock = false;
+        certFound = true;
+        // base64 decode data, make certs, append to chain
+        UniqueSECItem der(::SECITEM_AllocItem(nullptr, nullptr, 0));
+        if (!der || !NSSBase64_DecodeBuffer(nullptr, der.get(),
+                                            blockData.BeginReading(),
+                                            blockData.Length())) {
+          CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
+          return NS_ERROR_FAILURE;
+        }
+        CERTCertificate* tmpCert =
+          CERT_NewTempCertificate(CERT_GetDefaultCertDB(), der.get(), nullptr,
+                                  false, true);
+        if (!tmpCert) {
+          return NS_ERROR_FAILURE;
+        }
+        // if adding tmpCert succeeds, tmpCert will now be owned by aCertList
+        SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert);
+        if (res != SECSuccess) {
+          CERT_DestroyCertificate(tmpCert);
+          return MapSECStatus(res);
+        }
+      } else {
+        blockData.Append(token);
+      }
+    } else if (token.Equals(header)) {
+      inBlock = true;
+      blockData = "";
+    }
+  }
+  if (inBlock || !certFound) {
+    // the PEM data did not end; bad data.
+    CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n"));
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+// Create a context for a content signature verification.
+// It sets signature, certificate chain, and context that shold be used to
+// verify the data. The optional data parameter is added to the data to verify.
+NS_IMETHODIMP
+ContentSignatureVerifier::CreateContext(const nsACString& aData,
+                                        const nsACString& aCSHeader,
+                                        const nsACString& aCertChain,
+                                        const uint32_t aSource)
+{
+  MutexAutoLock lock(mMutex);
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mCx) {
+    return NS_ERROR_ALREADY_INITIALIZED;
+  }
+
+  ScopedCERTCertList certCertList(CERT_NewCertList());
+
+  if (!certCertList) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get(), locker);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get());
+  if (!node || !node->cert) {
+    return NS_ERROR_FAILURE;
+  }
+
+  SECItem* certSecItem = &node->cert->derCert;
+
+  Input certDER;
+  Result result =
+    certDER.Init(reinterpret_cast<const uint8_t*>(certSecItem->data),
+                 certSecItem->len);
+  if (result != Success) {
+    return NS_ERROR_FAILURE;
+  }
+
+
+  // Check the signerCert chain is good
+  CSTrustDomain trustDomain(certCertList);
+  result = BuildCertChain(trustDomain, certDER, Now(),
+                          EndEntityOrCA::MustBeEndEntity,
+                          KeyUsage::noParticularKeyUsageRequired,
+                          KeyPurposeId::id_kp_codeSigning,
+                          CertPolicyId::anyPolicy,
+                          nullptr/*stapledOCSPResponse*/);
+  if (result != Success) {
+    // the chain is bad
+    CSVerifier_LOG(("CSVerifier: The supplied chain is bad\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Check the SAN
+  nsAutoCString hostname;
+
+  Input hostnameInput;
+
+  switch (aSource) {
+    case ABOUT_NEWTAB:
+      hostname = "remote-newtab-signer.mozilla.org";
+      break;
+    case ONECRL:
+      hostname = "oneCRL-signer.mozilla.org";
+      break;
+    default:
+      CSVerifier_LOG(("CSVerifier: bad context\n"));
+      return NS_ERROR_INVALID_ARG;
+  }
+
+  result = hostnameInput.Init(uint8_t_ptr_cast(hostname.BeginReading()),
+                              hostname.Length());
+  if (result != Success) {
+    return NS_ERROR_FAILURE;
+  }
+
+  BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
+  result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
+  if (result != Success) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  mKey.reset(CERT_ExtractPublicKey(node->cert));
+
+  // in case we were not able to extract a key
+  if (!mKey) {
+    CSVerifier_LOG(("CSVerifier: unable to extract a key\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // we get the raw content-signature header here, so first parse aCSHeader
+  rv = ParseContentSignatureHeader(aCSHeader);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // Base 64 decode the signature
+  UniqueSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!rawSignatureItem ||
+      !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem.get(),
+                              mSignature.get(), mSignature.Length())) {
+    CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
+    return NS_ERROR_FAILURE;
+  }
+
+  // get signature object
+  UniqueSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!signatureItem) {
+    return NS_ERROR_FAILURE;
+  }
+  // We have a raw ecdsa signature r||s so we have to DER-encode it first
+  // Note that we have to check rawSignatureItem->len % 2 here as
+  // DSAU_EncodeDerSigWithLen asserts this
+  if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) {
+    CSVerifier_LOG(("CSVerifier: signature length is bad\n"));
+    return NS_ERROR_FAILURE;
+  }
+  if (DSAU_EncodeDerSigWithLen(signatureItem.get(), rawSignatureItem.get(),
+                               rawSignatureItem->len) != SECSuccess) {
+    CSVerifier_LOG(("CSVerifier: encoding the signature failed\n"));
+    return NS_ERROR_FAILURE;
+  }
+
+  // this is the only OID we support for now
+  SECOidTag oid = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
+
+  mCx = UniqueVFYContext(
+    VFY_CreateContext(mKey.get(), signatureItem.get(), oid, nullptr));
+
+  if (!mCx) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  if (VFY_Begin(mCx.get()) != SECSuccess) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  rv = UpdateInternal(kPREFIX, lock, locker);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  // add data if we got any
+  return UpdateInternal(aData, lock, locker);
+}
+
+nsresult
+ContentSignatureVerifier::UpdateInternal(const nsACString& aData,
+  MutexAutoLock& /*proofOfLock*/,
+  const nsNSSShutDownPreventionLock& /*proofOfLock*/)
+{
+  if (!aData.IsEmpty()) {
+    if (VFY_Update(mCx.get(), (const unsigned char*)nsPromiseFlatCString(aData).get(),
+                   aData.Length()) != SECSuccess){
+      return NS_ERROR_INVALID_SIGNATURE;
+    }
+  }
+  return NS_OK;
+}
+
+/**
+ * Add data to the context that shold be verified.
+ */
+NS_IMETHODIMP
+ContentSignatureVerifier::Update(const nsACString& aData)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
+    return NS_ERROR_FAILURE;
+  }
+  MutexAutoLock lock(mMutex);
+  return UpdateInternal(aData, lock, locker);
+}
+
+/**
+ * Finish signature verification and return the result in _retval.
+ */
+NS_IMETHODIMP
+ContentSignatureVerifier::End(bool* _retval)
+{
+  NS_ENSURE_ARG(_retval);
+  MutexAutoLock lock(mMutex);
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
+    return NS_ERROR_FAILURE;
+  }
+
+  *_retval = (VFY_End(mCx.get()) == SECSuccess);
+
+  return NS_OK;
+}
+
+nsresult
+ContentSignatureVerifier::ParseContentSignatureHeader(
+  const nsACString& aContentSignatureHeader)
+{
+  // We only support p384 ecdsa according to spec
+  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
+
+  nsSecurityHeaderParser parser(aContentSignatureHeader.BeginReading());
+  nsresult rv = parser.Parse();
+  if (NS_FAILED(rv)) {
+    CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header\n"));
+    return NS_ERROR_FAILURE;
+  }
+  LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
+
+  for (nsSecurityHeaderDirective* directive = directives->getFirst();
+       directive != nullptr; directive = directive->getNext()) {
+    CSVerifier_LOG(("CSVerifier: found directive %s\n", directive->mName.get()));
+    if (directive->mName.Length() == signature_var.Length() &&
+        directive->mName.EqualsIgnoreCase(signature_var.get(),
+                                          signature_var.Length())) {
+      if (!mSignature.IsEmpty()) {
+        CSVerifier_LOG(("CSVerifier: found two ContentSignatures\n"));
+        return NS_ERROR_INVALID_SIGNATURE;
+      }
+
+      CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n"));
+      mSignature = directive->mValue;
+    }
+  }
+
+  // we have to ensure that we found a signature at this point
+  if (mSignature.IsEmpty()) {
+    CSVerifier_LOG(("CSVerifier: got a Content-Signature header but didn't find a signature.\n"));
+    return NS_ERROR_FAILURE;
+  }
+
+  // Bug 769521: We have to change b64 url to regular encoding as long as we
+  // don't have a b64 url decoder. This should change soon, but in the meantime
+  // we have to live with this.
+  mSignature.ReplaceChar('-', '+');
+  mSignature.ReplaceChar('_', '/');
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/ContentSignatureVerifier.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef ContentSignatureVerifier_h
+#define ContentSignatureVerifier_h
+
+#include "cert.h"
+#include "CSTrustDomain.h"
+#include "nsIContentSignatureVerifier.h"
+#include "nsNSSShutDown.h"
+#include "ScopedNSSTypes.h"
+
+// 45a5fe2f-c350-4b86-962d-02d5aaaa955a
+#define NS_CONTENTSIGNATUREVERIFIER_CID \
+  { 0x45a5fe2f, 0xc350, 0x4b86, \
+    { 0x96, 0x2d, 0x02, 0xd5, 0xaa, 0xaa, 0x95, 0x5a } }
+#define NS_CONTENTSIGNATUREVERIFIER_CONTRACTID \
+    "@mozilla.org/security/contentsignatureverifier;1"
+
+class ContentSignatureVerifier final : public nsIContentSignatureVerifier
+                                     , public nsNSSShutDownObject
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICONTENTSIGNATUREVERIFIER
+
+  ContentSignatureVerifier()
+    : mCx(nullptr)
+    , mMutex("CSVerifier::mMutex")
+  {
+  }
+
+  // nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference() override
+  {
+    destructorSafeDestroyNSSReference();
+  }
+
+private:
+  ~ContentSignatureVerifier();
+
+  nsresult UpdateInternal(const nsACString& aData,
+                          MutexAutoLock& /*proofOfLock*/,
+                          const nsNSSShutDownPreventionLock& /*proofOfLock*/);
+
+  void destructorSafeDestroyNSSReference()
+  {
+    mCx = nullptr;
+    mKey = nullptr;
+  }
+
+  nsresult ParseContentSignatureHeader(const nsACString& aContentSignatureHeader);
+
+  // verifier context for incremental verifications
+  mozilla::UniqueVFYContext mCx;
+  // signature to verify
+  nsCString mSignature;
+  // verification key
+  mozilla::UniqueSECKEYPublicKey mKey;
+  mozilla::Mutex mMutex;
+};
+
+#endif // ContentSignatureVerifier_h
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -13,16 +13,17 @@ XPIDL_SOURCES += [
     'nsIAssociatedContentSecurity.idl',
     'nsIBadCertListener2.idl',
     'nsIBufEntropyCollector.idl',
     'nsICertBlocklist.idl',
     'nsICertificateDialogs.idl',
     'nsICertOverrideService.idl',
     'nsICertPickDialogs.idl',
     'nsIClientAuthDialogs.idl',
+    'nsIContentSignatureVerifier.idl',
     'nsIDataSignatureVerifier.idl',
     'nsIGenKeypairInfoDlg.idl',
     'nsIKeygenThread.idl',
     'nsIKeyModule.idl',
     'nsINSSVersion.idl',
     'nsIPK11Token.idl',
     'nsIPK11TokenDB.idl',
     'nsIPKCS11.idl',
@@ -77,17 +78,19 @@ EXPORTS.mozilla.psm += [
 ]
 
 EXPORTS.ipc += [
     'DataStorageIPCUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'CertBlocklist.cpp',
+    'ContentSignatureVerifier.cpp',
     'CryptoTask.cpp',
+    'CSTrustDomain.cpp',
     'DataStorage.cpp',
     'nsCertOverrideService.cpp',
     'nsCertPicker.cpp',
     'nsCertVerificationThread.cpp',
     'nsClientAuthRemember.cpp',
     'nsCrypto.cpp',
     'nsCryptoHash.cpp',
     'nsDataSignatureVerifier.cpp',
--- a/security/manager/ssl/nsCertTree.cpp
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -11,16 +11,17 @@
 #include "nsISupportsPrimitives.h"
 #include "nsITreeColumns.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509Cert.h"
 #include "nsIX509CertValidity.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSComponent.h" // for PIPNSS string bundle calls.
+#include "nsNSSHelper.h"
 #include "nsReadableUtils.h"
 #include "nsTHashtable.h"
 #include "nsUnicharUtils.h"
 #include "nsXPCOMCID.h"
 #include "nsXPIDLString.h"
 #include "pkix/pkixtypes.h"
 
 using namespace mozilla;
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/nsIContentSignatureVerifier.idl
@@ -0,0 +1,88 @@
+/* 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 "nsISupports.idl"
+
+/**
+ * An interface for verifying content-signatures, inspired by
+ * https://tools.ietf.org/html/draft-thomson-http-content-signature-00
+ * described here https://github.com/franziskuskiefer/content-signature/tree/pki
+ *
+ * A new signature verifier instance should be created for each signature
+ * verification - you can create these instances with do_CreateInstance.
+ *
+ * There are two ways to use this functionality:
+ * The first allows a signature to be verified all at once by simply calling
+ * verifyContentSignature.
+ * The second allows for streaming; call createContext with the signature
+ * information (and initial data), call update with more data as it becomes
+ * available then, finally, call end to verify the signature.
+ */
+[scriptable, uuid(45a5fe2f-c350-4b86-962d-02d5aaaa955a)]
+interface nsIContentSignatureVerifier : nsISupports
+{
+
+  /**
+   * Verification sources.
+   * If the verification is from ABOUT_NEWTAB, the content signature can only be
+   * verified with a certificate chain where the end entity is valid for the
+   * hostname "remote-newtab-signer.mozilla.org".
+   * If the verification is from ONECRL, the end entity must be valid for the
+   * hostname "oneCRL-signer.mozilla.org"
+   */
+  const unsigned long ABOUT_NEWTAB = 0;
+  const unsigned long ONECRL = 1;
+
+  /**
+   * Verifies that the data matches the data that was used to generate the
+   * signature.
+   *
+   * @param aData                   The data to be tested.
+   * @param aContentSignatureHeader The content-signature header,
+   *                                url-safe base64 encoded.
+   * @param aCertificateChain       The certificate chain to use for verification.
+   *                                PEM encoded string.
+   * @param aSource                 The source of this verification (one of the
+   *                                values defined above).
+   * @returns true if the signature matches the data and aCertificateChain is
+   *          valid within aContext, false if not.
+   */
+  boolean verifyContentSignature(in ACString aData, in ACString aSignature,
+                                 in ACString aCertificateChain,
+                                 in unsigned long aSource);
+
+  /**
+   * Creates a context to verify a content signature against data that is added
+   * later with update calls.
+   *
+   * @param aData                   The first chunk of data to be tested.
+   *                                This parameter is optional.
+   * @param aContentSignatureHeader The signature of the data, url-safe base64
+   *                                encoded.
+   * @param aCertificateChain       The certificate chain to use for
+   *                                verification. PEM encoded string.
+   * @param aSource                 The source of this verification (one of the
+   *                                values defined above).
+   */
+  void createContext(in ACString aData, in ACString aSignature,
+                     in ACString aCertificateChain, in unsigned long aSource);
+
+  /**
+   * Adds data to the context that was used to generate the signature.
+   *
+   * @param aData        More data to be tested.
+   */
+  void update(in ACString aData);
+
+  /**
+   * Finalises the signature and returns the result of the signature
+   * verification.
+   *
+   * @returns true if the signature matches the data added with createContext
+   *          and update, false if not.
+   */
+  boolean end();
+
+};
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -34,16 +34,17 @@
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsUsageArrayHelper.h"
 #include "nsXULAppAPI.h"
 #include "nspr.h"
 #include "nssb64.h"
 #include "pkix/pkixnss.h"
 #include "pkix/pkixtypes.h"
+#include "pkix/Result.h"
 #include "plbase64.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "secasn1.h"
 #include "secder.h"
 #include "secerr.h"
 #include "ssl.h"
@@ -221,18 +222,18 @@ NS_IMETHODIMP
 nsNSSCertificate::GetIsBuiltInRoot(bool* aIsBuiltInRoot)
 {
   NS_ENSURE_ARG(aIsBuiltInRoot);
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  Result rv = IsCertBuiltInRoot(mCert, *aIsBuiltInRoot);
-  if (rv != Success) {
+  pkix::Result rv = IsCertBuiltInRoot(mCert, *aIsBuiltInRoot);
+  if (rv != pkix::Result::Success) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
 nsNSSCertificate::MarkForPermDeletion()
 {
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1804,16 +1804,20 @@ nsNSSComponent::Observe(nsISupports* aSu
       setValidationOptions(false, lock);
 #ifdef DEBUG
     } else if (prefName.EqualsLiteral("security.test.built_in_root_hash")) {
       MutexAutoLock lock(mutex);
       mTestBuiltInRootHash = Preferences::GetString("security.test.built_in_root_hash");
 #endif // DEBUG
     } else if (prefName.Equals(kFamilySafetyModePref)) {
       MaybeEnableFamilySafetyCompatibility();
+    } else if (prefName.EqualsLiteral("security.content.signature.root_hash")) {
+      MutexAutoLock lock(mutex);
+      mContentSigningRootHash =
+        Preferences::GetString("security.content.signature.root_hash");
     } else {
       clearSessionCache = false;
     }
     if (clearSessionCache)
       SSL_ClearSessionCache();
   }
 
   return NS_OK;
@@ -1956,16 +1960,42 @@ nsNSSComponent::IsCertTestBuiltInRoot(CE
     return rv;
   }
 
   result = mTestBuiltInRootHash.Equals(certHash);
   return NS_OK;
 }
 #endif // DEBUG
 
+NS_IMETHODIMP
+nsNSSComponent::IsCertContentSigningRoot(CERTCertificate* cert, bool& result)
+{
+  MutexAutoLock lock(mutex);
+  MOZ_ASSERT(mNSSInitialized);
+
+  result = false;
+
+  if (mContentSigningRootHash.IsEmpty()) {
+    return NS_OK;
+  }
+
+  RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert);
+  if (!nsc) {
+    return NS_ERROR_FAILURE;
+  }
+  nsAutoString certHash;
+  nsresult rv = nsc->GetSha256Fingerprint(certHash);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  result = mContentSigningRootHash.Equals(certHash);
+  return NS_OK;
+}
+
 SharedCertVerifier::~SharedCertVerifier() { }
 
 already_AddRefed<SharedCertVerifier>
 nsNSSComponent::GetDefaultCertVerifier()
 {
   MutexAutoLock lock(mutex);
   MOZ_ASSERT(mNSSInitialized);
   RefPtr<SharedCertVerifier> certVerifier(mDefaultCertVerifier);
--- a/security/manager/ssl/nsNSSComponent.h
+++ b/security/manager/ssl/nsNSSComponent.h
@@ -81,16 +81,18 @@ public:
 #endif
 
   NS_IMETHOD IsNSSInitialized(bool* initialized) = 0;
 
 #ifdef DEBUG
   NS_IMETHOD IsCertTestBuiltInRoot(CERTCertificate* cert, bool& result) = 0;
 #endif
 
+  NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) = 0;
+
   virtual ::already_AddRefed<mozilla::psm::SharedCertVerifier>
     GetDefaultCertVerifier() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsINSSComponent, NS_INSSCOMPONENT_IID)
 
 class nsNSSShutDownList;
 class nsCertVerificationThread;
@@ -135,16 +137,18 @@ public:
 #endif
 
   NS_IMETHOD IsNSSInitialized(bool* initialized) override;
 
 #ifdef DEBUG
   NS_IMETHOD IsCertTestBuiltInRoot(CERTCertificate* cert, bool& result) override;
 #endif
 
+  NS_IMETHOD IsCertContentSigningRoot(CERTCertificate* cert, bool& result) override;
+
   ::already_AddRefed<mozilla::psm::SharedCertVerifier>
     GetDefaultCertVerifier() override;
 
   // The following two methods are thread-safe.
   static bool AreAnyWeakCiphersEnabled();
   static void UseWeakCiphersOnSocket(PRFileDesc* fd);
 
   static void FillTLSVersionRange(SSLVersionRange& rangeOut,
@@ -179,16 +183,17 @@ private:
   nsNSSShutDownList* mShutdownObjectList;
 #ifndef MOZ_NO_SMART_CARDS
   SmartCardThreadList* mThreadList;
 #endif
 
 #ifdef DEBUG
   nsAutoString mTestBuiltInRootHash;
 #endif
+  nsString mContentSigningRootHash;
 
   void deleteBackgroundThreads();
   void createBackgroundThreads();
   nsCertVerificationThread* mCertVerificationThread;
 
   nsNSSHttpInterface mHttpForNSS;
   RefPtr<mozilla::psm::SharedCertVerifier> mDefaultCertVerifier;
 
--- a/security/manager/ssl/nsNSSModule.cpp
+++ b/security/manager/ssl/nsNSSModule.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "CertBlocklist.h"
+#include "ContentSignatureVerifier.h"
 #include "nsCertOverrideService.h"
 #include "nsCertPicker.h"
 #include "nsCrypto.h"
 #include "nsCryptoHash.h"
 #include "nsCURILoader.h"
 #include "nsDataSignatureVerifier.h"
 #include "nsDOMCID.h" //For the NS_CRYPTO_CONTRACTID define
 #include "nsEntropyCollector.h"
@@ -202,16 +203,17 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEn
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsPkcs11)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertPicker)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsNTLMAuthModule, InitTest)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsCryptoHash)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsCryptoHMAC)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObject)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObjectFactory)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsDataSignatureVerifier)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, ContentSignatureVerifier)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsRandomGenerator)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, nsSSLStatus)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo)
 
 typedef mozilla::psm::NSSErrorsService NSSErrorsService;
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NSSErrorsService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSVersion)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCertOverrideService, Init)
@@ -238,16 +240,17 @@ NS_DEFINE_NAMED_CID(NS_CERTTREE_CID);
 NS_DEFINE_NAMED_CID(NS_PKCS11_CID);
 NS_DEFINE_NAMED_CID(NS_CRYPTO_HASH_CID);
 NS_DEFINE_NAMED_CID(NS_CRYPTO_HMAC_CID);
 NS_DEFINE_NAMED_CID(NS_CERT_PICKER_CID);
 NS_DEFINE_NAMED_CID(NS_NTLMAUTHMODULE_CID);
 NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECT_CID);
 NS_DEFINE_NAMED_CID(NS_KEYMODULEOBJECTFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_DATASIGNATUREVERIFIER_CID);
+NS_DEFINE_NAMED_CID(NS_CONTENTSIGNATUREVERIFIER_CID);
 NS_DEFINE_NAMED_CID(NS_CERTOVERRIDE_CID);
 NS_DEFINE_NAMED_CID(NS_RANDOMGENERATOR_CID);
 NS_DEFINE_NAMED_CID(NS_SSLSTATUS_CID);
 NS_DEFINE_NAMED_CID(TRANSPORTSECURITYINFO_CID);
 NS_DEFINE_NAMED_CID(NS_NSSERRORSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_NSSVERSION_CID);
 NS_DEFINE_NAMED_CID(NS_ENTROPYCOLLECTOR_CID);
 NS_DEFINE_NAMED_CID(NS_SECURE_BROWSER_UI_CID);
@@ -273,16 +276,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_PKCS11_CID, false, nullptr, nsPkcs11Constructor },
   { &kNS_CRYPTO_HASH_CID, false, nullptr, nsCryptoHashConstructor },
   { &kNS_CRYPTO_HMAC_CID, false, nullptr, nsCryptoHMACConstructor },
   { &kNS_CERT_PICKER_CID, false, nullptr, nsCertPickerConstructor },
   { &kNS_NTLMAUTHMODULE_CID, false, nullptr, nsNTLMAuthModuleConstructor },
   { &kNS_KEYMODULEOBJECT_CID, false, nullptr, nsKeyObjectConstructor },
   { &kNS_KEYMODULEOBJECTFACTORY_CID, false, nullptr, nsKeyObjectFactoryConstructor },
   { &kNS_DATASIGNATUREVERIFIER_CID, false, nullptr, nsDataSignatureVerifierConstructor },
+  { &kNS_CONTENTSIGNATUREVERIFIER_CID, false, nullptr, ContentSignatureVerifierConstructor },
   { &kNS_CERTOVERRIDE_CID, false, nullptr, nsCertOverrideServiceConstructor },
   { &kNS_RANDOMGENERATOR_CID, false, nullptr, nsRandomGeneratorConstructor },
   { &kNS_SSLSTATUS_CID, false, nullptr, nsSSLStatusConstructor },
   { &kTRANSPORTSECURITYINFO_CID, false, nullptr, TransportSecurityInfoConstructor },
   { &kNS_NSSERRORSSERVICE_CID, false, nullptr, NSSErrorsServiceConstructor },
   { &kNS_NSSVERSION_CID, false, nullptr, nsNSSVersionConstructor },
   { &kNS_ENTROPYCOLLECTOR_CID, false, nullptr, nsEntropyCollectorConstructor },
   { &kNS_SECURE_BROWSER_UI_CID, false, nullptr, nsSecureBrowserUIImplConstructor },
@@ -313,16 +317,17 @@ static const mozilla::Module::ContractID
   { NS_CRYPTO_HMAC_CONTRACTID, &kNS_CRYPTO_HMAC_CID },
   { NS_CERT_PICKER_CONTRACTID, &kNS_CERT_PICKER_CID },
   { "@mozilla.org/uriloader/psm-external-content-listener;1", &kNS_PSMCONTENTLISTEN_CID },
   { NS_CRYPTO_FIPSINFO_SERVICE_CONTRACTID, &kNS_PKCS11MODULEDB_CID },
   { NS_NTLMAUTHMODULE_CONTRACTID, &kNS_NTLMAUTHMODULE_CID },
   { NS_KEYMODULEOBJECT_CONTRACTID, &kNS_KEYMODULEOBJECT_CID },
   { NS_KEYMODULEOBJECTFACTORY_CONTRACTID, &kNS_KEYMODULEOBJECTFACTORY_CID },
   { NS_DATASIGNATUREVERIFIER_CONTRACTID, &kNS_DATASIGNATUREVERIFIER_CID },
+  { NS_CONTENTSIGNATUREVERIFIER_CONTRACTID, &kNS_CONTENTSIGNATUREVERIFIER_CID },
   { NS_CERTOVERRIDE_CONTRACTID, &kNS_CERTOVERRIDE_CID },
   { NS_RANDOMGENERATOR_CONTRACTID, &kNS_RANDOMGENERATOR_CID },
   { NS_ENTROPYCOLLECTOR_CONTRACTID, &kNS_ENTROPYCOLLECTOR_CID },
   { NS_SECURE_BROWSER_UI_CONTRACTID, &kNS_SECURE_BROWSER_UI_CID },
   { NS_SSSERVICE_CONTRACTID, &kNS_SITE_SECURITY_SERVICE_CID },
   { NS_CERTBLOCKLIST_CONTRACTID, &kNS_CERT_BLOCKLIST_CID },
   { NS_WEAKCRYPTOOVERRIDE_CONTRACTID, &kNS_WEAKCRYPTOOVERRIDE_CID },
   { nullptr }
--- a/security/manager/ssl/nsSDR.cpp
+++ b/security/manager/ssl/nsSDR.cpp
@@ -10,23 +10,25 @@
 
 #include "mozilla/Services.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
+#include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsITokenPasswordDialogs.h"
 
 #include "nsISecretDecoderRing.h"
 #include "nsCRT.h"
 #include "nsSDR.h"
 #include "nsNSSComponent.h"
+#include "nsNSSHelper.h"
 #include "nsNSSShutDown.h"
 #include "ScopedNSSTypes.h"
 
 #include "pk11func.h"
 #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
 
 #include "ssl.h" // For SSL_ClearSessionCache
 
--- a/security/manager/ssl/nsSDR.h
+++ b/security/manager/ssl/nsSDR.h
@@ -3,16 +3,17 @@
  * 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 _NSSDR_H_
 #define _NSSDR_H_
 
 #include "nsISecretDecoderRing.h"
+#include "nsNSSShutDown.h"
 
 /**
  * NS_SDR_CONTRACTID - contract id for SDR services.
  *   Implements nsISecretDecoderRing.
  *   Should eventually implement an interface to set window
  *   context and other information. (nsISecretDecoderRingConfig)
  *
  * NOTE: This definition should move to base code.  It