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 331123 cb6b876450fb64170ba9d4b287351401c0b06c4a
parent 331122 8206ecdf17e8c08cf6127d1b2bcc572c83f40bbe
child 331124 3c3401eaf3a24301d9f29234f72b859d28cfdfe1
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, franziskus, Cykesiopka
bugs1252882
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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