Bug 1035009: Stop using CERTCertList in mozilla::pkix, r=keeler
authorBrian Smith <brian@briansmith.org>
Sun, 06 Jul 2014 15:55:38 -0700
changeset 193195 0ed88d692f42f34802beafcea77797f61c918155
parent 193194 19663c22e7eb1d0bd8a2d4f84e1f36da8f2da0d8
child 193196 3a4d57c7ffdf74dae7e1c3e1cdadba44750dab15
push id27112
push usercbook@mozilla.com
push dateThu, 10 Jul 2014 12:47:23 +0000
treeherdermozilla-central@6e9f72bdd32e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1035009
milestone33.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 1035009: Stop using CERTCertList in mozilla::pkix, r=keeler
security/apps/AppSignatureVerification.cpp
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/OCSPCache.cpp
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/nsDataSignatureVerifier.cpp
security/manager/ssl/src/nsNSSCertCache.cpp
security/manager/ssl/src/nsNSSCertificate.cpp
security/manager/ssl/src/nsNSSCertificate.h
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsNSSComponent.h
security/pkix/include/pkix/pkix.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/lib/pkixcheck.cpp
security/pkix/lib/pkixkey.cpp
security/pkix/lib/pkixutil.h
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -519,49 +519,49 @@ ParseMF(const char* filebuf, nsIZipReade
     // unrecognized attributes must be ignored
   }
 
   return NS_OK;
 }
 
 struct VerifyCertificateContext {
   AppTrustedRoot trustedRoot;
-  mozilla::pkix::ScopedCERTCertList& builtChain;
+  ScopedCERTCertList& builtChain;
 };
 
 nsresult
 VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg)
 {
   // TODO: null pinArg is tolerated.
   if (NS_WARN_IF(!signerCert) || NS_WARN_IF(!voidContext)) {
     return NS_ERROR_INVALID_ARG;
   }
   const VerifyCertificateContext& context =
     *reinterpret_cast<const VerifyCertificateContext*>(voidContext);
 
-  AppTrustDomain trustDomain(pinArg);
+  AppTrustDomain trustDomain(context.builtChain, pinArg);
   if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   if (BuildCertChain(trustDomain, signerCert->derCert, PR_Now(),
                      EndEntityOrCA::MustBeEndEntity,
                      KeyUsage::digitalSignature,
                      KeyPurposeId::id_kp_codeSigning,
                      CertPolicyId::anyPolicy,
-                     nullptr, context.builtChain) != SECSuccess) {
+                     nullptr/*stapledOCSPResponse*/) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   return NS_OK;
 }
 
 nsresult
 VerifySignature(AppTrustedRoot trustedRoot, const SECItem& buffer,
                 const SECItem& detachedDigest,
-                /*out*/ mozilla::pkix::ScopedCERTCertList& builtChain)
+                /*out*/ ScopedCERTCertList& builtChain)
 {
   VerifyCertificateContext context = { trustedRoot, builtChain };
   // XXX: missing pinArg
   return VerifyCMSDetachedSignatureIncludingCertificate(buffer, detachedDigest,
                                                         VerifyCertificate,
                                                         &context, nullptr);
 }
 
@@ -604,17 +604,17 @@ OpenSignedAppFile(AppTrustedRoot aTruste
   Digest sfCalculatedDigest;
   rv = FindAndLoadOneEntry(zip, NS_LITERAL_CSTRING(JAR_SF_SEARCH_STRING),
                            sfFilename, sfBuffer, &sfCalculatedDigest);
   if (NS_FAILED(rv)) {
     return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
   }
 
   sigBuffer.type = siBuffer;
-  mozilla::pkix::ScopedCERTCertList builtChain;
+  ScopedCERTCertList builtChain;
   rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(),
                        builtChain);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   ScopedAutoSECItem mfDigest;
   rv = ParseSF(char_ptr_cast(sfBuffer.data), mfDigest);
@@ -718,18 +718,17 @@ OpenSignedAppFile(AppTrustedRoot aTruste
   }
 
   // Return the reader to the caller if they want it
   if (aZipReader) {
     zip.forget(aZipReader);
   }
 
   // Return the signer's certificate to the reader if they want it.
-  // XXX: We should return an nsIX509CertList with the whole validated chain,
-  //      but we can't do that until we switch to libpkix.
+  // XXX: We should return an nsIX509CertList with the whole validated chain.
   if (aSignerCert) {
     MOZ_ASSERT(CERT_LIST_HEAD(builtChain));
     nsCOMPtr<nsIX509Cert> signerCert =
       nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert);
     NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY);
     signerCert.forget(aSignerCert);
   }
 
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -8,16 +8,17 @@
 #define FORCE_PR_LOG 1
 #endif
 
 #include "AppTrustDomain.h"
 #include "certdb.h"
 #include "pkix/pkix.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsIX509CertDB.h"
+#include "nsNSSCertificate.h"
 #include "prerror.h"
 #include "secerr.h"
 
 // Generated in Makefile.in
 #include "marketplace-prod-public.inc"
 #include "marketplace-prod-reviewers.inc"
 #include "marketplace-dev-public.inc"
 #include "marketplace-dev-reviewers.inc"
@@ -26,18 +27,19 @@
 using namespace mozilla::pkix;
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
 
 namespace mozilla { namespace psm {
 
-AppTrustDomain::AppTrustDomain(void* pinArg)
-  : mPinArg(pinArg)
+AppTrustDomain::AppTrustDomain(ScopedCERTCertList& certChain, void* pinArg)
+  : mCertChain(certChain)
+  , mPinArg(pinArg)
 {
 }
 
 SECStatus
 AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot)
 {
   SECItem trustedDER;
 
@@ -99,17 +101,17 @@ AppTrustDomain::FindIssuer(const SECItem
   // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
   // FindIssuer must only pass certificates with a matching subject name to
   // checker.Check, we can stop using CERT_CreateSubjectCertList and instead
   // use logic like this:
   //
   // 1. First, try the trusted trust anchor.
   // 2. Secondly, iterate through the certificates that were stored in the CMS
   //    message, passing each one to checker.Check.
-  mozilla::pkix::ScopedCERTCertList
+  ScopedCERTCertList
     candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                           &encodedIssuerName, time, true));
   if (candidates) {
     for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
          !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
       bool keepGoing;
       SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
       if (srv != SECSuccess) {
@@ -197,9 +199,15 @@ AppTrustDomain::CheckRevocation(EndEntit
                                 /*optional*/ const SECItem*,
                                 /*optional*/ const SECItem*)
 {
   // We don't currently do revocation checking. If we need to distrust an Apps
   // certificate, we will use the active distrust mechanism.
   return SECSuccess;
 }
 
-} }
+SECStatus
+AppTrustDomain::IsChainValid(const DERArray& certChain)
+{
+  return ConstructCERTCertListFromReversedDERArray(certChain, mCertChain);
+}
+
+} } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -12,34 +12,35 @@
 #include "nsIX509CertDB.h"
 #include "ScopedNSSTypes.h"
 
 namespace mozilla { namespace psm {
 
 class AppTrustDomain MOZ_FINAL : public mozilla::pkix::TrustDomain
 {
 public:
-  AppTrustDomain(void* pinArg);
+  AppTrustDomain(ScopedCERTCertList&, void* pinArg);
 
   SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot);
 
   SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                          const mozilla::pkix::CertPolicyId& policy,
                          const SECItem& candidateCertDER,
                  /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
   SECStatus FindIssuer(const SECItem& encodedIssuerName,
                        IssuerChecker& checker, PRTime time) MOZ_OVERRIDE;
   SECStatus VerifySignedData(const CERTSignedData& signedData,
                              const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
   SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                             const mozilla::pkix::CertID& certID, PRTime time,
                             /*optional*/ const SECItem* stapledOCSPresponse,
                             /*optional*/ const SECItem* aiaExtension);
-  SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
+  SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain);
 
 private:
+  /*out*/ ScopedCERTCertList& mCertChain;
   void* mPinArg; // non-owning!
   ScopedCERTCertificate mTrustedRoot;
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm_AppsTrustDomain_h
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -14,18 +14,16 @@
 #include "PublicKeyPinningService.h"
 #include "cert.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
 #include "prerror.h"
 #include "secerr.h"
 #include "sslerr.h"
 
-// ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
-// mozilla::ScopedXXX.
 using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gCertVerifierLog = nullptr;
 #endif
 
 namespace mozilla { namespace psm {
@@ -56,17 +54,17 @@ InitCertVerifierLog()
     gCertVerifierLog = PR_NewLogModule("certverifier");
   }
 #endif
 }
 
 SECStatus
 IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
   result = false;
-  ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
+  ScopedPK11SlotList slots;
   slots = PK11_GetAllSlotsForCert(cert, nullptr);
   if (!slots) {
     if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
       // no list
       return SECSuccess;
     }
     return SECFailure;
   }
@@ -156,56 +154,52 @@ SECStatus chainValidationCallback(void* 
   return SECSuccess;
 }
 
 static SECStatus
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
                              PRTime time, KeyUsage ku1, KeyUsage ku2,
                              KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
-                             const SECItem* stapledOCSPResponse,
-                             ScopedCERTCertList& builtChain)
+                             const SECItem* stapledOCSPResponse)
 {
   SECStatus rv = BuildCertChain(trustDomain, cert->derCert, time,
                                 EndEntityOrCA::MustBeEndEntity, ku1,
-                                eku, requiredPolicy,
-                                stapledOCSPResponse, builtChain);
+                                eku, requiredPolicy, stapledOCSPResponse);
   if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
     rv = BuildCertChain(trustDomain, cert->derCert, time,
                         EndEntityOrCA::MustBeEndEntity, ku2,
-                        eku, requiredPolicy,
-                        stapledOCSPResponse, builtChain);
+                        eku, requiredPolicy, stapledOCSPResponse);
     if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity, ku3,
-                          eku, requiredPolicy,
-                          stapledOCSPResponse, builtChain);
+                          eku, requiredPolicy, stapledOCSPResponse);
       if (rv != SECSuccess) {
         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
       }
     }
   }
   return rv;
 }
 
 SECStatus
 CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
                          PRTime time, void* pinArg, const char* hostname,
                          const Flags flags,
             /*optional*/ const SECItem* stapledOCSPResponse,
-        /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
+        /*optional out*/ ScopedCERTCertList* builtChain,
         /*optional out*/ SECOidTag* evOidPolicy)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n"));
 
   PR_ASSERT(cert);
   PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
 
-  if (validationChain) {
-    *validationChain = nullptr;
+  if (builtChain) {
+    *builtChain = nullptr;
   }
   if (evOidPolicy) {
     *evOidPolicy = SEC_OID_UNKNOWN;
   }
 
   if (!cert ||
       (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
@@ -225,29 +219,28 @@ CertVerifier::VerifyCert(CERTCertificate
     : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
                                 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
 
   ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
                                                   : ocsp_get_disabled;
 
   SECStatus rv;
 
-  mozilla::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig);
+                                       pinArg, ocspGETConfig, nullptr,
+                                       builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
-                          CertPolicyId::anyPolicy, stapledOCSPResponse,
-                          builtChain);
+                          CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
       // restrict the acceptable key usage based on the key exchange method
       // chosen by the server.
 
@@ -257,110 +250,107 @@ CertVerifier::VerifyCert(CERTCertificate
       SECOidTag evPolicyOidTag;
       rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
       if (rv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
-                      mOCSPCache, pinArg, ocspGETConfig, &callbackContainer);
+                      mOCSPCache, pinArg, ocspGETConfig,
+                      &callbackContainer, builtChain);
         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                           KeyUsage::digitalSignature,// (EC)DHE
                                           KeyUsage::keyEncipherment, // RSA
                                           KeyUsage::keyAgreement,    // (EC)DH
                                           KeyPurposeId::id_kp_serverAuth,
-                                          evPolicy, stapledOCSPResponse,
-                                          builtChain);
+                                          evPolicy, stapledOCSPResponse);
         if (rv == SECSuccess) {
           if (evOidPolicy) {
             *evOidPolicy = evPolicyOidTag;
           }
           break;
         }
-        builtChain = nullptr; // clear built chain, just in case.
       }
 #endif
 
       if (flags & FLAG_MUST_BE_EV) {
         PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
         rv = SECFailure;
         break;
       }
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig,
-                                       &callbackContainer);
+                                       pinArg, ocspGETConfig, &callbackContainer,
+                                       builtChain);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KeyUsage::digitalSignature, // (EC)DHE
                                         KeyUsage::keyEncipherment, // RSA
                                         KeyUsage::keyAgreement, // (EC)DH
                                         KeyPurposeId::id_kp_serverAuth,
                                         CertPolicyId::anyPolicy,
-                                        stapledOCSPResponse, builtChain);
+                                        stapledOCSPResponse);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig);
+                                       pinArg, ocspGETConfig, nullptr,
+                                       builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
-                          EndEntityOrCA::MustBeCA,
-                          KeyUsage::keyCertSign,
+                          EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
                           KeyPurposeId::id_kp_serverAuth,
-                          CertPolicyId::anyPolicy,
-                          stapledOCSPResponse, builtChain);
+                          CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig);
+                                       pinArg, ocspGETConfig, nullptr,
+                                       builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_emailProtection,
-                          CertPolicyId::anyPolicy,
-                          stapledOCSPResponse, builtChain);
+                          CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
-                                       pinArg, ocspGETConfig);
+                                       pinArg, ocspGETConfig, nullptr,
+                                       builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
-                          CertPolicyId::anyPolicy,
-                          stapledOCSPResponse, builtChain);
+                          CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, cert->derCert, time,
                             EndEntityOrCA::MustBeEndEntity,
                             KeyUsage::keyAgreement, // ECDH/DH
                             KeyPurposeId::id_kp_emailProtection,
-                            CertPolicyId::anyPolicy,
-                            stapledOCSPResponse, builtChain);
+                            CertPolicyId::anyPolicy, stapledOCSPResponse);
       }
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
-                                       mOCSPCache, pinArg, ocspGETConfig);
+                                       mOCSPCache, pinArg, ocspGETConfig,
+                                       nullptr, builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_codeSigning,
-                          CertPolicyId::anyPolicy,
-                          stapledOCSPResponse, builtChain);
+                          CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageVerifyCA:
     case certificateUsageStatusResponder: {
       // XXX This is a pretty useless way to verify a certificate. It is used
       // by the implementation of window.crypto.importCertificates and in the
       // certificate viewer UI. Because we don't know what trust bit is
@@ -373,99 +363,91 @@ CertVerifier::VerifyCert(CERTCertificate
         keyUsage = KeyUsage::keyCertSign;
         eku = KeyPurposeId::anyExtendedKeyUsage;
       } else {
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
-      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
-                                    pinArg, ocspGETConfig);
+      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
+                                    ocspGETConfig, nullptr, builtChain);
       rv = BuildCertChain(sslTrust, cert->derCert, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
-                          stapledOCSPResponse, builtChain);
+                          stapledOCSPResponse);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
-                                        pinArg, ocspGETConfig);
+                                        pinArg, ocspGETConfig, nullptr,
+                                        builtChain);
         rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA,
                             keyUsage, eku, CertPolicyId::anyPolicy,
-                            stapledOCSPResponse, builtChain);
+                            stapledOCSPResponse);
         if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
-                                                  pinArg, ocspGETConfig);
+                                                  pinArg, ocspGETConfig,
+                                                  nullptr, builtChain);
           rv = BuildCertChain(objectSigningTrust, cert->derCert, time,
                               endEntityOrCA, keyUsage, eku,
-                              CertPolicyId::anyPolicy, stapledOCSPResponse,
-                              builtChain);
+                              CertPolicyId::anyPolicy, stapledOCSPResponse);
         }
       }
 
       break;
     }
 
     default:
       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
       return SECFailure;
   }
 
-  if (validationChain && rv == SECSuccess) {
-    *validationChain = builtChain.release();
-  }
-
   return rv;
 }
 
 SECStatus
 CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
                      /*optional*/ const SECItem* stapledOCSPResponse,
                                   PRTime time,
                      /*optional*/ void* pinarg,
                                   const char* hostname,
                                   bool saveIntermediatesInPermanentDatabase,
-                 /*optional out*/ mozilla::pkix::ScopedCERTCertList* certChainOut,
+                 /*optional out*/ ScopedCERTCertList* builtChain,
                  /*optional out*/ SECOidTag* evOidPolicy)
 {
   PR_ASSERT(peerCert);
   // XXX: PR_ASSERT(pinarg)
   PR_ASSERT(hostname);
   PR_ASSERT(hostname[0]);
 
-  if (certChainOut) {
-    *certChainOut = nullptr;
+  if (builtChain) {
+    *builtChain = nullptr;
   }
   if (evOidPolicy) {
     *evOidPolicy = SEC_OID_UNKNOWN;
   }
 
   if (!hostname || !hostname[0]) {
     PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
     return SECFailure;
   }
 
   // CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
   // if VerifyCert succeeded.
-  ScopedCERTCertList validationChain;
   SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
-                            hostname, 0, stapledOCSPResponse, &validationChain,
+                            hostname, 0, stapledOCSPResponse, builtChain,
                             evOidPolicy);
   if (rv != SECSuccess) {
     return rv;
   }
 
   rv = CERT_VerifyCertName(peerCert, hostname);
   if (rv != SECSuccess) {
     return rv;
   }
 
-  if (saveIntermediatesInPermanentDatabase) {
-    SaveIntermediateCerts(validationChain);
-  }
-
-  if (certChainOut) {
-    *certChainOut = validationChain.release();
+  if (saveIntermediatesInPermanentDatabase && builtChain) {
+    SaveIntermediateCerts(*builtChain);
   }
 
   return SECSuccess;
 }
 
 } } // namespace mozilla::psm
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -4,16 +4,17 @@
  * 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 mozilla_psm__CertVerifier_h
 #define mozilla_psm__CertVerifier_h
 
 #include "pkix/pkixtypes.h"
 #include "OCSPCache.h"
+#include "ScopedNSSTypes.h"
 
 namespace mozilla { namespace psm {
 
 struct ChainValidationCallbackState;
 
 class CertVerifier
 {
 public:
@@ -27,27 +28,27 @@ public:
   // Only one usage per verification is supported.
   SECStatus VerifyCert(CERTCertificate* cert,
                        SECCertificateUsage usage,
                        PRTime time,
                        void* pinArg,
                        const char* hostname,
                        Flags flags = 0,
        /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
-      /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain = nullptr,
+      /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
       /*optional out*/ SECOidTag* evOidPolicy = nullptr);
 
   SECStatus VerifySSLServerCert(
                     CERTCertificate* peerCert,
        /*optional*/ const SECItem* stapledOCSPResponse,
                     PRTime time,
        /*optional*/ void* pinarg,
                     const char* hostname,
                     bool saveIntermediatesInPermanentDatabase = false,
-   /*optional out*/ mozilla::pkix::ScopedCERTCertList* certChainOut = nullptr,
+   /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
    /*optional out*/ SECOidTag* evOidPolicy = nullptr);
 
   enum pinning_enforcement_config {
     pinningDisabled = 0,
     pinningAllowUserCAMITM = 1,
     pinningStrict = 2,
     pinningEnforceTestMode = 3
   };
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -4,23 +4,25 @@
  * 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 "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
 #include "ExtendedValidation.h"
+#include "nsNSSCertificate.h"
 #include "NSSErrorsService.h"
 #include "OCSPRequestor.h"
 #include "certdb.h"
 #include "mozilla/Telemetry.h"
 #include "nss.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
+#include "pkix/ScopedPtr.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "ScopedNSSTypes.h"
 #include "secerr.h"
 #include "secmod.h"
 
 using namespace mozilla;
@@ -40,35 +42,37 @@ namespace {
 
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
                                            OCSPFetching ocspFetching,
                                            OCSPCache& ocspCache,
-                                           void* pinArg,
+             /*optional but shouldn't be*/ void* pinArg,
                                            CertVerifier::ocsp_get_config ocspGETConfig,
-                                           CERTChainVerifyCallback* checkChainCallback)
+                              /*optional*/ CERTChainVerifyCallback* checkChainCallback,
+                              /*optional*/ ScopedCERTCertList* builtChain)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
   , mOCSPGetConfig(ocspGETConfig)
   , mCheckChainCallback(checkChainCallback)
+  , mBuiltChain(builtChain)
 {
 }
 
 SECStatus
 NSSCertDBTrustDomain::FindIssuer(const SECItem& encodedIssuerName,
                                  IssuerChecker& checker, PRTime time)
 {
   // TODO: NSS seems to be ambiguous between "no potential issuers found" and
   // "there was an error trying to retrieve the potential issuers."
-  mozilla::pkix::ScopedCERTCertList
+  ScopedCERTCertList
     candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                           &encodedIssuerName, time, true));
   if (candidates) {
     for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
          !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
       bool keepGoing;
       SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
       if (srv != SECSuccess) {
@@ -557,44 +561,55 @@ NSSCertDBTrustDomain::VerifyAndMaybeCach
   // (the call to Put may have un-set it).
   if (rv != SECSuccess) {
     PR_SetError(error, 0);
   }
   return rv;
 }
 
 SECStatus
-NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
-  SECStatus rv = SECFailure;
-
+NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray)
+{
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
-      ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
+      ("NSSCertDBTrustDomain: Top of IsChainValid mCheckChainCallback=%p",
        mCheckChainCallback));
 
-  if (!mCheckChainCallback) {
+  if (!mBuiltChain && !mCheckChainCallback) {
+    // No need to create a CERTCertList, and nothing else to do.
     return SECSuccess;
   }
-  if (!mCheckChainCallback->isChainValid) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-  PRBool chainOK;
-  rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg,
-                                           certChain, &chainOK);
+
+  ScopedCERTCertList certList;
+  SECStatus rv = ConstructCERTCertListFromReversedDERArray(certArray, certList);
   if (rv != SECSuccess) {
     return rv;
   }
-  // rv = SECSuccess only implies successful call, now is time
-  // to check the chain check status
-  // we should only return success if the chain is valid
-  if (chainOK) {
-    return SECSuccess;
+
+  if (mCheckChainCallback) {
+    if (!mCheckChainCallback->isChainValid) {
+      PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+      return SECFailure;
+    }
+    PRBool chainOK;
+    rv = (mCheckChainCallback->isChainValid)(
+            mCheckChainCallback->isChainValidArg, certList.get(), &chainOK);
+    if (rv != SECSuccess) {
+      return rv;
+    }
+    if (!chainOK) {
+      PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
+      return SECFailure;
+    }
   }
-  PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
-  return SECFailure;
+
+  if (mBuiltChain) {
+    *mBuiltChain = certList.forget();
+  }
+
+  return SECSuccess;
 }
 
 namespace {
 
 static char*
 nss_addEscape(const char* string, char quote)
 {
   char* newString = 0;
@@ -762,17 +777,17 @@ DefaultServerNicknameForCert(CERTCertifi
     PR_Free(nickname);
     count++;
   }
   PR_FREEIF(servername);
   return nickname;
 }
 
 void
-SaveIntermediateCerts(const mozilla::pkix::ScopedCERTCertList& certList)
+SaveIntermediateCerts(const ScopedCERTCertList& certList)
 {
   if (!certList) {
     return;
   }
 
   bool isEndEntity = true;
   for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
         !CERT_LIST_END(node, certList);
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -30,33 +30,34 @@ void PORT_Free_string(char* str);
 SECStatus LoadLoadableRoots(/*optional*/ const char* dir,
                             const char* modNameUTF8);
 
 void UnloadLoadableRoots(const char* modNameUTF8);
 
 // Caller must free the result with PR_Free
 char* DefaultServerNicknameForCert(CERTCertificate* cert);
 
-void SaveIntermediateCerts(const mozilla::pkix::ScopedCERTCertList& certList);
+void SaveIntermediateCerts(const ScopedCERTCertList& certList);
 
 class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
 {
 
 public:
   enum OCSPFetching {
     NeverFetchOCSP = 0,
     FetchOCSPForDVSoftFail = 1,
     FetchOCSPForDVHardFail = 2,
     FetchOCSPForEV = 3,
     LocalOnlyOCSPForEV = 4,
   };
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
                        CertVerifier::ocsp_get_config ocspGETConfig,
-                       CERTChainVerifyCallback* checkChainCallback = nullptr);
+          /*optional*/ CERTChainVerifyCallback* checkChainCallback = nullptr,
+          /*optional*/ ScopedCERTCertList* builtChain = nullptr);
 
   virtual SECStatus FindIssuer(const SECItem& encodedIssuerName,
                                IssuerChecker& checker, PRTime time);
 
   virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                  const mozilla::pkix::CertPolicyId& policy,
                                  const SECItem& candidateCertDER,
                          /*out*/ mozilla::pkix::TrustLevel* trustLevel);
@@ -65,17 +66,17 @@ public:
                                      const SECItem& subjectPublicKeyInfo);
 
   virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                     const mozilla::pkix::CertID& certID,
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPResponse,
                        /*optional*/ const SECItem* aiaExtension);
 
-  virtual SECStatus IsChainValid(const CERTCertList* certChain);
+  virtual SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain);
 
 private:
   enum EncodedResponseSource {
     ResponseIsFromNetwork = 1,
     ResponseWasStapled = 2
   };
   static const PRTime ServerFailureDelay = 5 * 60 * PR_USEC_PER_SEC;
   SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
@@ -84,13 +85,14 @@ private:
     EncodedResponseSource responseSource, /*out*/ bool& expired);
 
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
   const CertVerifier::ocsp_get_config mOCSPGetConfig;
   CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
+  ScopedCERTCertList* mBuiltChain; // non-owning
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/certverifier/OCSPCache.cpp
+++ b/security/certverifier/OCSPCache.cpp
@@ -24,36 +24,27 @@
 
 #include "OCSPCache.h"
 
 #include <limits>
 
 #include "NSSCertDBTrustDomain.h"
 #include "pk11pub.h"
 #include "pkix/pkixtypes.h"
+#include "ScopedNSSTypes.h"
 #include "secerr.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gCertVerifierLog;
 #endif
 
 using namespace mozilla::pkix;
 
 namespace mozilla { namespace psm {
 
-void
-MozillaPKIX_PK11_DestroyContext_true(PK11Context* context)
-{
-  PK11_DestroyContext(context, true);
-}
-
-typedef mozilla::pkix::ScopedPtr<PK11Context,
-                                 MozillaPKIX_PK11_DestroyContext_true>
-                                 ScopedPK11Context;
-
 // Let derIssuer be the DER encoding of the issuer of aCert.
 // Let derPublicKey be the DER encoding of the public key of aIssuerCert.
 // Let serialNumber be the bytes of the serial number of aCert.
 // The value calculated is SHA384(derIssuer || derPublicKey || serialNumber).
 // Because the DER encodings include the length of the data encoded,
 // there do not exist A(derIssuerA, derPublicKeyA, serialNumberA) and
 // B(derIssuerB, derPublicKeyB, serialNumberB) such that the concatenation of
 // each triplet results in the same string of bytes but where each part in A is
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -734,17 +734,17 @@ AuthCertificate(CertVerifier& certVerifi
 
   SECStatus rv;
 
   // We want to avoid storing any intermediate cert information when browsing
   // in private, transient contexts.
   bool saveIntermediates =
     !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
 
-  mozilla::pkix::ScopedCERTCertList certList;
+  ScopedCERTCertList certList;
   SECOidTag evOidPolicy;
   rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
                                         time, infoObject,
                                         infoObject->GetHostNameRaw(),
                                         saveIntermediates, nullptr,
                                         &evOidPolicy);
 
   // We want to remember the CA certs in the temp db, so that the application can find the
--- a/security/manager/ssl/src/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/src/nsDataSignatureVerifier.cpp
@@ -125,17 +125,17 @@ VerifyCMSDetachedSignatureIncludingCerti
   // XXX: missing pinArg is tolerated.
   if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
       NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
       (!verifyCertificate) ||
       NS_WARN_IF(!verifyCertificateContext)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  ScopedPtr<NSSCMSMessage, NSS_CMSMessage_Destroy>
+  ScopedNSSCMSMessage
     cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
                                         nullptr, nullptr, nullptr, nullptr,
                                         nullptr));
   if (!cmsMsg) {
     return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
   }
 
   if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
@@ -218,17 +218,17 @@ VerifyCMSDetachedSignatureIncludingCerti
 
 } // namespace mozilla
 
 namespace {
 
 struct VerifyCertificateContext
 {
   nsCOMPtr<nsICertificatePrincipal> principal;
-  mozilla::pkix::ScopedCERTCertList builtChain;
+  ScopedCERTCertList builtChain;
 };
 
 static nsresult
 VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
 {
   // XXX: missing pinArg is tolerated
   if (NS_WARN_IF(!cert) || NS_WARN_IF(!voidContext)) {
     return NS_ERROR_INVALID_ARG;
--- a/security/manager/ssl/src/nsNSSCertCache.cpp
+++ b/security/manager/ssl/src/nsNSSCertCache.cpp
@@ -41,18 +41,17 @@ NS_IMETHODIMP
 nsNSSCertCache::CacheAllCerts()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
   
-  mozilla::pkix::ScopedCERTCertList newList(
-    PK11_ListCerts(PK11CertListUnique, cxt));
+  ScopedCERTCertList newList(PK11_ListCerts(PK11CertListUnique, cxt));
 
   if (newList) {
     MutexAutoLock lock(mutex);
     mCertList = new nsNSSCertList(newList, locker);
   }
   
   return NS_OK;
 }
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -6,16 +6,17 @@
 #include "nsNSSCertificate.h"
 
 #include "prmem.h"
 #include "prerror.h"
 #include "prprf.h"
 #include "CertVerifier.h"
 #include "ExtendedValidation.h"
 #include "pkix/pkixtypes.h"
+#include "pkix/ScopedPtr.h"
 #include "nsNSSComponent.h" // for PIPNSS string bundle calls.
 #include "nsNSSCleaner.h"
 #include "nsCOMPtr.h"
 #include "nsIMutableArray.h"
 #include "nsNSSCertValidity.h"
 #include "nsPKCS12Blob.h"
 #include "nsPK11TokenDB.h"
 #include "nsIX509Cert.h"
@@ -34,19 +35,19 @@
 #include "nsCertVerificationThread.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsXULAppAPI.h"
 #include "nsProxyRelease.h"
 #include "mozilla/Base64.h"
 #include "NSSCertDBTrustDomain.h"
-
 #include "nspr.h"
 #include "certdb.h"
+#include "pkix/pkixtypes.h"
 #include "secerr.h"
 #include "nssb64.h"
 #include "secasn1.h"
 #include "secder.h"
 #include "ssl.h"
 #include "plbase64.h"
 
 using namespace mozilla;
@@ -823,29 +824,33 @@ nsNSSCertificate::GetChain(nsIArray** _r
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(_rvChain);
   nsresult rv;
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting chain for \"%s\"\n", mCert->nickname));
 
-  ::mozilla::pkix::ScopedCERTCertList nssChain;
+  ScopedCERTCertList nssChain;
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   // We want to test all usages, but we start with server because most of the
   // time Firefox users care about server certs.
-  certVerifier->VerifyCert(mCert.get(),
-                           certificateUsageSSLServer, PR_Now(),
-                           nullptr, /*XXX fixme*/
-                           nullptr, /* hostname */
-                           CertVerifier::FLAG_LOCAL_ONLY,
-                           nullptr, /* stapledOCSPResponse */
-                           &nssChain);
+  if (certVerifier->VerifyCert(mCert.get(),
+                               certificateUsageSSLServer, PR_Now(),
+                               nullptr, /*XXX fixme*/
+                               nullptr, /* hostname */
+                               CertVerifier::FLAG_LOCAL_ONLY,
+                               nullptr, /* stapledOCSPResponse */
+                               &nssChain) != SECSuccess) {
+    nssChain = nullptr;
+    // keep going
+  }
+
   // This is the whitelist of all non-SSLServer usages that are supported by
   // verifycert.
   const int otherUsagesToTest = certificateUsageSSLClient |
                                 certificateUsageSSLCA |
                                 certificateUsageEmailSigner |
                                 certificateUsageEmailRecipient |
                                 certificateUsageObjectSigner |
                                 certificateUsageStatusResponder;
@@ -853,23 +858,26 @@ nsNSSCertificate::GetChain(nsIArray** _r
        usage < certificateUsageAnyCA && !nssChain;
        usage = usage << 1) {
     if ((usage & otherUsagesToTest) == 0) {
       continue;
     }
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("pipnss: PKIX attempting chain(%d) for '%s'\n",
             usage, mCert->nickname));
-    certVerifier->VerifyCert(mCert.get(),
-                             usage, PR_Now(),
-                             nullptr, /*XXX fixme*/
-                             nullptr, /*hostname*/
-                             CertVerifier::FLAG_LOCAL_ONLY,
-                             nullptr, /* stapledOCSPResponse */
-                             &nssChain);
+    if (certVerifier->VerifyCert(mCert.get(),
+                                 usage, PR_Now(),
+                                 nullptr, /*XXX fixme*/
+                                 nullptr, /*hostname*/
+                                 CertVerifier::FLAG_LOCAL_ONLY,
+                                 nullptr, /* stapledOCSPResponse */
+                                 &nssChain) != SECSuccess) {
+      nssChain = nullptr;
+      // keep going
+    }
   }
 
   if (!nssChain) {
     // There is not verified path for the chain, howeever we still want to 
     // present to the user as much of a possible chain as possible, in the case
     // where there was a problem with the cert or the issuers.
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("pipnss: getchain :CertVerify failed to get chain for '%s'\n",
@@ -1493,23 +1501,61 @@ nsNSSCertificate::GetValidEVPolicyOid(ns
     outDottedOid.Assign(oid_str);
     PR_smprintf_free(oid_str);
   }
 #endif
 
   return NS_OK;
 }
 
+namespace mozilla {
+
+// TODO(bug 1036065): It seems like we only construct CERTCertLists for the
+// purpose of constructing nsNSSCertLists, so maybe we should change this
+// function to output an nsNSSCertList instead.
+SECStatus
+ConstructCERTCertListFromReversedDERArray(
+  const mozilla::pkix::DERArray& certArray,
+  /*out*/ ScopedCERTCertList& certList)
+{
+  certList = CERT_NewCertList();
+  if (!certList) {
+    return SECFailure;
+  }
+
+  CERTCertDBHandle* certDB(CERT_GetDefaultCertDB()); // non-owning
+
+  size_t numCerts = certArray.GetLength();
+  for (size_t i = 0; i < numCerts; ++i) {
+    SECItem* certDER(const_cast<SECItem*>(certArray.GetDER(i)));
+    ScopedCERTCertificate cert(CERT_NewTempCertificate(certDB, certDER,
+                                                       nullptr, false, true));
+    if (!cert) {
+      return SECFailure;
+    }
+    // certArray is ordered with the root first, but we want the resulting
+    // certList to have the root last.
+    if (CERT_AddCertToListHead(certList, cert) != SECSuccess) {
+      return SECFailure;
+    }
+    cert.forget(); // cert is now owned by certList.
+  }
+
+  return SECSuccess;
+}
+
+} // namespace mozilla
+
 NS_IMPL_ISUPPORTS(nsNSSCertList, nsIX509CertList)
 
-nsNSSCertList::nsNSSCertList(mozilla::pkix::ScopedCERTCertList& certList,
+nsNSSCertList::nsNSSCertList(ScopedCERTCertList& certList,
                              const nsNSSShutDownPreventionLock& proofOfLock)
 {
   if (certList) {
-    mCertList = certList.release();
+    mCertList = certList.forget();
   } else {
     mCertList = CERT_NewCertList();
   }
 }
 
 nsNSSCertList::nsNSSCertList()
 {
   mCertList = CERT_NewCertList();
--- a/security/manager/ssl/src/nsNSSCertificate.h
+++ b/security/manager/ssl/src/nsNSSCertificate.h
@@ -11,20 +11,21 @@
 #include "nsIX509CertList.h"
 #include "nsIASN1Object.h"
 #include "nsIIdentityInfo.h"
 #include "nsCOMPtr.h"
 #include "nsNSSShutDown.h"
 #include "nsISimpleEnumerator.h"
 #include "nsISerializable.h"
 #include "nsIClassInfo.h"
-#include "pkix/pkixtypes.h"
 #include "ScopedNSSTypes.h"
 #include "certt.h"
 
+namespace mozilla { namespace pkix { class DERArray; } }
+
 class nsAutoString;
 class nsINSSComponent;
 class nsIASN1Sequence;
 
 class nsNSSCertificate : public nsIX509Cert,
                          public nsIIdentityInfo,
                          public nsISerializable,
                          public nsIClassInfo,
@@ -67,45 +68,51 @@ private:
     ev_status_invalid = 0, ev_status_valid = 1, ev_status_unknown = 2
   } mCachedEVStatus;
   SECOidTag mCachedEVOidTag;
   nsresult hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
   nsresult getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
 };
 
 namespace mozilla {
+
 template<>
 struct HasDangerousPublicDestructor<nsNSSCertificate>
 {
   static const bool value = true;
 };
-}
+
+SECStatus ConstructCERTCertListFromReversedDERArray(
+            const mozilla::pkix::DERArray& certArray,
+            /*out*/ mozilla::ScopedCERTCertList& certList);
+
+} // namespcae mozilla
 
 class nsNSSCertList: public nsIX509CertList,
                      public nsNSSShutDownObject
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIX509CERTLIST
 
   // certList is adopted
-  nsNSSCertList(mozilla::pkix::ScopedCERTCertList& certList,
+  nsNSSCertList(mozilla::ScopedCERTCertList& certList,
                 const nsNSSShutDownPreventionLock& proofOfLock);
 
   nsNSSCertList();
 
   static CERTCertList* DupCertList(CERTCertList* aCertList,
                                    const nsNSSShutDownPreventionLock&
                                      proofOfLock);
 private:
    virtual ~nsNSSCertList();
    virtual void virtualDestroyNSSReference();
    void destructorSafeDestroyNSSReference();
 
-   mozilla::pkix::ScopedCERTCertList mCertList;
+   mozilla::ScopedCERTCertList mCertList;
 
    nsNSSCertList(const nsNSSCertList&) MOZ_DELETE;
    void operator=(const nsNSSCertList&) MOZ_DELETE;
 };
 
 class nsNSSCertListEnumerator: public nsISimpleEnumerator,
                                public nsNSSShutDownObject
 {
@@ -115,17 +122,17 @@ public:
 
    nsNSSCertListEnumerator(CERTCertList* certList,
                            const nsNSSShutDownPreventionLock& proofOfLock);
 private:
    virtual ~nsNSSCertListEnumerator();
    virtual void virtualDestroyNSSReference();
    void destructorSafeDestroyNSSReference();
 
-   mozilla::pkix::ScopedCERTCertList mCertList;
+   mozilla::ScopedCERTCertList mCertList;
 
    nsNSSCertListEnumerator(const nsNSSCertListEnumerator&) MOZ_DELETE;
    void operator=(const nsNSSCertListEnumerator&) MOZ_DELETE;
 };
 
 
 #define NS_NSS_LONG 4
 #define NS_NSS_GET_LONG(x) ((((unsigned long)((x)[0])) << 24) | \
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -496,17 +496,17 @@ nsNSSCertificateDB::ImportCertificates(u
   }  
   PORT_FreeArena(arena, false);
   return nsrv;
 }
 
 static 
 SECStatus 
 ImportCertsIntoPermanentStorage(
-  const mozilla::pkix::ScopedCERTCertList& certChain,
+  const ScopedCERTCertList& certChain,
   const SECCertUsage usage, const PRBool caOnly)
 {
   CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
 
   int chainLen = 0;
   for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
        !CERT_LIST_END(chainNode, certChain);
        chainNode = CERT_LIST_NEXT(chainNode)) {
@@ -617,17 +617,17 @@ nsNSSCertificateDB::ImportEmailCertifica
   for (node = CERT_LIST_HEAD(certList);
        !CERT_LIST_END(node,certList);
        node = CERT_LIST_NEXT(node)) {
 
     if (!node->cert) {
       continue;
     }
 
-    mozilla::pkix::ScopedCERTCertList certChain;
+    ScopedCERTCertList certChain;
 
     SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageEmailRecipient,
                                             now, ctx, nullptr, 0,
                                             nullptr, &certChain);
 
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
@@ -786,17 +786,17 @@ nsNSSCertificateDB::ImportValidCACertsIn
   /* go down the remaining list of certs and verify that they have
    * valid chains, if yes, then import.
    */
   CERTCertListNode *node;
 
   for (node = CERT_LIST_HEAD(certList);
        !CERT_LIST_END(node,certList);
        node = CERT_LIST_NEXT(node)) {
-    mozilla::pkix::ScopedCERTCertList certChain;
+    ScopedCERTCertList certChain;
     SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageVerifyCA,
                                             PR_Now(), ctx, nullptr, 0, nullptr,
                                             &certChain);
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
       continue;
@@ -1687,18 +1687,17 @@ nsNSSCertificateDB::GetCerts(nsIX509Cert
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }  
 
   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
   nsCOMPtr<nsIX509CertList> nssCertList;
-  mozilla::pkix::ScopedCERTCertList certList(
-    PK11_ListCerts(PK11CertListUnique, ctx));
+  ScopedCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
 
   // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
   // (returns an empty list) 
   nssCertList = new nsNSSCertList(certList, locker);
 
   *_retval = nssCertList;
   NS_ADDREF(*_retval);
   return NS_OK;
@@ -1733,17 +1732,17 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
   ScopedCERTCertificate nssCert(aCert->GetCert());
   if (!nssCert) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
 
-  mozilla::pkix::ScopedCERTCertList resultChain;
+  ScopedCERTCertList resultChain;
   SECOidTag evOidPolicy;
   SECStatus srv;
 
   srv = certVerifier->VerifyCert(nssCert,
                                  aUsage, PR_Now(),
                                  nullptr, // Assume no context
                                  nullptr, // hostname
                                  aFlags,
--- a/security/manager/ssl/src/nsNSSComponent.h
+++ b/security/manager/ssl/src/nsNSSComponent.h
@@ -13,17 +13,16 @@
 #include "nsIEntropyCollector.h"
 #include "nsIStringBundle.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
 #endif
 #include "nsINSSErrorsService.h"
 #include "nsNSSCallbacks.h"
-#include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
 #include "nsNSSHelper.h"
 #include "nsClientAuthRemember.h"
 #include "prerror.h"
 
 class nsIDOMWindow;
 class nsIPrompt;
 class SmartCardThreadList;
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -20,16 +20,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkix_h
 #define mozilla_pkix__pkix_h
 
+#include "pkix/nullptr.h"
 #include "pkixtypes.h"
 #include "prtime.h"
 
 namespace mozilla { namespace pkix {
 
 // ----------------------------------------------------------------------------
 // LIMITED SUPPORT FOR CERTIFICATE POLICIES
 //
@@ -89,18 +90,17 @@ namespace mozilla { namespace pkix {
 //                            distrust.
 // TODO(bug 968451): Document more of these.
 
 SECStatus BuildCertChain(TrustDomain& trustDomain, const SECItem& cert,
                          PRTime time, EndEntityOrCA endEntityOrCA,
                          KeyUsage requiredKeyUsageIfPresent,
                          KeyPurposeId requiredEKUIfPresent,
                          const CertPolicyId& requiredPolicy,
-            /*optional*/ const SECItem* stapledOCSPResponse,
-                 /*out*/ ScopedCERTCertList& results);
+            /*optional*/ const SECItem* stapledOCSPResponse);
 
 // Verify the given signed data using the given public key.
 SECStatus VerifySignedData(const CERTSignedData& sd,
                            const SECItem& subjectPublicKeyInfo,
                            void* pkcs11PinArg);
 
 // The return value, if non-null, is owned by the arena and MUST NOT be freed.
 SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CertID& certID);
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -22,25 +22,22 @@
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
 #include "cert.h"
 #include "pkix/enumclass.h"
-#include "pkix/ScopedPtr.h"
 #include "seccomon.h"
 #include "secport.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
-typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
-
 MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
 
 MOZILLA_PKIX_ENUM_CLASS KeyUsage : uint8_t {
   digitalSignature = 0,
   nonRepudiation   = 1,
   keyEncipherment  = 2,
   dataEncipherment = 3,
   keyAgreement     = 4,
@@ -100,16 +97,31 @@ public:
   }
   const SECItem& issuer;
   const SECItem& issuerSubjectPublicKeyInfo;
   const SECItem& serialNumber;
 private:
   void operator=(const CertID&) /*= delete*/;
 };
 
+class DERArray
+{
+public:
+  // Returns the number of DER-encoded items in the array.
+  virtual size_t GetLength() const = 0;
+
+  // Returns a weak (non-owning) pointer the ith DER-encoded item in the array
+  // (0-indexed). The result is guaranteed to be non-null if i < GetLength(),
+  // and the result is guaranteed to be nullptr if i >= GetLength().
+  virtual const SECItem* GetDER(size_t i) const = 0;
+protected:
+  DERArray() { }
+  virtual ~DERArray() { }
+};
+
 // Applications control the behavior of path building and verification by
 // implementing the TrustDomain interface. The TrustDomain is used for all
 // cryptography and for determining which certificates are trusted or
 // distrusted.
 class TrustDomain
 {
 public:
   virtual ~TrustDomain() { }
@@ -190,28 +202,46 @@ public:
 
   // Verify the given signature using the given public key.
   //
   // Most implementations of this function should probably forward the call
   // directly to mozilla::pkix::VerifySignedData.
   virtual SECStatus VerifySignedData(const CERTSignedData& signedData,
                                      const SECItem& subjectPublicKeyInfo) = 0;
 
+  // Called as soon as we think we have a valid chain but before revocation
+  // checks are done. This function can be used to compute additional checks,
+  // especilaly checks that require the entire certificate chain. This callback
+  // can also be used to save a copy of the built certificate chain for later
+  // use.
+  //
+  // This function may be called multiple times, regardless of whether it
+  // returns SECSuccess or SECFailure. It is guaranteed that BuildCertChain
+  // will not return SECSuccess unless the last call to IsChainValid returns
+  // SECSuccess. Further, it is guaranteed that when BuildCertChain returns
+  // SECSuccess the last chain passed to IsChainValid is the valid chain that
+  // should be used for further operations that require the whole chain.
+  //
+  // Keep in mind, in particular, that if the application saves a copy of the
+  // certificate chain the last invocation of IsChainValid during a validation,
+  // it is still possible for BuildCertChain to fail (return SECFailure), in
+  // which case the application must not assume anything about the validity of
+  // the last certificate chain passed to IsChainValid; especially, it would be
+  // very wrong to assume that the certificate chain is valid.
+  //
+  // certChain.GetDER(0) is the trust anchor.
+  virtual SECStatus IsChainValid(const DERArray& certChain) = 0;
+
   // issuerCertToDup is only non-const so CERT_DupCertificate can be called on
   // it.
   virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA,
                                     const CertID& certID, PRTime time,
                        /*optional*/ const SECItem* stapledOCSPresponse,
                        /*optional*/ const SECItem* aiaExtension) = 0;
 
-  // Called as soon as we think we have a valid chain but before revocation
-  // checks are done. Called to compute additional chain level checks, by the
-  // TrustDomain.
-  virtual SECStatus IsChainValid(const CERTCertList* certChain) = 0;
-
 protected:
   TrustDomain() { }
 
 private:
   TrustDomain(const TrustDomain&) /* = delete */;
   void operator=(const TrustDomain&) /* = delete */;
 };
 
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -33,43 +33,40 @@ namespace mozilla { namespace pkix {
 static Result BuildForward(TrustDomain& trustDomain,
                            const BackCert& subject,
                            PRTime time,
                            EndEntityOrCA endEntityOrCA,
                            KeyUsage requiredKeyUsageIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
                            const CertPolicyId& requiredPolicy,
                            /*optional*/ const SECItem* stapledOCSPResponse,
-                           unsigned int subCACount,
-                           /*out*/ ScopedCERTCertList& results);
+                           unsigned int subCACount);
 
 TrustDomain::IssuerChecker::IssuerChecker() { }
 TrustDomain::IssuerChecker::~IssuerChecker() { }
 
 // The implementation of TrustDomain::IssuerTracker is in a subclass only to
 // hide the implementation from external users.
 class PathBuildingStep : public TrustDomain::IssuerChecker
 {
 public:
   PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject,
                    PRTime time, EndEntityOrCA endEntityOrCA,
                    KeyPurposeId requiredEKUIfPresent,
                    const CertPolicyId& requiredPolicy,
                    /*optional*/ const SECItem* stapledOCSPResponse,
-                   unsigned int subCACount,
-                   /*out*/ ScopedCERTCertList& results)
+                   unsigned int subCACount)
     : trustDomain(trustDomain)
     , subject(subject)
     , time(time)
     , endEntityOrCA(endEntityOrCA)
     , requiredEKUIfPresent(requiredEKUIfPresent)
     , requiredPolicy(requiredPolicy)
     , stapledOCSPResponse(stapledOCSPResponse)
     , subCACount(subCACount)
-    , results(results)
     , result(SEC_ERROR_LIBRARY_FAILURE)
     , resultWasSet(false)
   {
   }
 
   SECStatus Check(const SECItem& potentialIssuerDER,
                   /*out*/ bool& keepGoing);
 
@@ -79,17 +76,16 @@ private:
   TrustDomain& trustDomain;
   const BackCert& subject;
   const PRTime time;
   const EndEntityOrCA endEntityOrCA;
   const KeyPurposeId requiredEKUIfPresent;
   const CertPolicyId& requiredPolicy;
   /*optional*/ SECItem const* const stapledOCSPResponse;
   const unsigned int subCACount;
-  /*out*/ ScopedCERTCertList& results;
 
   SECStatus RecordResult(PRErrorCode currentResult, /*out*/ bool& keepGoing);
   PRErrorCode result;
   bool resultWasSet;
 
   PathBuildingStep(const PathBuildingStep&) /*= delete*/;
   void operator=(const PathBuildingStep&) /*= delete*/;
 };
@@ -170,17 +166,17 @@ PathBuildingStep::Check(const SECItem& p
     return RecordResult(PR_GetError(), keepGoing);
   }
 
   // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the
   // subject public key MUST NOT be used to verify signatures on certificates
   // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
   rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA,
                     KeyUsage::keyCertSign, requiredEKUIfPresent,
-                    requiredPolicy, nullptr, subCACount, results);
+                    requiredPolicy, nullptr, subCACount);
   if (rv != Success) {
     return RecordResult(PR_GetError(), keepGoing);
   }
 
   SECStatus srv = trustDomain.VerifySignedData(
                                 subject.GetSignedData(),
                                 potentialIssuer.GetSubjectPublicKeyInfo());
   if (srv != SECSuccess) {
@@ -209,18 +205,17 @@ static Result
 BuildForward(TrustDomain& trustDomain,
              const BackCert& subject,
              PRTime time,
              EndEntityOrCA endEntityOrCA,
              KeyUsage requiredKeyUsageIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
              /*optional*/ const SECItem* stapledOCSPResponse,
-             unsigned int subCACount,
-             /*out*/ ScopedCERTCertList& results)
+             unsigned int subCACount)
 {
   Result rv;
 
   TrustLevel trustLevel;
   // If this is an end-entity and not a trust anchor, we defer reporting
   // any error found here until after attempting to find a valid chain.
   // See the explanation of error prioritization in pkix.h.
   rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
@@ -236,92 +231,84 @@ BuildForward(TrustDomain& trustDomain,
     } else {
       return rv;
     }
   }
 
   if (trustLevel == TrustLevel::TrustAnchor) {
     // End of the recursion.
 
-    // Construct the results cert chain.
-    results = CERT_NewCertList();
-    if (!results) {
-      return MapSECStatus(SECFailure);
-    }
+    NonOwningDERArray chain;
     for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
-      ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
-        nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
-                                        const_cast<SECItem*>(&cert->GetDER()),
-                                        nullptr, false, true));
-      if (CERT_AddCertToListHead(results.get(), nssCert.get()) != SECSuccess) {
-        return MapSECStatus(SECFailure);
+      Result rv = chain.Append(cert->GetDER());
+      if (rv != Success) {
+        PR_NOT_REACHED("NonOwningDERArray::SetItem failed.");
+        return rv;
       }
-      nssCert.release(); // nssCert is now owned by results.
     }
 
     // This must be done here, after the chain is built but before any
     // revocation checks have been done.
-    SECStatus srv = trustDomain.IsChainValid(results.get());
+    SECStatus srv = trustDomain.IsChainValid(chain);
     if (srv != SECSuccess) {
       return MapSECStatus(srv);
     }
 
     return Success;
   }
 
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     // Avoid stack overflows and poor performance by limiting cert chain
     // length.
     static const unsigned int MAX_SUBCA_COUNT = 6;
+    static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
+                  NonOwningDERArray::MAX_LENGTH,
+                  "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch.");
     if (subCACount >= MAX_SUBCA_COUNT) {
       return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
     }
     ++subCACount;
   } else {
     PR_ASSERT(subCACount == 0);
   }
 
   // Find a trusted issuer.
 
   PathBuildingStep pathBuilder(trustDomain, subject, time, endEntityOrCA,
                                requiredEKUIfPresent, requiredPolicy,
-                               stapledOCSPResponse, subCACount, results);
+                               stapledOCSPResponse, subCACount);
 
   // TODO(bug 965136): Add SKI/AKI matching optimizations
   if (trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time)
         != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   rv = pathBuilder.CheckResult();
   if (rv != Success) {
     return rv;
   }
 
-  // We've built a valid chain from the subject cert up to a trusted root.
-  // At this point, we know the results contains the complete cert chain.
-
   // If we found a valid chain but deferred reporting an error with the
   // end-entity certificate, report it now.
   if (deferredEndEntityError != 0) {
     return Fail(RecoverableError, deferredEndEntityError);
   }
 
   // We've built a valid chain from the subject cert up to a trusted root.
   return Success;
 }
 
 SECStatus
 BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER,
                PRTime time, EndEntityOrCA endEntityOrCA,
                KeyUsage requiredKeyUsageIfPresent,
                KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
-               /*optional*/ const SECItem* stapledOCSPResponse,
-               /*out*/ ScopedCERTCertList& results)
+               /*optional*/ const SECItem* stapledOCSPResponse)
 {
   // XXX: Support the legacy use of the subject CN field for indicating the
   // domain name the certificate is valid for.
   BackCert::IncludeCN includeCN
     = endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
       requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth
     ? BackCert::IncludeCN::Yes
     : BackCert::IncludeCN::No;
@@ -329,18 +316,17 @@ BuildCertChain(TrustDomain& trustDomain,
   BackCert cert(certDER, nullptr, includeCN);
   Result rv = cert.Init();
   if (rv != Success) {
     return SECFailure;
   }
 
   rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
                     requiredKeyUsageIfPresent, requiredEKUIfPresent,
-                    requiredPolicy, stapledOCSPResponse, 0, results);
+                    requiredPolicy, stapledOCSPResponse, 0);
   if (rv != Success) {
-    results = nullptr;
     return SECFailure;
   }
 
   return SECSuccess;
 }
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -21,16 +21,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <limits>
 
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
+#include "pkix/ScopedPtr.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 Result
 CheckValidity(const SECItem& encodedValidity, PRTime time)
--- a/security/pkix/lib/pkixkey.cpp
+++ b/security/pkix/lib/pkixkey.cpp
@@ -24,16 +24,17 @@
 
 #include <limits>
 #include <stdint.h>
 
 #include "cert.h"
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "pkix/pkix.h"
+#include "pkix/ScopedPtr.h"
 #include "prerror.h"
 #include "secerr.h"
 
 namespace mozilla { namespace pkix {
 
 SECStatus
 VerifySignedData(const CERTSignedData& sd, const SECItem& subjectPublicKeyInfo,
                  void* pkcs11PinArg)
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -199,11 +199,45 @@ private:
 
   der::Result RememberExtension(der::Input& extnID, const SECItem& extnValue,
                                 /*out*/ bool& understood);
 
   BackCert(const BackCert&) /* = delete */;
   void operator=(const BackCert&); /* = delete */;
 };
 
+class NonOwningDERArray : public DERArray
+{
+public:
+  NonOwningDERArray()
+    : numItems(0)
+  {
+    // we don't need to initialize the items array because we always check
+    // numItems before accessing i.
+  }
+
+  virtual size_t GetLength() const { return numItems; }
+
+  virtual const SECItem* GetDER(size_t i) const
+  {
+    return i < numItems ? items[i] : nullptr;
+  }
+
+  Result Append(const SECItem& der)
+  {
+    if (numItems >= MAX_LENGTH) {
+      return Fail(RecoverableError, SEC_ERROR_INVALID_ARGS);
+    }
+    items[numItems] = &der;
+    ++numItems;
+    return Success;
+  }
+
+  // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT.
+  static const size_t MAX_LENGTH = 8;
+private:
+  const SECItem* items[MAX_LENGTH]; // avoids any heap allocations
+  size_t numItems;
+};
+
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixutil_h
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -123,17 +123,17 @@ private:
       *trustLevel = TrustLevel::InheritsTrust;
     }
     return SECSuccess;
   }
 
   SECStatus FindIssuer(const SECItem& encodedIssuerName,
                        IssuerChecker& checker, PRTime time)
   {
-    mozilla::pkix::ScopedCERTCertList
+    ScopedCERTCertList
       candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                             &encodedIssuerName, time, true));
     if (candidates) {
       for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
            !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
         bool keepGoing;
         SECStatus srv = checker.Check(n->cert->derCert, keepGoing);
         if (srv != SECSuccess) {
@@ -157,28 +157,28 @@ private:
 
   SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
                             /*optional*/ const SECItem*,
                             /*optional*/ const SECItem*)
   {
     return SECSuccess;
   }
 
-  virtual SECStatus IsChainValid(const CERTCertList*)
+  virtual SECStatus IsChainValid(const DERArray&)
   {
     return SECSuccess;
   }
 
   // We hold references to CERTCertificates in the cert chain tail so that we
   // CERT_CreateSubjectCertList can find them.
   ScopedCERTCertificate certChainTail[7];
 
 public:
   ScopedSECKEYPrivateKey leafCAKey;
-  CERTCertificate* GetLeafeCACert() const
+  CERTCertificate* GetLeafCACert() const
   {
     return certChainTail[PR_ARRAY_SIZE(certChainTail) - 1].get();
   }
 };
 
 class pkixbuild : public NSSTest
 {
 public:
@@ -195,71 +195,64 @@ public:
 protected:
   static TestTrustDomain trustDomain;
 };
 
 /*static*/ TestTrustDomain pkixbuild::trustDomain;
 
 TEST_F(pkixbuild, MaxAcceptableCertChainLength)
 {
-  ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain,
-                                   trustDomain.GetLeafeCACert()->derCert,
+                                   trustDomain.GetLeafCACert()->derCert,
                                    now, EndEntityOrCA::MustBeCA,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(),
-                         trustDomain.GetLeafeCACert()->subjectName,
+                         trustDomain.GetLeafCACert()->subjectName,
                          "CN=Direct End-Entity",
                          EndEntityOrCA::MustBeEndEntity,
                          trustDomain.leafCAKey.get(), privateKey, cert));
   ASSERT_SECSuccess(BuildCertChain(trustDomain, cert->derCert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
 {
-  ScopedCERTCertList results;
-
   ScopedSECKEYPrivateKey caPrivateKey;
   ScopedCERTCertificate caCert;
   ASSERT_TRUE(CreateCert(arena.get(),
-                         trustDomain.GetLeafeCACert()->subjectName,
+                         trustDomain.GetLeafCACert()->subjectName,
                          "CN=CA Too Far", EndEntityOrCA::MustBeCA,
                          trustDomain.leafCAKey.get(),
                          caPrivateKey, caCert));
   PR_SetError(0, 0);
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
                     BuildCertChain(trustDomain, caCert->derCert, now,
                                    EndEntityOrCA::MustBeCA,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), caCert->subjectName,
                          "CN=End-Entity Too Far",
                          EndEntityOrCA::MustBeEndEntity,
                          caPrivateKey.get(), privateKey, cert));
   PR_SetError(0, 0);
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
                     BuildCertChain(trustDomain, cert->derCert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -99,17 +99,17 @@ private:
 
   SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
                             /*optional*/ const SECItem*,
                             /*optional*/ const SECItem*)
   {
     return SECSuccess;
   }
 
-  virtual SECStatus IsChainValid(const CERTCertList*)
+  virtual SECStatus IsChainValid(const DERArray&)
   {
     return SECSuccess;
   }
 };
 
 class pkixcert_extension: public NSSTest
 {
 public:
@@ -144,25 +144,23 @@ TEST_F(pkixcert_extension, UnknownCritic
     sizeof(unknownCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown Critical Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN,
                                  &unknownCriticalExtension, key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, *cert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
 // thus unknown to us) verifies successfully.
 TEST_F(pkixcert_extension, UnknownNonCriticalExtension)
 {
   static const uint8_t unknownNonCriticalExtensionBytes[] = {
     0x30, 0x16, // SEQUENCE (length = 22)
@@ -178,24 +176,22 @@ TEST_F(pkixcert_extension, UnknownNonCri
     sizeof(unknownNonCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown NonCritical Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN,
                                  &unknownNonCriticalExtension, key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that an incorrect OID for id-pe-authorityInformationAccess
 // (when marked critical) is detected and that verification fails.
 // (Until bug 1020993 was fixed, the code checked for this OID.)
 TEST_F(pkixcert_extension, WrongOIDCriticalExtension)
 {
   static const uint8_t wrongOIDCriticalExtensionBytes[] = {
@@ -212,25 +208,23 @@ TEST_F(pkixcert_extension, WrongOIDCriti
     sizeof(wrongOIDCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical Wrong OID Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN,
                                  &wrongOIDCriticalExtension, key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, *cert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a id-pe-authorityInformationAccess critical extension
 // is detected and that verification succeeds.
 TEST_F(pkixcert_extension, CriticalAIAExtension)
 {
   // XXX: According to RFC 5280 an AIA that consists of an empty sequence is
   // not legal, but  we accept it and that is not what we're testing here.
@@ -249,24 +243,22 @@ TEST_F(pkixcert_extension, CriticalAIAEx
     sizeof(criticalAIAExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical AIA Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN, &criticalAIAExtension,
                                  key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
 // Tests that an unknown id-ce extension is detected and that verification
 // fails.
 TEST_F(pkixcert_extension, UnknownCriticalCEExtension)
 {
   static const uint8_t unknownCriticalCEExtensionBytes[] = {
@@ -282,25 +274,23 @@ TEST_F(pkixcert_extension, UnknownCritic
     sizeof(unknownCriticalCEExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown Critical id-ce Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN,
                                  &unknownCriticalCEExtension, key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, *cert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a certificate with a known critical id-ce extension (in this case,
 // OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully.
 TEST_F(pkixcert_extension, KnownCriticalCEExtension)
 {
   static const uint8_t criticalCEExtensionBytes[] = {
     0x30, 0x0d, // SEQUENCE (length = 13)
@@ -316,24 +306,22 @@ TEST_F(pkixcert_extension, KnownCritical
     sizeof(criticalCEExtensionBytes)
   };
   const char* certCN = "CN=Cert With Known Critical id-ce Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN, &criticalCEExtension,
                                  key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert,
                                    now, EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
 
 // Two subjectAltNames must result in an error.
 TEST_F(pkixcert_extension, DuplicateSubjectAltName)
 {
   static const uint8_t DER_BYTES[] = {
     0x30, 22, // SEQUENCE (length = 22)
       0x06, 3, // OID (length = 3)
@@ -349,18 +337,16 @@ TEST_F(pkixcert_extension, DuplicateSubj
     sizeof(DER_BYTES)
   };
   static SECItem const* const extensions[] = { &DER, &DER, nullptr };
   static const char* certCN = "CN=Cert With Duplicate subjectAltName";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
   const SECItem* cert(CreateCert(arena.get(), certCN, extensions, key));
   ASSERT_TRUE(cert);
-  ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_EXTENSION_VALUE_INVALID,
                     BuildCertChain(trustDomain, *cert,
                                    now, EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
-                                   nullptr, // stapled OCSP response
-                                   results));
+                                   nullptr/*stapledOCSPResponse*/));
 }
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -47,16 +47,17 @@ inline void
 SECITEM_FreeItem_true(SECItem* item)
 {
   SECITEM_FreeItem(item, true);
 }
 
 } // unnamed namespace
 
 typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate> ScopedCERTCertificate;
+typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
 typedef mozilla::pkix::ScopedPtr<FILE, fclose_void> ScopedFILE;
 typedef mozilla::pkix::ScopedPtr<SECItem, SECITEM_FreeItem_true> ScopedSECItem;
 typedef mozilla::pkix::ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
   ScopedSECKEYPublicKey;
 typedef mozilla::pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
   ScopedSECKEYPrivateKey;
 
 FILE* OpenFile(const char* dir, const char* filename, const char* mode);