Bug 1039064: Use strongly-typed enum instead of NSPR-style error handling, r=keeler
authorBrian Smith <brian@briansmith.org>
Fri, 18 Jul 2014 11:48:49 -0700
changeset 197202 5f7dc391e8611d1f12f77d55f2c5a56ef8f6f29e
parent 197068 1f216e2e339e2ec5db3c9df769de0bb1a8037c04
child 197203 68fe3e5c7181331a5bf13e865cb3c559bfe81400
push id27236
push useremorley@mozilla.com
push dateFri, 01 Aug 2014 15:52:48 +0000
treeherdermozilla-central@44e5072476b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1039064
milestone34.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 1039064: Use strongly-typed enum instead of NSPR-style error handling, r=keeler
netwerk/base/public/nsINSSErrorsService.idl
security/apps/AppSignatureVerification.cpp
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/OCSPCache.cpp
security/certverifier/OCSPCache.h
security/certverifier/moz.build
security/manager/ssl/src/NSSErrorsService.cpp
security/manager/ssl/src/NSSErrorsService.h
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_pinning.js
security/pkix/include/pkix/Input.h
security/pkix/include/pkix/Result.h
security/pkix/include/pkix/pkix.h
security/pkix/include/pkix/pkixnss.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/lib/pkixcert.cpp
security/pkix/lib/pkixcheck.cpp
security/pkix/lib/pkixder.cpp
security/pkix/lib/pkixder.h
security/pkix/lib/pkixkey.cpp
security/pkix/lib/pkixnss.cpp
security/pkix/lib/pkixocsp.cpp
security/pkix/lib/pkixutil.h
security/pkix/moz.build
security/pkix/test/gtest/moz.build
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
security/pkix/test/gtest/pkixder_input_tests.cpp
security/pkix/test/gtest/pkixder_pki_types_tests.cpp
security/pkix/test/gtest/pkixder_universal_types_tests.cpp
security/pkix/test/gtest/pkixgtest.cpp
security/pkix/test/gtest/pkixgtest.h
security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
security/pkix/test/lib/pkixtestutil.cpp
--- a/netwerk/base/public/nsINSSErrorsService.idl
+++ b/netwerk/base/public/nsINSSErrorsService.idl
@@ -1,17 +1,17 @@
 /* -*- 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 "nsISupports.idl"
 
-[scriptable, uuid(dc8013f2-4aed-47a8-bc4e-9fd7a6cc60fa)]
+[scriptable, uuid(12f60021-e14b-4020-99d1-ed2c795be66a)]
 interface nsINSSErrorsService : nsISupports
 {
     /**
      *  @param aNSPRCode An error code obtained using PR_GetError()
      *  @return True if it is error code defined by the NSS library
      */
     boolean isNSSErrorCode(in int32_t aNSPRCode);
 
@@ -49,17 +49,20 @@ interface nsINSSErrorsService : nsISuppo
     const long NSS_SEC_ERROR_BASE  = -(0x2000);
     const long NSS_SEC_ERROR_LIMIT = (NSS_SEC_ERROR_BASE + 1000);
     const long NSS_SSL_ERROR_BASE  = -(0x3000);
     const long NSS_SSL_ERROR_LIMIT = (NSS_SSL_ERROR_BASE + 1000);
 
     /**
      * The error codes within each module must fit in 16 bits. We want these
      * errors to fit in the same module as the NSS errors but not overlap with
-     * any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
-     * involves negating the value of the error and then synthesizing an error
-     * in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at
-     * a negative value that both doesn't overlap with the current value
-     * ranges for NSS errors and that will fit in 16 bits when negated.
+     * any of them. Converting an NSS SEC, NSS SSL, or mozilla::pkix error to
+     * an NS error involves negating the value of the error and then
+     * synthesizing an error in the NS_ERROR_MODULE_SECURITY module. Hence,
+     * mozilla::pkix errors will start at a negative value that both doesn't
+     * overlap with the current value ranges for NSS errors and that will fit
+     * in 16 bits when negated.
+     *
+     * Keep these in sync with pkixnss.h.
      */
-    const long PSM_ERROR_BASE  = -(0x4000);
-    const long PSM_ERROR_LIMIT = (PSM_ERROR_BASE + 1000);
+    const long MOZILLA_PKIX_ERROR_BASE  = -(0x4000);
+    const long MOZILLA_PKIX_ERROR_LIMIT = (MOZILLA_PKIX_ERROR_BASE + 1000);
 };
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -6,16 +6,17 @@
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG 1
 #endif
 
 #include "nsNSSCertificateDB.h"
 
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
 #include "mozilla/RefPtr.h"
 #include "CryptoTask.h"
 #include "AppTrustDomain.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDataSignatureVerifier.h"
 #include "nsHashKeys.h"
 #include "nsIFile.h"
@@ -536,23 +537,24 @@ VerifyCertificate(CERTCertificate* signe
   }
   const VerifyCertificateContext& context =
     *reinterpret_cast<const VerifyCertificateContext*>(voidContext);
 
   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/*stapledOCSPResponse*/) != SECSuccess) {
-    return MapSECStatus(SECFailure);
+  Result rv = BuildCertChain(trustDomain, signerCert->derCert, PR_Now(),
+                             EndEntityOrCA::MustBeEndEntity,
+                             KeyUsage::digitalSignature,
+                             KeyPurposeId::id_kp_codeSigning,
+                             CertPolicyId::anyPolicy,
+                             nullptr/*stapledOCSPResponse*/);
+  if (rv != Success) {
+    return mozilla::psm::GetXPCOMFromNSSError(MapResultToPRErrorCode(rv));
   }
 
   return NS_OK;
 }
 
 nsresult
 VerifySignature(AppTrustedRoot trustedRoot, const SECItem& buffer,
                 const SECItem& detachedDigest,
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG 1
 #endif
 
 #include "AppTrustDomain.h"
 #include "certdb.h"
-#include "pkix/pkix.h"
+#include "pkix/pkixnss.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"
@@ -88,25 +88,24 @@ AppTrustDomain::SetTrustedRoot(AppTruste
                                          &trustedDER, nullptr, false, true);
   if (!mTrustedRoot) {
     return SECFailure;
   }
 
   return SECSuccess;
 }
 
-SECStatus
+Result
 AppTrustDomain::FindIssuer(const SECItem& encodedIssuerName,
                            IssuerChecker& checker, PRTime time)
 
 {
   MOZ_ASSERT(mTrustedRoot);
   if (!mTrustedRoot) {
-    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_INVALID_STATE;
   }
 
   // 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.
@@ -114,60 +113,58 @@ AppTrustDomain::FindIssuer(const SECItem
   //    message, passing each one to checker.Check.
   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,
-                                    nullptr/*additionalNameConstraints*/,
-                                    keepGoing);
-      if (srv != SECSuccess) {
-        return SECFailure;
+      Result rv = checker.Check(n->cert->derCert,
+                                nullptr/*additionalNameConstraints*/,
+                                keepGoing);
+      if (rv != Success) {
+        return rv;
       }
       if (!keepGoing) {
         break;
       }
     }
   }
 
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
                              const CertPolicyId& policy,
                              const SECItem& candidateCertDER,
                      /*out*/ TrustLevel* trustLevel)
 {
   MOZ_ASSERT(policy.IsAnyPolicy());
   MOZ_ASSERT(trustLevel);
   MOZ_ASSERT(mTrustedRoot);
   if (!trustLevel || !policy.IsAnyPolicy()) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
   if (!mTrustedRoot) {
-    PR_SetError(PR_INVALID_STATE_ERROR, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_INVALID_STATE;
   }
 
   // Handle active distrust of the certificate.
 
   // XXX: This would be cleaner and more efficient if we could get the trust
   // information without constructing a CERTCertificate here, but NSS doesn't
   // expose it in any other easy-to-use fashion.
   ScopedCERTCertificate candidateCert(
     CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                             const_cast<SECItem*>(&candidateCertDER), nullptr,
                             false, true));
   if (!candidateCert) {
-    return SECFailure;
+    return MapPRErrorCodeToResult(PR_GetError());
   }
 
   CERTCertTrust trust;
   if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) {
     PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning);
 
     // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit,
     // because we can have active distrust for either type of cert. Note that
@@ -175,60 +172,65 @@ AppTrustDomain::GetCertTrust(EndEntityOr
     // relevant trust bit isn't set then that means the cert must be considered
     // distrusted.
     PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA
                               ? CERTDB_TRUSTED_CA
                               : CERTDB_TRUSTED;
     if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD)))
             == CERTDB_TERMINAL_RECORD) {
       *trustLevel = TrustLevel::ActivelyDistrusted;
-      return SECSuccess;
+      return Success;
     }
   }
 
   // mTrustedRoot is the only trust anchor for this validation.
   if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) {
     *trustLevel = TrustLevel::TrustAnchor;
-    return SECSuccess;
+    return Success;
   }
 
   *trustLevel = TrustLevel::InheritsTrust;
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 AppTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData,
                                  const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                            mPinArg);
 }
 
-SECStatus
+Result
 AppTrustDomain::DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
                           size_t digestBufLen)
 {
   return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
 }
 
-SECStatus
+Result
 AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, PRTime time,
                                 /*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;
+  return Success;
 }
 
-SECStatus
+Result
 AppTrustDomain::IsChainValid(const DERArray& certChain)
 {
-  return ConstructCERTCertListFromReversedDERArray(certChain, mCertChain);
+  SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain,
+                                                            mCertChain);
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+  return Success;
 }
 
-SECStatus
+Result
 AppTrustDomain::CheckPublicKey(const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
 }
 
 } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -12,38 +12,43 @@
 #include "nsIX509CertDB.h"
 #include "ScopedNSSTypes.h"
 
 namespace mozilla { namespace psm {
 
 class AppTrustDomain MOZ_FINAL : public mozilla::pkix::TrustDomain
 {
 public:
+  typedef mozilla::pkix::Result Result;
+
   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 CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                            const mozilla::pkix::CertID& certID, PRTime time,
-                            /*optional*/ const SECItem* stapledOCSPresponse,
-                            /*optional*/ const SECItem* aiaExtension);
-  SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain);
-
-  SECStatus VerifySignedData(
-              const mozilla::pkix::SignedDataWithSignature& signedData,
-              const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
-  SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
-                      size_t digestBufLen) MOZ_OVERRIDE;
-  SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
+  virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                              const mozilla::pkix::CertPolicyId& policy,
+                              const SECItem& candidateCertDER,
+                              /*out*/ mozilla::pkix::TrustLevel* trustLevel)
+                              MOZ_OVERRIDE;
+  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+                            IssuerChecker& checker,
+                            PRTime time) MOZ_OVERRIDE;
+  virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                                 const mozilla::pkix::CertID& certID, PRTime time,
+                                 /*optional*/ const SECItem* stapledOCSPresponse,
+                                 /*optional*/ const SECItem* aiaExtension);
+  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain)
+                              MOZ_OVERRIDE;
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+                                MOZ_OVERRIDE;
+  virtual Result VerifySignedData(
+           const mozilla::pkix::SignedDataWithSignature& signedData,
+           const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen) MOZ_OVERRIDE;
 
 private:
   /*out*/ ScopedCERTCertList& mCertChain;
   void* mPinArg; // non-owning!
   ScopedCERTCertificate mTrustedRoot;
 };
 
 } } // namespace mozilla::psm
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -10,16 +10,17 @@
 
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "NSSErrorsService.h"
 #include "PublicKeyPinningService.h"
 #include "cert.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
 #include "prerror.h"
 #include "secerr.h"
 #include "sslerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
 #ifdef PR_LOGGING
@@ -149,36 +150,36 @@ SECStatus chainValidationCallback(void* 
                           CertVerifier::pinningEnforceTestMode);
   *chainOK = PublicKeyPinningService::
     ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
                       enforceTestMode);
 
   return SECSuccess;
 }
 
-static SECStatus
+static Result
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
                              PRTime time, KeyUsage ku1, KeyUsage ku2,
                              KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
                              const SECItem* stapledOCSPResponse)
 {
-  SECStatus rv = BuildCertChain(trustDomain, cert->derCert, time,
-                                EndEntityOrCA::MustBeEndEntity, ku1,
-                                eku, requiredPolicy, stapledOCSPResponse);
-  if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+  Result rv = BuildCertChain(trustDomain, cert->derCert, time,
+                             EndEntityOrCA::MustBeEndEntity, ku1,
+                             eku, requiredPolicy, stapledOCSPResponse);
+  if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
     rv = BuildCertChain(trustDomain, cert->derCert, time,
                         EndEntityOrCA::MustBeEndEntity, ku2,
                         eku, requiredPolicy, stapledOCSPResponse);
-    if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+    if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity, ku3,
                           eku, requiredPolicy, stapledOCSPResponse);
-      if (rv != SECSuccess) {
-        PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
+      if (rv != Success) {
+        rv = Result::ERROR_INADEQUATE_KEY_USAGE;
       }
     }
   }
   return rv;
 }
 
 SECStatus
 CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
@@ -217,17 +218,17 @@ CertVerifier::VerifyCert(CERTCertificate
     = !mOCSPDownloadEnabled ||
       (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
     : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
                                 : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
 
   ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled
                                                   : ocsp_get_disabled;
 
-  SECStatus rv;
+  Result rv;
 
   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, nullptr,
                                        builtChain);
@@ -243,43 +244,42 @@ CertVerifier::VerifyCert(CERTCertificate
       // 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.
 
 #ifndef MOZ_NO_EV_CERTS
       // Try to validate for EV first.
       CertPolicyId evPolicy;
       SECOidTag evPolicyOidTag;
-      rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
-      if (rv == SECSuccess) {
+      SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
+      if (srv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
                       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);
-        if (rv == SECSuccess) {
+        if (rv == Success) {
           if (evOidPolicy) {
             *evOidPolicy = evPolicyOidTag;
           }
           break;
         }
       }
 #endif
 
       if (flags & FLAG_MUST_BE_EV) {
-        PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
-        rv = SECFailure;
+        rv = Result::ERROR_POLICY_VALIDATION_FAILED;
         break;
       }
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig, &callbackContainer,
                                        builtChain);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
@@ -322,17 +322,17 @@ CertVerifier::VerifyCert(CERTCertificate
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig, nullptr,
                                        builtChain);
       rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
-      if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+      if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, cert->derCert, time,
                             EndEntityOrCA::MustBeEndEntity,
                             KeyUsage::keyAgreement, // ECDH/DH
                             KeyPurposeId::id_kp_emailProtection,
                             CertPolicyId::anyPolicy, stapledOCSPResponse);
       }
       break;
     }
@@ -368,43 +368,47 @@ CertVerifier::VerifyCert(CERTCertificate
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg,
                                     ocspGETConfig, nullptr, builtChain);
       rv = BuildCertChain(sslTrust, cert->derCert, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse);
-      if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
+      if (rv == Result::ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
                                         pinArg, ocspGETConfig, nullptr,
                                         builtChain);
         rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA,
                             keyUsage, eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse);
-        if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
+        if (rv == Result::ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
                                                   pinArg, ocspGETConfig,
                                                   nullptr, builtChain);
           rv = BuildCertChain(objectSigningTrust, cert->derCert, time,
                               endEntityOrCA, keyUsage, eku,
                               CertPolicyId::anyPolicy, stapledOCSPResponse);
         }
       }
 
       break;
     }
 
     default:
-      PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-      return SECFailure;
+      rv = Result::FATAL_ERROR_INVALID_ARGS;
   }
 
-  return rv;
+  if (rv != Success) {
+    PR_SetError(MapResultToPRErrorCode(rv), 0);
+    return SECFailure;
+  }
+
+  return SECSuccess;
 }
 
 SECStatus
 CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
                      /*optional*/ const SECItem* stapledOCSPResponse,
                                   PRTime time,
                      /*optional*/ void* pinarg,
                                   const char* hostname,
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -12,16 +12,17 @@
 #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/pkixnss.h"
 #include "pkix/ScopedPtr.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "ScopedNSSTypes.h"
 #include "secerr.h"
 #include "secmod.h"
 
@@ -101,17 +102,17 @@ static const uint8_t PERMIT_FRANCE_GOV_N
                        "\x30\x05\x82\x03" ".tf";
 
 static const SECItem PERMIT_FRANCE_GOV_NAME_CONSTRAINTS = {
   siBuffer,
   const_cast<uint8_t*>(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA),
   sizeof(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA) - 1
 };
 
-SECStatus
+Result
 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."
   ScopedCERTCertList
     candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                           &encodedIssuerName, time, true));
@@ -119,62 +120,59 @@ NSSCertDBTrustDomain::FindIssuer(const S
     for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
          !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
       const SECItem* additionalNameConstraints = nullptr;
       // TODO: Use CERT_CompareName or equivalent
       if (SECITEM_ItemsAreEqual(&encodedIssuerName, &ANSSI_SUBJECT)) {
         additionalNameConstraints = &PERMIT_FRANCE_GOV_NAME_CONSTRAINTS;
       }
       bool keepGoing;
-      SECStatus srv = checker.Check(n->cert->derCert,
-                                    additionalNameConstraints, keepGoing);
-      if (srv != SECSuccess) {
-        return SECFailure;
+      Result rv = checker.Check(n->cert->derCert,
+                                additionalNameConstraints, keepGoing);
+      if (rv != Success) {
+        return rv;
       }
       if (!keepGoing) {
         break;
       }
     }
   }
 
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
                                    const CertPolicyId& policy,
                                    const SECItem& candidateCertDER,
                                    /*out*/ TrustLevel* trustLevel)
 {
   PR_ASSERT(trustLevel);
-
   if (!trustLevel) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
 #ifdef MOZ_NO_EV_CERTS
   if (!policy.IsAnyPolicy()) {
-    PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
-    return SECFailure;
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
 #endif
 
   // XXX: This would be cleaner and more efficient if we could get the trust
   // information without constructing a CERTCertificate here, but NSS doesn't
   // expose it in any other easy-to-use fashion. The use of
   // CERT_NewTempCertificate to get a CERTCertificate shouldn't be a
   // performance problem because NSS will just find the existing
   // CERTCertificate in its in-memory cache and return it.
   ScopedCERTCertificate candidateCert(
     CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                             const_cast<SECItem*>(&candidateCertDER), nullptr,
                             false, true));
   if (!candidateCert) {
-    return SECFailure;
+    return MapPRErrorCodeToResult(PR_GetError());
   }
 
   // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
   // SECSuccess means that there is a trust record and SECFailure means there
   // is not a trust record. I looked at NSS's internal uses of
   // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
   // "We have a trust record."
   CERTCertTrust trust;
@@ -187,49 +185,49 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
     // relevant trust bit isn't set then that means the cert must be considered
     // distrusted.
     PRUint32 relevantTrustBit =
       endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA
                                                : CERTDB_TRUSTED;
     if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
             == CERTDB_TERMINAL_RECORD) {
       *trustLevel = TrustLevel::ActivelyDistrusted;
-      return SECSuccess;
+      return Success;
     }
 
     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
     // needed to consider end-entity certs to be their own trust anchors since
     // Gecko implemented nsICertOverrideService.
     if (flags & CERTDB_TRUSTED_CA) {
       if (policy.IsAnyPolicy()) {
         *trustLevel = TrustLevel::TrustAnchor;
-        return SECSuccess;
+        return Success;
       }
 #ifndef MOZ_NO_EV_CERTS
       if (CertIsAuthoritativeForEVPolicy(candidateCert.get(), policy)) {
         *trustLevel = TrustLevel::TrustAnchor;
-        return SECSuccess;
+        return Success;
       }
 #endif
     }
   }
 
   *trustLevel = TrustLevel::InheritsTrust;
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData,
                                        const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                            mPinArg);
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::DigestBuf(const SECItem& item,
                                 /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
 }
 
 
 static PRIntervalTime
@@ -252,68 +250,66 @@ OCSPFetchingTypeToTimeoutTime(NSSCertDBT
   return PR_SecondsToInterval(2);
 }
 
 // Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
 // CERT_GetGeneralNameByType. Returns SECFailure on error, SECSuccess
 // with url == nullptr when an OCSP URI was not found, and SECSuccess with
 // url != nullptr when an OCSP URI was found. The output url will be owned
 // by the arena.
-static SECStatus
+static Result
 GetOCSPAuthorityInfoAccessLocation(PLArenaPool* arena,
                                    const SECItem& aiaExtension,
                                    /*out*/ char const*& url)
 {
   url = nullptr;
 
   // TODO(bug 1028380): Remove the const_cast.
   CERTAuthInfoAccess** aia = CERT_DecodeAuthInfoAccessExtension(
                                 arena,
                                 const_cast<SECItem*>(&aiaExtension));
   if (!aia) {
-    PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
-    return SECFailure;
+    return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
   }
   for (size_t i = 0; aia[i]; ++i) {
     if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
       // NSS chooses the **last** OCSP URL; we choose the **first**
       CERTGeneralName* current = aia[i]->location;
       if (!current) {
         continue;
       }
       do {
         if (current->type == certURI) {
           const SECItem& location = current->name.other;
           // (location.len + 1) must be small enough to fit into a uint32_t,
           // but we limit it to a smaller bound to reduce OOM risk.
           if (location.len > 1024 || memchr(location.data, 0, location.len)) {
             // Reject embedded nulls. (NSS doesn't do this)
-            PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
-            return SECFailure;
+            return Result::ERROR_CERT_BAD_ACCESS_LOCATION;
           }
           // Copy the non-null-terminated SECItem into a null-terminated string.
           char* nullTerminatedURL(static_cast<char*>(
                                     PORT_ArenaAlloc(arena, location.len + 1)));
           if (!nullTerminatedURL) {
-            return SECFailure;
+            return Result::FATAL_ERROR_NO_MEMORY;
           }
           memcpy(nullTerminatedURL, location.data, location.len);
           nullTerminatedURL[location.len] = 0;
           url = nullTerminatedURL;
-          return SECSuccess;
+          return Success;
         }
         current = CERT_GetNextGeneralName(current);
       } while (current != aia[i]->location);
     }
   }
 
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
                                       const CertID& certID, PRTime time,
                          /*optional*/ const SECItem* stapledOCSPResponse,
                          /*optional*/ const SECItem* aiaExtension)
 {
   // Actively distrusted certificates will have already been blocked by
   // GetCertTrust.
 
@@ -333,363 +329,345 @@ NSSCertDBTrustDomain::CheckRevocation(En
   }
 
   // If we have a stapled OCSP response then the verification of that response
   // determines the result unless the OCSP response is expired. We make an
   // exception for expired responses because some servers, nginx in particular,
   // are known to serve expired responses due to bugs.
   // We keep track of the result of verifying the stapled response but don't
   // immediately return failure if the response has expired.
-  PRErrorCode stapledOCSPResponseErrorCode = 0;
+  Result stapledOCSPResponseResult = Success;
   if (stapledOCSPResponse) {
     PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
     bool expired;
-    SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
-                                                          maxOCSPLifetimeInDays,
-                                                          *stapledOCSPResponse,
-                                                          ResponseWasStapled,
-                                                          expired);
-    if (rv == SECSuccess) {
+    stapledOCSPResponseResult =
+      VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
+                                             maxOCSPLifetimeInDays,
+                                             *stapledOCSPResponse,
+                                             ResponseWasStapled, expired);
+    if (stapledOCSPResponseResult == Success) {
       // stapled OCSP response present and good
       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: stapled OCSP response: good"));
-      return rv;
+      return Success;
     }
-    stapledOCSPResponseErrorCode = PR_GetError();
-    if (stapledOCSPResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE ||
+    if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
         expired) {
       // stapled OCSP response present but expired
       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: expired stapled OCSP response"));
     } else {
       // stapled OCSP response present but invalid for some reason
       Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: stapled OCSP response: failure"));
-      return rv;
+      return stapledOCSPResponseResult;
     }
   } else {
     // no stapled OCSP response
     Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: no stapled OCSP response"));
   }
 
-  PRErrorCode cachedResponseErrorCode = 0;
+  Result cachedResponseResult = Success;
   PRTime cachedResponseValidThrough = 0;
   bool cachedResponsePresent = mOCSPCache.Get(certID,
-                                              cachedResponseErrorCode,
+                                              cachedResponseResult,
                                               cachedResponseValidThrough);
   if (cachedResponsePresent) {
-    if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) {
+    if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: cached OCSP response: good"));
-      return SECSuccess;
+      return Success;
     }
     // If we have a cached revoked response, use it.
-    if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
+    if (cachedResponseResult == Result::ERROR_REVOKED_CERTIFICATE) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: cached OCSP response: revoked"));
-      PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
-      return SECFailure;
+      return Result::ERROR_REVOKED_CERTIFICATE;
     }
     // The cached response may indicate an unknown certificate or it may be
     // expired. Don't return with either of these statuses yet - we may be
     // able to fetch a more recent one.
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid "
-           "until %lld", cachedResponseErrorCode, cachedResponseValidThrough));
+           "until %lld", cachedResponseResult, cachedResponseValidThrough));
     // When a good cached response has expired, it is more convenient
     // to convert that to an error code and just deal with
-    // cachedResponseErrorCode from here on out.
-    if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) {
-      cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE;
+    // cachedResponseResult from here on out.
+    if (cachedResponseResult == Success && cachedResponseValidThrough < time) {
+      cachedResponseResult = Result::ERROR_OCSP_OLD_RESPONSE;
     }
     // We may have a cached indication of server failure. Ignore it if
     // it has expired.
-    if (cachedResponseErrorCode != 0 &&
-        cachedResponseErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT &&
-        cachedResponseErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE &&
+    if (cachedResponseResult != Success &&
+        cachedResponseResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
+        cachedResponseResult != Result::ERROR_OCSP_OLD_RESPONSE &&
         cachedResponseValidThrough < time) {
-      cachedResponseErrorCode = 0;
+      cachedResponseResult = Success;
       cachedResponsePresent = false;
     }
   } else {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: no cached OCSP response"));
   }
-  // At this point, if and only if cachedErrorResponseCode is 0, there was no
+  // At this point, if and only if cachedErrorResult is Success, there was no
   // cached response.
-  PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) ||
-            (cachedResponsePresent && cachedResponseErrorCode != 0));
+  PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
+            (cachedResponsePresent && cachedResponseResult != Success));
 
   // TODO: We still need to handle the fallback for expired responses. But,
   // if/when we disable OCSP fetching by default, it would be ambiguous whether
   // security.OCSP.enable==0 means "I want the default" or "I really never want
   // you to ever fetch OCSP."
 
   if ((mOCSPFetching == NeverFetchOCSP) ||
       (endEntityOrCA == EndEntityOrCA::MustBeCA &&
        (mOCSPFetching == FetchOCSPForDVHardFail ||
         mOCSPFetching == FetchOCSPForDVSoftFail))) {
     // We're not going to be doing any fetching, so if there was a cached
     // "unknown" response, say so.
-    if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
-      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
-      return SECFailure;
+    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+      return Result::ERROR_OCSP_UNKNOWN_CERT;
     }
     // If we're doing hard-fail, we want to know if we have a cached response
     // that has expired.
     if (mOCSPFetching == FetchOCSPForDVHardFail &&
-        cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
-      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
-      return SECFailure;
+        cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+      return Result::ERROR_OCSP_OLD_RESPONSE;
     }
 
-    return SECSuccess;
+    return Success;
   }
 
   if (mOCSPFetching == LocalOnlyOCSPForEV) {
-    PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode
-                                             : SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
-    return SECFailure;
+    if (cachedResponseResult != Success) {
+      return cachedResponseResult;
+    }
+    return Result::ERROR_OCSP_UNKNOWN_CERT;
   }
 
   ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
-    return SECFailure;
+    return Result::FATAL_ERROR_NO_MEMORY;
   }
 
+  Result rv;
   const char* url = nullptr; // owned by the arena
 
   if (aiaExtension) {
-    if (GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url)
-          != SECSuccess) {
-      return SECFailure;
+    rv = GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url);
+    if (rv != Success) {
+      return rv;
     }
   }
 
   if (!url) {
     if (mOCSPFetching == FetchOCSPForEV ||
-        cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
-      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
-      return SECFailure;
+        cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
+      return Result::ERROR_OCSP_UNKNOWN_CERT;
     }
-    if (cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
-      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
-      return SECFailure;
+    if (cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+      return Result::ERROR_OCSP_OLD_RESPONSE;
     }
-    if (stapledOCSPResponseErrorCode != 0) {
-      PR_SetError(stapledOCSPResponseErrorCode, 0);
-      return SECFailure;
+    if (stapledOCSPResponseResult != Success) {
+      return stapledOCSPResponseResult;
     }
 
     // Nothing to do if we don't have an OCSP responder URI for the cert; just
     // assume it is good. Note that this is the confusing, but intended,
     // interpretation of "strict" revocation checking in the face of a
     // certificate that lacks an OCSP responder URI.
-    return SECSuccess;
+    return Success;
   }
 
   // Only request a response if we didn't have a cached indication of failure
   // (don't keep requesting responses from a failing server).
   const SECItem* response;
   bool attemptedRequest;
-  // Initialize error here to a value we check that it isn't later, because
-  // the compiler on windows isn't smart enough to figure out that it's
-  // always initialized before it gets used.
-  PRErrorCode error = 0;
-  if (cachedResponseErrorCode == 0 ||
-      cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
-      cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
-    const SECItem* request(CreateEncodedOCSPRequest(*this, arena.get(),
-                                                    certID));
-    if (!request) {
-      return SECFailure;
+  if (cachedResponseResult == Success ||
+      cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT ||
+      cachedResponseResult == Result::ERROR_OCSP_OLD_RESPONSE) {
+    uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
+    size_t ocspRequestLength;
+    rv = CreateEncodedOCSPRequest(*this, certID, ocspRequest,
+                                  ocspRequestLength);
+    if (rv != Success) {
+      return rv;
     }
-
-    response = DoOCSPRequest(arena.get(), url, request,
+    SECItem ocspRequestItem = {
+      siBuffer,
+      ocspRequest,
+      static_cast<unsigned int>(ocspRequestLength)
+    };
+    response = DoOCSPRequest(arena.get(), url, &ocspRequestItem,
                              OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
                              mOCSPGetConfig == CertVerifier::ocsp_get_enabled);
     if (!response) {
-      error = PR_GetError();
+      rv = MapPRErrorCodeToResult(PR_GetError());
     }
     attemptedRequest = true;
   } else {
-    error = cachedResponseErrorCode;
+    rv = cachedResponseResult;
     response = nullptr;
     attemptedRequest = false;
   }
 
   // If we don't have a response, either something went wrong when fetching it
   // or we didn't attempt to fetch a response because of a failing responder.
   if (!response) {
-    MOZ_ASSERT(error != 0);
-    // If we haven't actually attempted to fetch a response, we have nothing
-    // new to tell the cache. Otherwise, we do.
+    Result error = rv;
     if (attemptedRequest) {
       PRTime timeout = time + ServerFailureDelay;
-      SECStatus rv = mOCSPCache.Put(certID, error, time, timeout);
-      if (rv != SECSuccess) {
-        return SECFailure;
+      rv = mOCSPCache.Put(certID, error, time, timeout);
+      if (rv != Success) {
+        return rv;
       }
     }
-    PR_SetError(error, 0);
     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure after "
               "OCSP request failure"));
-      return SECFailure;
+      return error;
     }
-    if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+    if (cachedResponseResult == Result::ERROR_OCSP_UNKNOWN_CERT) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure from cached "
               "response after OCSP request failure"));
-      PR_SetError(cachedResponseErrorCode, 0);
-      return SECFailure;
+      return cachedResponseResult;
     }
-    if (stapledOCSPResponseErrorCode != 0) {
+    if (stapledOCSPResponseResult != Success) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure from expired "
               "stapled response after OCSP request failure"));
-      PR_SetError(stapledOCSPResponseErrorCode, 0);
-      return SECFailure;
+      return stapledOCSPResponseResult;
     }
 
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECSuccess after "
             "OCSP request failure"));
-    return SECSuccess; // Soft fail -> success :(
+    return Success; // Soft fail -> success :(
   }
 
   // If the response from the network has expired but indicates a revoked
   // or unknown certificate, PR_GetError() will return the appropriate error.
   // We actually ignore expired here.
   bool expired;
-  SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
-                                                        maxOCSPLifetimeInDays,
-                                                        *response,
-                                                        ResponseIsFromNetwork,
-                                                        expired);
-  if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
+  rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
+                                              maxOCSPLifetimeInDays,
+                                              *response, ResponseIsFromNetwork,
+                                              expired);
+  if (rv == Success || mOCSPFetching != FetchOCSPForDVSoftFail) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
       ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
     return rv;
   }
 
-  error = PR_GetError();
-  if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
-      error == SEC_ERROR_REVOKED_CERTIFICATE) {
+  if (rv == Result::ERROR_OCSP_UNKNOWN_CERT ||
+      rv == Result::ERROR_REVOKED_CERTIFICATE) {
     return rv;
   }
-
-  if (stapledOCSPResponseErrorCode != 0) {
+  if (stapledOCSPResponseResult != Success) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
             "response after OCSP request verification failure"));
-    PR_SetError(stapledOCSPResponseErrorCode, 0);
-    return SECFailure;
+    return stapledOCSPResponseResult;
   }
 
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
-  return SECSuccess; // Soft fail -> success :(
+  return Success; // Soft fail -> success :(
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   const CertID& certID, PRTime time, uint16_t maxLifetimeInDays,
   const SECItem& encodedResponse, EncodedResponseSource responseSource,
   /*out*/ bool& expired)
 {
   PRTime thisUpdate = 0;
   PRTime validThrough = 0;
-  SECStatus rv = VerifyEncodedOCSPResponse(*this, certID, time,
-                                           maxLifetimeInDays, encodedResponse,
-                                           expired, &thisUpdate, &validThrough);
-  PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError());
+  Result rv = VerifyEncodedOCSPResponse(*this, certID, time,
+                                        maxLifetimeInDays, encodedResponse,
+                                        expired, &thisUpdate, &validThrough);
   // If a response was stapled and expired, we don't want to cache it. Return
   // early to simplify the logic here.
   if (responseSource == ResponseWasStapled && expired) {
-    PR_ASSERT(rv != SECSuccess);
+    PR_ASSERT(rv != Success);
     return rv;
   }
   // validThrough is only trustworthy if the response successfully verifies
   // or it indicates a revoked or unknown certificate.
   // If this isn't the case, store an indication of failure (to prevent
   // repeatedly requesting a response from a failing server).
-  if (rv != SECSuccess && error != SEC_ERROR_REVOKED_CERTIFICATE &&
-      error != SEC_ERROR_OCSP_UNKNOWN_CERT) {
+  if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
+      rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
     validThrough = time + ServerFailureDelay;
   }
   if (responseSource == ResponseIsFromNetwork ||
-      rv == SECSuccess ||
-      error == SEC_ERROR_REVOKED_CERTIFICATE ||
-      error == SEC_ERROR_OCSP_UNKNOWN_CERT) {
+      rv == Success ||
+      rv == Result::ERROR_REVOKED_CERTIFICATE ||
+      rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: caching OCSP response"));
-    if (mOCSPCache.Put(certID, error, thisUpdate, validThrough) != SECSuccess) {
-      return SECFailure;
+    Result putRV = mOCSPCache.Put(certID, rv, thisUpdate, validThrough);
+    if (putRV != Success) {
+      return putRV;
     }
   }
 
-  // If the verification failed, re-set to that original error
-  // (the call to Put may have un-set it).
-  if (rv != SECSuccess) {
-    PR_SetError(error, 0);
-  }
   return rv;
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
       ("NSSCertDBTrustDomain: Top of IsChainValid mCheckChainCallback=%p",
        mCheckChainCallback));
 
   if (!mBuiltChain && !mCheckChainCallback) {
     // No need to create a CERTCertList, and nothing else to do.
-    return SECSuccess;
+    return Success;
   }
 
   ScopedCERTCertList certList;
-  SECStatus rv = ConstructCERTCertListFromReversedDERArray(certArray, certList);
-  if (rv != SECSuccess) {
-    return rv;
+  SECStatus srv = ConstructCERTCertListFromReversedDERArray(certArray,
+                                                            certList);
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
   }
 
   if (mCheckChainCallback) {
     if (!mCheckChainCallback->isChainValid) {
-      PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-      return SECFailure;
+      return Result::FATAL_ERROR_INVALID_ARGS;
     }
     PRBool chainOK;
-    rv = (mCheckChainCallback->isChainValid)(
+    srv = (mCheckChainCallback->isChainValid)(
             mCheckChainCallback->isChainValidArg, certList.get(), &chainOK);
-    if (rv != SECSuccess) {
-      return rv;
+    if (srv != SECSuccess) {
+      return MapPRErrorCodeToResult(PR_GetError());
     }
     if (!chainOK) {
-      PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
-      return SECFailure;
+      return Result::ERROR_KEY_PINNING_FAILURE;
     }
   }
 
   if (mBuiltChain) {
     *mBuiltChain = certList.forget();
   }
 
-  return SECSuccess;
+  return Success;
 }
 
-SECStatus
+Result
 NSSCertDBTrustDomain::CheckPublicKey(const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
 }
 
 namespace {
 
 static char*
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -36,61 +36,67 @@ void UnloadLoadableRoots(const char* mod
 char* DefaultServerNicknameForCert(CERTCertificate* cert);
 
 void SaveIntermediateCerts(const ScopedCERTCertList& certList);
 
 class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
 {
 
 public:
+  typedef mozilla::pkix::Result Result;
+
   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,
           /*optional*/ CERTChainVerifyCallback* checkChainCallback = nullptr,
           /*optional*/ ScopedCERTCertList* builtChain = nullptr);
 
-  virtual SECStatus FindIssuer(const SECItem& encodedIssuerName,
-                               IssuerChecker& checker, PRTime time);
+  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+                            IssuerChecker& checker,
+                            PRTime time) MOZ_OVERRIDE;
 
-  virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                                 const mozilla::pkix::CertPolicyId& policy,
-                                 const SECItem& candidateCertDER,
-                         /*out*/ mozilla::pkix::TrustLevel* trustLevel);
+  virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                              const mozilla::pkix::CertPolicyId& policy,
+                              const SECItem& candidateCertDER,
+                              /*out*/ mozilla::pkix::TrustLevel* trustLevel)
+                              MOZ_OVERRIDE;
 
-  virtual SECStatus VerifySignedData(
-                      const mozilla::pkix::SignedDataWithSignature& signedData,
-                      const SECItem& subjectPublicKeyInfo);
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+                                MOZ_OVERRIDE;
 
-  virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
-                              size_t digestBufLen);
+  virtual Result VerifySignedData(
+                   const mozilla::pkix::SignedDataWithSignature& signedData,
+                   const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
+
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen) MOZ_OVERRIDE;
 
-  virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                                    const mozilla::pkix::CertID& certID,
-                                    PRTime time,
-                       /*optional*/ const SECItem* stapledOCSPResponse,
-                       /*optional*/ const SECItem* aiaExtension);
+  virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                                 const mozilla::pkix::CertID& certID,
+                                 PRTime time,
+                    /*optional*/ const SECItem* stapledOCSPResponse,
+                    /*optional*/ const SECItem* aiaExtension) MOZ_OVERRIDE;
 
-  virtual SECStatus IsChainValid(const mozilla::pkix::DERArray& certChain);
-
-  virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo);
+  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain)
+                              MOZ_OVERRIDE;
 
 private:
   enum EncodedResponseSource {
     ResponseIsFromNetwork = 1,
     ResponseWasStapled = 2
   };
   static const PRTime ServerFailureDelay = 5 * 60 * PR_USEC_PER_SEC;
-  SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
+  Result VerifyAndMaybeCacheEncodedOCSPResponse(
     const mozilla::pkix::CertID& certID, PRTime time,
     uint16_t maxLifetimeInDays, const SECItem& encodedResponse,
     EncodedResponseSource responseSource, /*out*/ bool& expired);
 
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
--- a/security/certverifier/OCSPCache.cpp
+++ b/security/certverifier/OCSPCache.cpp
@@ -23,17 +23,17 @@
  */
 
 #include "OCSPCache.h"
 
 #include <limits>
 
 #include "NSSCertDBTrustDomain.h"
 #include "pk11pub.h"
-#include "pkix/pkixtypes.h"
+#include "pkix/pkixnss.h"
 #include "ScopedNSSTypes.h"
 #include "secerr.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gCertVerifierLog;
 #endif
 
 using namespace mozilla::pkix;
@@ -79,24 +79,28 @@ CertIDHash(SHA384Buffer& buf, const Cert
   uint32_t outLen = 0;
   rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH);
   if (outLen != SHA384_LENGTH) {
     return SECFailure;
   }
   return rv;
 }
 
-SECStatus
-OCSPCache::Entry::Init(const CertID& aCertID, PRErrorCode aErrorCode,
+Result
+OCSPCache::Entry::Init(const CertID& aCertID, Result aResult,
                        PRTime aThisUpdate, PRTime aValidThrough)
 {
-  mErrorCode = aErrorCode;
+  mResult = aResult;
   mThisUpdate = aThisUpdate;
   mValidThrough = aValidThrough;
-  return CertIDHash(mIDHash, aCertID);
+  SECStatus srv = CertIDHash(mIDHash, aCertID);
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+  return Success;
 }
 
 OCSPCache::OCSPCache()
   : mMutex("OCSPCache-mutex")
 {
 }
 
 OCSPCache::~OCSPCache()
@@ -145,123 +149,117 @@ OCSPCache::MakeMostRecentlyUsed(size_t a
   Entry* entry = mEntries[aIndex];
   // Since mEntries is sorted with the most-recently-used entry at the end,
   // aIndex is likely to be near the end, so this is likely to be fast.
   mEntries.erase(mEntries.begin() + aIndex);
   mEntries.append(entry);
 }
 
 bool
-OCSPCache::Get(const CertID& aCertID, PRErrorCode& aErrorCode,
-               PRTime& aValidThrough)
+OCSPCache::Get(const CertID& aCertID, Result& aResult, PRTime& aValidThrough)
 {
   MutexAutoLock lock(mMutex);
 
   size_t index;
   if (!FindInternal(aCertID, index, lock)) {
     LogWithCertID("OCSPCache::Get(%p) not in cache", aCertID);
     return false;
   }
   LogWithCertID("OCSPCache::Get(%p) in cache", aCertID);
-  aErrorCode = mEntries[index]->mErrorCode;
+  aResult = mEntries[index]->mResult;
   aValidThrough = mEntries[index]->mValidThrough;
   MakeMostRecentlyUsed(index, lock);
   return true;
 }
 
-SECStatus
-OCSPCache::Put(const CertID& aCertID, PRErrorCode aErrorCode,
+Result
+OCSPCache::Put(const CertID& aCertID, Result aResult,
                PRTime aThisUpdate, PRTime aValidThrough)
 {
   MutexAutoLock lock(mMutex);
 
   size_t index;
   if (FindInternal(aCertID, index, lock)) {
     // Never replace an entry indicating a revoked certificate.
-    if (mEntries[index]->mErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
+    if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) {
       LogWithCertID("OCSPCache::Put(%p) already in cache as revoked - "
                     "not replacing", aCertID);
       MakeMostRecentlyUsed(index, lock);
-      return SECSuccess;
+      return Success;
     }
 
     // Never replace a newer entry with an older one unless the older entry
     // indicates a revoked certificate, which we want to remember.
     if (mEntries[index]->mThisUpdate > aThisUpdate &&
-        aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) {
+        aResult != Result::ERROR_REVOKED_CERTIFICATE) {
       LogWithCertID("OCSPCache::Put(%p) already in cache with more recent "
                     "validity - not replacing", aCertID);
       MakeMostRecentlyUsed(index, lock);
-      return SECSuccess;
+      return Success;
     }
 
     // Only known good responses or responses indicating an unknown
     // or revoked certificate should replace previously known responses.
-    if (aErrorCode != 0 && aErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT &&
-        aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) {
+    if (aResult != Success &&
+        aResult != Result::ERROR_OCSP_UNKNOWN_CERT &&
+        aResult != Result::ERROR_REVOKED_CERTIFICATE) {
       LogWithCertID("OCSPCache::Put(%p) already in cache - not replacing "
                     "with less important status", aCertID);
       MakeMostRecentlyUsed(index, lock);
-      return SECSuccess;
+      return Success;
     }
 
     LogWithCertID("OCSPCache::Put(%p) already in cache - replacing", aCertID);
-    mEntries[index]->mErrorCode = aErrorCode;
+    mEntries[index]->mResult = aResult;
     mEntries[index]->mThisUpdate = aThisUpdate;
     mEntries[index]->mValidThrough = aValidThrough;
     MakeMostRecentlyUsed(index, lock);
-    return SECSuccess;
+    return Success;
   }
 
   if (mEntries.length() == MaxEntries) {
     LogWithCertID("OCSPCache::Put(%p) too full - evicting an entry", aCertID);
     for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end();
          toEvict++) {
       // Never evict an entry that indicates a revoked or unknokwn certificate,
       // because revoked responses are more security-critical to remember.
-      if ((*toEvict)->mErrorCode != SEC_ERROR_REVOKED_CERTIFICATE &&
-          (*toEvict)->mErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT) {
+      if ((*toEvict)->mResult != Result::ERROR_REVOKED_CERTIFICATE &&
+          (*toEvict)->mResult != Result::ERROR_OCSP_UNKNOWN_CERT) {
         delete *toEvict;
         mEntries.erase(toEvict);
         break;
       }
     }
     // Well, we tried, but apparently everything is revoked or unknown.
     // We don't want to remove a cached revoked or unknown response. If we're
     // trying to insert a good response, we can just return "successfully"
     // without doing so. This means we'll lose some speed, but it's not a
     // security issue. If we're trying to insert a revoked or unknown response,
     // we can't. We should return with an error that causes the current
     // verification to fail.
     if (mEntries.length() == MaxEntries) {
-      if (aErrorCode != 0) {
-        PR_SetError(aErrorCode, 0);
-        return SECFailure;
-      }
-      return SECSuccess;
+      return aResult;
     }
   }
 
-  Entry* newEntry = new Entry();
+  Entry* newEntry = new (std::nothrow) Entry();
   // Normally we don't have to do this in Gecko, because OOM is fatal.
   // However, if we want to embed this in another project, OOM might not
   // be fatal, so handle this case.
   if (!newEntry) {
-    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_NO_MEMORY;
   }
-  SECStatus rv = newEntry->Init(aCertID, aErrorCode, aThisUpdate,
-                                aValidThrough);
-  if (rv != SECSuccess) {
+  Result rv = newEntry->Init(aCertID, aResult, aThisUpdate, aValidThrough);
+  if (rv != Success) {
     delete newEntry;
     return rv;
   }
   mEntries.append(newEntry);
   LogWithCertID("OCSPCache::Put(%p) added to cache", aCertID);
-  return SECSuccess;
+  return Success;
 }
 
 void
 OCSPCache::Clear()
 {
   MutexAutoLock lock(mMutex);
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("OCSPCache::Clear: clearing cache"));
   // First go through and delete the memory being pointed to by the pointers
--- a/security/certverifier/OCSPCache.h
+++ b/security/certverifier/OCSPCache.h
@@ -23,16 +23,17 @@
  */
 
 #ifndef mozilla_psm_OCSPCache_h
 #define mozilla_psm_OCSPCache_h
 
 #include "hasht.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Vector.h"
+#include "pkix/Result.h"
 #include "prerror.h"
 #include "prtime.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix {
 struct CertID;
 } } // namespace mozilla::pkix
 
@@ -53,41 +54,44 @@ public:
   OCSPCache();
   ~OCSPCache();
 
   // Returns true if the status of the given certificate (issued by the given
   // issuer) is in the cache, and false otherwise.
   // If it is in the cache, returns by reference the error code of the cached
   // status and the time through which the status is considered trustworthy.
   bool Get(const mozilla::pkix::CertID& aCertID,
-           /* out */ PRErrorCode& aErrorCode, /* out */ PRTime& aValidThrough);
+           /*out*/ mozilla::pkix::Result& aResult,
+           /*out*/ PRTime& aValidThrough);
 
   // Caches the status of the given certificate (issued by the given issuer).
   // The status is considered trustworthy through the given time.
   // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not
   // be replaced or evicted.
   // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not
   // be evicted when the cache is full.
   // A status with a more recent thisUpdate will not be replaced with a
   // status with a less recent thisUpdate unless the less recent status
   // indicates the certificate is revoked.
-  SECStatus Put(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode,
-                PRTime aThisUpdate, PRTime aValidThrough);
+  mozilla::pkix::Result Put(const mozilla::pkix::CertID& aCertID,
+                            mozilla::pkix::Result aResult, PRTime aThisUpdate,
+                            PRTime aValidThrough);
 
   // Removes everything from the cache.
   void Clear();
 
 private:
   class Entry
   {
   public:
-    SECStatus Init(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode,
-                   PRTime aThisUpdate, PRTime aValidThrough);
+    mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID,
+                               mozilla::pkix::Result aResult,
+                               PRTime aThisUpdate, PRTime aValidThrough);
 
-    PRErrorCode mErrorCode;
+    mozilla::pkix::Result mResult;
     PRTime mThisUpdate;
     PRTime mValidThrough;
     // The SHA-384 hash of the concatenation of the DER encodings of the
     // issuer name and issuer key, followed by the serial number.
     // See the documentation for CertIDHash in OCSPCache.cpp.
     SHA384Buffer mIDHash;
   };
 
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -27,16 +27,17 @@ DIRS += [
 ]
 
 CXXFLAGS += ['-Wall']
 if CONFIG['_MSC_VER']:
   # -Wall with Visual C++ enables too many problematic warnings
   CXXFLAGS += [
     '-wd4480', # nonstandard extension used: specifying underlying type for
                # enum 'enum'
+    '-wd4481', # nonstandard extension used: override specifier 'keyword'
     '-wd4510', # default constructor could not be generated
     '-wd4512', # assignment operator could not be generated
     '-wd4514', # 'function': unreferenced inline function has been removed
     '-wd4610', # struct 'symbol' can never be instantiated - user defined
                # constructor required
     '-wd4619', # pragma warning: there is no warning 'warning'
     '-wd4625', # copy constructor could not be generated because a base
                # class copy constructor is inaccessible or deleted
--- a/security/manager/ssl/src/NSSErrorsService.cpp
+++ b/security/manager/ssl/src/NSSErrorsService.cpp
@@ -1,50 +1,40 @@
 /* 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 "NSSErrorsService.h"
 
 #include "nsNSSComponent.h"
 #include "nsServiceManagerUtils.h"
+#include "pkix/pkixnss.h"
 #include "secerr.h"
 #include "sslerr.h"
 
 #define PIPNSS_STRBUNDLE_URL "chrome://pipnss/locale/pipnss.properties"
 #define NSSERR_STRBUNDLE_URL "chrome://pipnss/locale/nsserrors.properties"
 
 namespace mozilla {
 namespace psm {
 
-static const struct PRErrorMessage PSMErrorTableText[] = {
-  { "PSM_ERROR_KEY_PINNING_FAILURE",
-    "The server uses key pinning (HPKP) but no trusted certificate chain "
-    "could be constructed that matches the pinset. Key pinning violations "
-    "cannot be overridden." }
-};
-
-static const struct PRErrorTable PSMErrorTable = {
-  PSMErrorTableText,
-  "psmerrors",
-  nsINSSErrorsService::PSM_ERROR_BASE,
-  PR_ARRAY_SIZE(PSMErrorTableText)
-};
-
-void
-RegisterPSMErrorTable()
-{
-  PR_ErrorInstallTable(&PSMErrorTable);
-}
+static_assert(mozilla::pkix::ERROR_BASE ==
+                nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE,
+              "MOZILLA_PKIX_ERROR_BASE and "
+                "nsINSSErrorsService::MOZILLA_PKIX_ERROR_BASE do not match.");
+static_assert(mozilla::pkix::ERROR_LIMIT ==
+                nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT,
+              "MOZILLA_PKIX_ERROR_LIMIT and "
+                "nsINSSErrorsService::MOZILLA_PKIX_ERROR_LIMIT do not match.");
 
 static bool
 IsPSMError(PRErrorCode error)
 {
-  return (error >= nsINSSErrorsService::PSM_ERROR_BASE &&
-          error < nsINSSErrorsService::PSM_ERROR_LIMIT);
+  return (error >= mozilla::pkix::ERROR_BASE &&
+          error < mozilla::pkix::ERROR_LIMIT);
 }
 
 NS_IMPL_ISUPPORTS(NSSErrorsService, nsINSSErrorsService)
 
 NSSErrorsService::~NSSErrorsService() { }
 
 nsresult
 NSSErrorsService::Init()
--- a/security/manager/ssl/src/NSSErrorsService.h
+++ b/security/manager/ssl/src/NSSErrorsService.h
@@ -1,31 +1,24 @@
 /* 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 NSSErrorsService_h
 #define NSSErrorsService_h
 
 #include "nsINSSErrorsService.h"
-
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsIStringBundle.h"
 #include "prerror.h"
 
 namespace mozilla {
 namespace psm {
 
-enum PSMErrorCodes {
-  PSM_ERROR_KEY_PINNING_FAILURE = (nsINSSErrorsService::PSM_ERROR_BASE + 0)
-};
-
-void RegisterPSMErrorTable();
-
 class NSSErrorsService MOZ_FINAL : public nsINSSErrorsService
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSINSSERRORSSERVICE
 
 public:
   nsresult Init();
 
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -46,16 +46,17 @@
 #include "nsIBufEntropyCollector.h"
 #include "nsITokenPasswordDialogs.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNSSShutDown.h"
 #include "SharedSSLState.h"
 #include "NSSErrorsService.h"
 
 #include "nss.h"
+#include "pkix/pkixnss.h"
 #include "ssl.h"
 #include "sslproto.h"
 #include "secmod.h"
 #include "secerr.h"
 #include "sslerr.h"
 
 #include "nsXULAppAPI.h"
 
@@ -1204,17 +1205,17 @@ nsNSSComponent::InitializeNSS()
   setValidationOptions(true, lock);
 
   mHttpForNSS.initTable();
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
   LaunchSmartCardThreads();
 #endif
 
-  RegisterPSMErrorTable();
+  mozilla::pkix::RegisterErrorTable();
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n"));
   return NS_OK;
 }
 
 void
 nsNSSComponent::ShutdownNSS()
 {
--- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -26,30 +26,31 @@ class OCSPCacheTest : public ::testing::
       NSS_NoDB_Init(nullptr);
       mozilla::psm::InitCertVerifierLog();
     }
 
     mozilla::psm::OCSPCache cache;
 };
 
 static void
-PutAndGet(OCSPCache& cache, const CertID& certID, PRErrorCode error,
+PutAndGet(OCSPCache& cache, const CertID& certID, Result result,
           PRTime time)
 {
   // The first time is thisUpdate. The second is validUntil.
   // The caller is expecting the validUntil returned with Get
   // to be equal to the passed-in time. Since these values will
   // be different in practice, make thisUpdate less than validUntil.
   ASSERT_TRUE(time >= 10);
-  SECStatus rv = cache.Put(certID, error, time - 10, time);
-  ASSERT_TRUE(rv == SECSuccess);
-  PRErrorCode errorOut;
+  Result rv = cache.Put(certID, result, time - 10, time);
+  ASSERT_TRUE(rv == Success);
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
-  ASSERT_TRUE(error == errorOut && time == timeOut);
+  ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
+  ASSERT_EQ(result, resultOut);
+  ASSERT_EQ(time, timeOut);
 }
 
 static const SECItem fakeIssuer1 = {
   siBuffer,
   const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer1")),
   10
 };
 static const SECItem fakeKey000 = {
@@ -77,69 +78,75 @@ TEST_F(OCSPCacheTest, TestPutAndGet)
   };
   static const SECItem fakeSerial001 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")),
     3
   };
 
   SCOPED_TRACE("");
-  PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), 0, PR_Now());
-  PRErrorCode errorOut;
+  PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001),
+            Success, PR_Now());
+  Result resultOut;
   PRTime timeOut;
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
-                         errorOut, timeOut));
+                         resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestVariousGets)
 {
   SCOPED_TRACE("");
   PRTime timeIn = PR_Now();
   for (int i = 0; i < MaxCacheEntries; i++) {
     char serialBuf[8];
     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
     const SECItem fakeSerial = {
       siBuffer,
       const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)),
       4
     };
-    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
+    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+              Success, timeIn + i);
   }
 
-  PRErrorCode errorOut;
+  Result resultOut;
   PRTime timeOut;
 
   // This will be at the end of the list in the cache
   CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
-  ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(timeIn, timeOut);
   // Once we access it, it goes to the front
-  ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(timeIn, timeOut);
 
   // This will be in the middle
   static const SECItem fakeSerial0512 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0512")),
     4
   };
   CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
-  ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
-  ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
+  ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(timeIn + 512, timeOut);
+  ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(timeIn + 512, timeOut);
 
   // We've never seen this certificate
   static const SECItem fakeSerial1111 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1111")),
     4
   };
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
-                         errorOut, timeOut));
+                         resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestEviction)
 {
   SCOPED_TRACE("");
   PRTime timeIn = PR_Now();
 
   // By putting more distinct entries in the cache than it can hold,
@@ -147,99 +154,100 @@ TEST_F(OCSPCacheTest, TestEviction)
   for (int i = 0; i < MaxCacheEntries + 1; i++) {
     char serialBuf[8];
     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
     const SECItem fakeSerial = {
       siBuffer,
       const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)),
       4
     };
-    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
+    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+              Success, timeIn + i);
   }
 
-  PRErrorCode errorOut;
+  Result resultOut;
   PRTime timeOut;
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
-                         errorOut, timeOut));
+                         resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses)
 {
   SCOPED_TRACE("");
   PRTime timeIn = PR_Now();
   CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
-  PutAndGet(cache, notEvicted, SEC_ERROR_REVOKED_CERTIFICATE, timeIn);
+  PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
   // By putting more distinct entries in the cache than it can hold,
   // we cause the least recently used entry that isn't revoked to be evicted.
   for (int i = 1; i < MaxCacheEntries + 1; i++) {
     char serialBuf[8];
     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
     const SECItem fakeSerial = {
       siBuffer,
       reinterpret_cast<uint8_t*>(serialBuf),
       4
     };
-    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
+    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+              Success, timeIn + i);
   }
-  PRErrorCode errorOut;
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_TRUE(cache.Get(notEvicted, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == timeIn);
+  ASSERT_TRUE(cache.Get(notEvicted, resultOut, timeOut));
+  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+  ASSERT_EQ(timeIn, timeOut);
 
   const SECItem fakeSerial0001 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0001")),
     4
   };
   CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
-  ASSERT_FALSE(cache.Get(evicted, errorOut, timeOut));
+  ASSERT_FALSE(cache.Get(evicted, resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
 {
   SCOPED_TRACE("");
   PRTime timeIn = PR_Now();
   // Fill up the cache with revoked responses.
   for (int i = 0; i < MaxCacheEntries; i++) {
     char serialBuf[8];
     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
     const SECItem fakeSerial = {
       siBuffer,
       reinterpret_cast<uint8_t*>(serialBuf),
       4
     };
     PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
-              SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i);
+              Result::ERROR_REVOKED_CERTIFICATE, timeIn + i);
   }
   const SECItem fakeSerial1025 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1025")),
     4
   };
   CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
   // This will "succeed", allowing verification to continue. However,
   // nothing was actually put in the cache.
-  SECStatus result = cache.Put(good, 0, timeIn + 1025 - 50, timeIn + 1025);
-  ASSERT_TRUE(result == SECSuccess);
-  PRErrorCode errorOut;
+  Result result = cache.Put(good, Success, timeIn + 1025 - 50, timeIn + 1025);
+  ASSERT_EQ(Success, result);
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_FALSE(cache.Get(good, errorOut, timeOut));
+  ASSERT_FALSE(cache.Get(good, resultOut, timeOut));
 
   const SECItem fakeSerial1026 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1026")),
     4
   };
   CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
   // This will fail, causing verification to fail.
-  result = cache.Put(revoked, SEC_ERROR_REVOKED_CERTIFICATE,
+  result = cache.Put(revoked, Result::ERROR_REVOKED_CERTIFICATE,
                      timeIn + 1026 - 50, timeIn + 1026);
-  PRErrorCode error = PR_GetError();
-  ASSERT_TRUE(result == SECFailure);
-  ASSERT_TRUE(error == SEC_ERROR_REVOKED_CERTIFICATE);
+  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
 }
 
 TEST_F(OCSPCacheTest, VariousIssuers)
 {
   SCOPED_TRACE("");
   static const SECItem fakeIssuer2 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer2")),
@@ -248,68 +256,73 @@ TEST_F(OCSPCacheTest, VariousIssuers)
   static const SECItem fakeSerial001 = {
     siBuffer,
     const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")),
     3
   };
 
   PRTime timeIn = PR_Now();
   CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
-  PutAndGet(cache, subject, 0, timeIn);
-  PRErrorCode errorOut;
+  PutAndGet(cache, subject, Success, timeIn);
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_TRUE(cache.Get(subject, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
+  ASSERT_TRUE(cache.Get(subject, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(timeIn, timeOut);
   // Test that we don't match a different issuer DN
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
-                         errorOut, timeOut));
+                         resultOut, timeOut));
   // Test that we don't match a different issuer key
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
-                         errorOut, timeOut));
+                         resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, Times)
 {
   SCOPED_TRACE("");
   CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
-  PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100);
-  PutAndGet(cache, certID, 0, 200);
+  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100);
+  PutAndGet(cache, certID, Success, 200);
   // This should not override the more recent entry.
-  ASSERT_EQ(SECSuccess, cache.Put(certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100,
-                                  100));
-  PRErrorCode errorOut;
+  ASSERT_EQ(Success,
+            cache.Put(certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100, 100));
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
+  ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   // Here we see the more recent time.
-  ASSERT_TRUE(errorOut == 0 && timeOut == 200);
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(200, timeOut);
 
-  // SEC_ERROR_REVOKED_CERTIFICATE overrides everything
-  PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 50);
+  // Result::ERROR_REVOKED_CERTIFICATE overrides everything
+  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 50);
 }
 
 TEST_F(OCSPCacheTest, NetworkFailure)
 {
   SCOPED_TRACE("");
   CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
-  PutAndGet(cache, certID, PR_CONNECT_REFUSED_ERROR, 100);
-  PutAndGet(cache, certID, 0, 200);
+  PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED, 100);
+  PutAndGet(cache, certID, Success, 200);
   // This should not override the already present entry.
-  SECStatus rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 300, 350);
-  ASSERT_TRUE(rv == SECSuccess);
-  PRErrorCode errorOut;
+  ASSERT_EQ(Success,
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 300, 350));
+  Result resultOut;
   PRTime timeOut;
-  ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == 0 && timeOut == 200);
+  ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
+  ASSERT_EQ(Success, resultOut);
+  ASSERT_EQ(200, timeOut);
 
-  PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 400);
+  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 400);
   // This should not override the already present entry.
-  rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 500, 550);
-  ASSERT_TRUE(rv == SECSuccess);
-  ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == SEC_ERROR_OCSP_UNKNOWN_CERT && timeOut == 400);
+  ASSERT_EQ(Success,
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 500, 550));
+  ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
+  ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
+  ASSERT_EQ(400, timeOut);
 
-  PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 600);
+  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 600);
   // This should not override the already present entry.
-  rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 700, 750);
-  ASSERT_TRUE(rv == SECSuccess);
-  ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
-  ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == 600);
+  ASSERT_EQ(Success,
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 700, 750));
+  ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
+  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+  ASSERT_EQ(600, timeOut);
 }
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -15,17 +15,17 @@ let { ctypes } = Cu.import("resource://g
 
 let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"]
                        .getService(Ci.nsIDebug2).isDebugBuild;
 
 const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
-const PSM_ERROR_BASE = Ci.nsINSSErrorsService.PSM_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
 
 // Sort in numerical order
 const SEC_ERROR_INVALID_ARGS                            = SEC_ERROR_BASE +   5; // -8187
 const SEC_ERROR_BAD_DER                                 = SEC_ERROR_BASE +   9;
 const SEC_ERROR_EXPIRED_CERTIFICATE                     = SEC_ERROR_BASE +  11;
 const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE +  12; // -8180
 const SEC_ERROR_UNKNOWN_ISSUER                          = SEC_ERROR_BASE +  13;
 const SEC_ERROR_BAD_DATABASE                            = SEC_ERROR_BASE +  18;
@@ -53,17 +53,17 @@ const SEC_ERROR_OCSP_OLD_RESPONSE       
 const SEC_ERROR_OCSP_INVALID_SIGNING_CERT               = SEC_ERROR_BASE + 144;
 const SEC_ERROR_POLICY_VALIDATION_FAILED                = SEC_ERROR_BASE + 160; // -8032
 const SEC_ERROR_OCSP_BAD_SIGNATURE                      = SEC_ERROR_BASE + 157;
 const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED       = SEC_ERROR_BASE + 176;
 const SEC_ERROR_APPLICATION_CALLBACK_ERROR              = SEC_ERROR_BASE + 178;
 
 const SSL_ERROR_BAD_CERT_DOMAIN                         = SSL_ERROR_BASE +  12;
 
-const PSM_ERROR_KEY_PINNING_FAILURE                     = PSM_ERROR_BASE +   0;
+const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE            = MOZILLA_PKIX_ERROR_BASE +   0;
 
 // Supported Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
 const certificateUsageObjectSigner           = 0x0040;
--- a/security/manager/ssl/tests/unit/test_pinning.js
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -34,17 +34,17 @@ function test_strict() {
   // test mode.
   add_test(function() {
     Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
     run_next_test();
   });
 
   // Issued by otherCA, which is not in the pinset for pinning.example.com.
   add_connection_test("bad.include-subdomains.pinning.example.com",
-    getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
 
   // These domains serve certs that match the pinset.
   add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
   add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
   add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
 
   // This domain serves a cert that doesn't match the pinset, but subdomains
   // are excluded.
@@ -96,33 +96,33 @@ function test_enforce_test_mode() {
   // In enforce test mode, we always enforce all pins, even test pins.
   add_test(function() {
     Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3);
     run_next_test();
   });
 
   // Issued by otherCA, which is not in the pinset for pinning.example.com.
   add_connection_test("bad.include-subdomains.pinning.example.com",
-    getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
 
   // These domains serve certs that match the pinset.
   add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
   add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
   add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
 
   // This domain serves a cert that doesn't match the pinset, but subdomains
   // are excluded.
   add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
 
   // This domain's pinset is exactly the same as
   // include-subdomains.pinning.example.com, serves the same cert as
   // bad.include-subdomains.pinning.example.com, is in test-mode, but we are
   // enforcing test mode pins.
   add_connection_test("test-mode.pinning.example.com",
-    getXPCOMStatusFromNSS(PSM_ERROR_KEY_PINNING_FAILURE));
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
 }
 
 function check_pinning_telemetry() {
   let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
   let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS")
                          .snapshot();
   let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS")
                          .snapshot();
--- a/security/pkix/include/pkix/Input.h
+++ b/security/pkix/include/pkix/Input.h
@@ -22,16 +22,17 @@
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__Input_h
 #define mozilla_pkix__Input_h
 
 #include "pkix/nullptr.h"
 #include "pkix/Result.h"
+#include "seccomon.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
 // Expect* functions advance the input mark and return Success if the input
 // matches the given criteria; they fail with the input mark in an undefined
 // state if the input does not match the criteria.
 //
@@ -50,21 +51,21 @@ public:
     , end(nullptr)
   {
   }
 
   Result Init(const uint8_t* data, size_t len)
   {
     if (input) {
       // already initialized
-      return Fail(SEC_ERROR_INVALID_ARGS);
+      return Result::FATAL_ERROR_INVALID_ARGS;
     }
     if (!data || len > 0xffffu) {
       // input too large
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
 
     // XXX: this->input = input bug was not caught by tests! Why not?
     //      this->end = end bug was not caught by tests! Why not?
     this->input = data;
     this->end = data + len;
 
     return Success;
@@ -72,17 +73,17 @@ public:
 
   Result Expect(const uint8_t* expected, uint16_t expectedLen)
   {
     Result rv = EnsureLength(expectedLen);
     if (rv != Success) {
       return rv;
     }
     if (memcmp(input, expected, expectedLen)) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
     input += expectedLen;
     return Success;
   }
 
   bool Peek(uint8_t expectedByte) const
   {
     return input < end && *input == expectedByte;
@@ -191,17 +192,17 @@ public:
   void SkipToEnd()
   {
     input = end;
   }
 
   Result EnsureLength(uint16_t len)
   {
     if (static_cast<size_t>(end - input) < len) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
     return Success;
   }
 
   bool AtEnd() const { return input == end; }
 
   class Mark
   {
@@ -214,17 +215,17 @@ public:
   };
 
   Mark GetMark() const { return Mark(*this, input); }
 
   Result GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item)
   {
     if (&mark.input != this || mark.mark > input) {
       PR_NOT_REACHED("invalid mark");
-      return Fail(SEC_ERROR_INVALID_ARGS);
+      return Result::FATAL_ERROR_INVALID_ARGS;
     }
     item.type = type;
     item.data = const_cast<uint8_t*>(mark.mark);
     item.len = static_cast<decltype(item.len)>(input - mark.mark);
     return Success;
   }
 
 private:
--- a/security/pkix/include/pkix/Result.h
+++ b/security/pkix/include/pkix/Result.h
@@ -20,70 +20,89 @@
  * 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__Result_h
 #define mozilla_pkix__Result_h
 
-#include "prerror.h"
-#include "seccomon.h"
-#include "secerr.h"
+#include "pkix/enumclass.h"
 
 namespace mozilla { namespace pkix {
 
-enum Result
+static const unsigned int FATAL_ERROR_FLAG = 0x800;
+
+MOZILLA_PKIX_ENUM_CLASS Result
 {
   Success = 0,
-  FatalError = -1,      // An error was encountered that caused path building
-                        // to stop immediately. example: out-of-memory.
-  RecoverableError = -2 // an error that will cause path building to continue
-                        // searching for alternative paths. example: expired
-                        // certificate.
+
+  ERROR_BAD_DER = 1,
+  ERROR_CA_CERT_INVALID = 2,
+  ERROR_BAD_SIGNATURE = 3,
+  ERROR_CERT_BAD_ACCESS_LOCATION = 4,
+  ERROR_CERT_NOT_IN_NAME_SPACE = 5,
+  ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = 6,
+  ERROR_CONNECT_REFUSED = 7,
+  ERROR_EXPIRED_CERTIFICATE = 8,
+  ERROR_EXTENSION_VALUE_INVALID = 9,
+  ERROR_INADEQUATE_CERT_TYPE = 10,
+  ERROR_INADEQUATE_KEY_USAGE = 11,
+  ERROR_INVALID_ALGORITHM = 12,
+  ERROR_INVALID_TIME = 13,
+  ERROR_KEY_PINNING_FAILURE = 14,
+  ERROR_PATH_LEN_CONSTRAINT_INVALID = 15,
+  ERROR_POLICY_VALIDATION_FAILED = 16,
+  ERROR_REVOKED_CERTIFICATE = 17,
+  ERROR_UNKNOWN_CRITICAL_EXTENSION = 18,
+  ERROR_UNKNOWN_ISSUER = 19,
+  ERROR_UNTRUSTED_CERT = 20,
+  ERROR_UNTRUSTED_ISSUER = 21,
+
+  ERROR_OCSP_BAD_SIGNATURE = 22,
+  ERROR_OCSP_INVALID_SIGNING_CERT = 23,
+  ERROR_OCSP_MALFORMED_REQUEST = 24,
+  ERROR_OCSP_MALFORMED_RESPONSE = 25,
+  ERROR_OCSP_OLD_RESPONSE = 26,
+  ERROR_OCSP_REQUEST_NEEDS_SIG = 27,
+  ERROR_OCSP_RESPONDER_CERT_INVALID = 28,
+  ERROR_OCSP_SERVER_ERROR = 29,
+  ERROR_OCSP_TRY_SERVER_LATER = 30,
+  ERROR_OCSP_UNAUTHORIZED_REQUEST = 31,
+  ERROR_OCSP_UNKNOWN_RESPONSE_STATUS = 32,
+  ERROR_OCSP_UNKNOWN_CERT = 33,
+  ERROR_OCSP_FUTURE_RESPONSE = 34,
+
+  ERROR_UNKNOWN_ERROR = 35,
+
+  ERROR_INVALID_KEY = 36,
+  ERROR_UNSUPPORTED_KEYALG = 37,
+
+  // Keep this in sync with MAP_LIST in pkixnss.cpp
+
+  FATAL_ERROR_INVALID_ARGS = FATAL_ERROR_FLAG | 1,
+  FATAL_ERROR_INVALID_STATE = FATAL_ERROR_FLAG | 2,
+  FATAL_ERROR_LIBRARY_FAILURE = FATAL_ERROR_FLAG | 3,
+  FATAL_ERROR_NO_MEMORY = FATAL_ERROR_FLAG | 4,
+
+  // Keep this in sync with MAP_LIST in pkixnss.cpp
 };
 
-// When returning errors, use this function instead of calling PR_SetError
-// directly. This helps ensure that we always call PR_SetError when we return
-// an error code. This is a useful place to set a breakpoint when a debugging
-// a certificate verification failure.
-inline Result
-Fail(Result result, PRErrorCode errorCode)
-{
-  PR_ASSERT(result != Success);
-  PR_SetError(errorCode, 0);
-  return result;
-}
-
-inline Result
-MapSECStatus(SECStatus srv)
-{
-  if (srv == SECSuccess) {
-    return Success;
-  }
+// We write many comparisons as (x != Success), and this shortened name makes
+// those comparisons clearer, especially because the shortened name often
+// results in less line wrapping.
+//
+// Visual Studio before VS2013 does not support "enum class," so
+// Result::Success will already be visible in this scope, and compilation will
+// fail if we try to define a variable with that name here.
+#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
+static const Result Success = Result::Success;
+#endif
 
-  PRErrorCode error = PORT_GetError();
-  switch (error) {
-    case SEC_ERROR_EXTENSION_NOT_FOUND:
-      return RecoverableError;
-
-    case PR_INVALID_STATE_ERROR:
-    case SEC_ERROR_INVALID_ARGS:
-    case SEC_ERROR_LIBRARY_FAILURE:
-    case SEC_ERROR_NO_MEMORY:
-      return FatalError;
-  }
-
-  // TODO: PORT_Assert(false); // we haven't classified the error yet
-  return RecoverableError;
-}
-
-inline Result
-Fail(PRErrorCode errorCode)
+inline bool
+IsFatalError(Result rv)
 {
-  PR_ASSERT(errorCode != 0);
-  PR_SetError(errorCode, 0);
-  return mozilla::pkix::MapSECStatus(SECFailure);
+  return static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG;
 }
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__Result_h
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -20,19 +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
 //
 // If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy
 // parameter then all policy validation will be skipped. Otherwise, path
@@ -55,93 +53,74 @@ namespace mozilla { namespace pkix {
 //
 // BuildCertChain prioritizes certain checks ahead of others so that when a
 // certificate chain has multiple errors, the "most serious" error is
 // returned. In practice, this ranking of seriousness is tied directly to how
 // Firefox's certificate error override mechanism.
 //
 // The ranking is:
 //
-//    1. Active distrust (SEC_ERROR_UNTRUSTED_CERT).
+//    1. Active distrust (Result::ERROR_UNTRUSTED_CERT).
 //    2. Problems with issuer-independent properties for CA certificates.
-//    3. Unknown issuer (SEC_ERROR_UNKNOWN_ISSUER).
+//    3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER).
 //    4. Problems with issuer-independent properties for EE certificates.
 //    5. Revocation.
 //
-// In particular, if BuildCertChain returns SEC_ERROR_UNKNOWN_ISSUER then the
-// caller can call CERT_CheckCertValidTimes to determine if the certificate is
-// ALSO expired.
+// In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then
+// the caller can call CERT_CheckCertValidTimes to determine if the certificate
+// is ALSO expired.
 //
 // It would be better if revocation were prioritized above expiration and
 // unknown issuer. However, it is impossible to do revocation checking without
 // knowing the issuer, since the issuer information is needed to validate the
 // revocation information. Also, generally revocation checking only works
 // during the validity period of the certificate.
 //
 // In general, when path building fails, BuildCertChain will return
-// SEC_ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in the
-// same error (which is trivially true when there is only one potential path),
-// more specific errors will be returned.
+// Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in
+// the same error (which is trivially true when there is only one potential
+// path), more specific errors will be returned.
 //
 // ----------------------------------------------------------------------------
 // Meaning of specific error codes
 //
-// SEC_ERROR_UNTRUSTED_CERT means that the end-entity certificate was actively
-//                          distrusted.
-// SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because of active
-//                            distrust.
+// Result::ERROR_UNTRUSTED_CERT means that the end-entity certificate was
+//                              actively distrusted.
+// Result::SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because
+//                                    of active 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);
+Result BuildCertChain(TrustDomain& trustDomain, const SECItem& cert,
+                      PRTime time, EndEntityOrCA endEntityOrCA,
+                      KeyUsage requiredKeyUsageIfPresent,
+                      KeyPurposeId requiredEKUIfPresent,
+                      const CertPolicyId& requiredPolicy,
+                      /*optional*/ const SECItem* stapledOCSPResponse);
 
-// Verify the given signed data using the given public key.
-SECStatus VerifySignedData(const SignedDataWithSignature& sd,
-                           const SECItem& subjectPublicKeyInfo,
-                           void* pkcs11PinArg);
-
-// The return value, if non-null, is owned by the arena and MUST NOT be freed.
-SECItem* CreateEncodedOCSPRequest(TrustDomain& trustDomain, PLArenaPool* arena,
-                                  const CertID& certID);
+static const size_t OCSP_REQUEST_MAX_LENGTH = 127;
+Result CreateEncodedOCSPRequest(TrustDomain& trustDomain,
+                                const struct CertID& certID,
+                                /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH],
+                                /*out*/ size_t& outLen);
 
 // The out parameter expired will be true if the response has expired. If the
 // response also indicates a revoked or unknown certificate, that error
-// will be returned by PR_GetError(). Otherwise, SEC_ERROR_OCSP_OLD_RESPONSE
-// will be returned by PR_GetError() for an expired response.
+// will be returned. Otherwise, REsult::ERROR_OCSP_OLD_RESPONSE will be
+// returned for an expired response.
+//
 // The optional parameter thisUpdate will be the thisUpdate value of
 // the encoded response if it is considered trustworthy. Only
 // good, unknown, or revoked responses that verify correctly are considered
 // trustworthy. If the response is not trustworthy, thisUpdate will be 0.
 // Similarly, the optional parameter validThrough will be the time through
 // which the encoded response is considered trustworthy (that is, if a response had a
 // thisUpdate time of validThrough, it would be considered trustworthy).
-SECStatus VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
-                                    const CertID& certID, PRTime time,
-                                    uint16_t maxLifetimeInDays,
-                                    const SECItem& encodedResponse,
-                          /* out */ bool& expired,
-                 /* optional out */ PRTime* thisUpdate = nullptr,
-                 /* optional out */ PRTime* validThrough = nullptr);
-
-// Computes the SHA-1 hash of the data in the current item.
-//
-// item contains the data to hash.
-// digestBuf must point to a buffer to where the SHA-1 hash will be written.
-// digestBufLen must be 20 (the length of a SHA-1 hash,
-//              TrustDomain::DIGEST_LENGTH).
-//
-// TODO(bug 966856): Add SHA-2 support
-// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
-// other, extensive, memory safety efforts in mozilla::pkix, and we should find
-// a way to provide a more-obviously-safe interface.
-SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
-                    size_t digestBufLen);
-
-// Checks, for RSA keys and DSA keys, that the modulus is at least 1024 bits.
-SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo);
+Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
+                                 const CertID& certID, PRTime time,
+                                 uint16_t maxLifetimeInDays,
+                                 const SECItem& encodedResponse,
+                       /* out */ bool& expired,
+              /* optional out */ PRTime* thisUpdate = nullptr,
+              /* optional out */ PRTime* validThrough = nullptr);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkix_h
new file mode 100644
--- /dev/null
+++ b/security/pkix/include/pkix/pkixnss.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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__pkixnss_h
+#define mozilla_pkix__pkixnss_h
+
+#include "pkixtypes.h"
+#include "prerror.h"
+#include "seccomon.h"
+
+namespace mozilla { namespace pkix {
+
+// Verify the given signed data using the given public key.
+Result VerifySignedData(const SignedDataWithSignature& sd,
+                        const SECItem& subjectPublicKeyInfo,
+                        void* pkcs11PinArg);
+
+// Computes the SHA-1 hash of the data in the current item.
+//
+// item contains the data to hash.
+// digestBuf must point to a buffer to where the SHA-1 hash will be written.
+// digestBufLen must be 20 (the length of a SHA-1 hash,
+//              TrustDomain::DIGEST_LENGTH).
+//
+// TODO(bug 966856): Add SHA-2 support
+// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
+// other, extensive, memory safety efforts in mozilla::pkix, and we should find
+// a way to provide a more-obviously-safe interface.
+Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                 size_t digestBufLen);
+
+// Checks, for RSA keys and DSA keys, that the modulus is at least 1024 bits.
+Result CheckPublicKey(const SECItem& subjectPublicKeyInfo);
+
+Result MapPRErrorCodeToResult(PRErrorCode errorCode);
+PRErrorCode MapResultToPRErrorCode(Result result);
+
+// Returns the stringified name of the given result, e.g. "Result::Success",
+// or nullptr if result is unknown (invalid).
+const char* MapResultToName(Result result);
+
+// The error codes within each module must fit in 16 bits. We want these
+// errors to fit in the same module as the NSS errors but not overlap with
+// any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
+// involves negating the value of the error and then synthesizing an error
+// in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at
+// a negative value that both doesn't overlap with the current value
+// ranges for NSS errors and that will fit in 16 bits when negated.
+static const PRErrorCode ERROR_BASE = -0x4000;
+static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000;
+
+enum ErrorCode {
+  MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0
+};
+
+void RegisterErrorTable();
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__pkixnss_h
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -20,17 +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__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
-#include "pkix/enumclass.h"
+#include "pkix/Result.h"
 #include "pkix/nullptr.h"
 #include "prtime.h"
 #include "seccomon.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
 MOZILLA_PKIX_ENUM_CLASS DigestAlgorithm
@@ -193,34 +193,34 @@ public:
   // When policy.IsAnyPolicy(), then no policy-related checking should be done.
   // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with
   // *trustLevel == TrustAnchor unless the given cert is considered a trust
   // anchor *for that policy*. In particular, if the user has marked an
   // intermediate certificate as trusted, but that intermediate isn't in the
   // list of EV roots, then GetCertTrust must result in
   // *trustLevel == InheritsTrust instead of *trustLevel == TrustAnchor
   // (assuming the candidate cert is not actively distrusted).
-  virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA,
-                                 const CertPolicyId& policy,
-                                 const SECItem& candidateCertDER,
-                         /*out*/ TrustLevel* trustLevel) = 0;
+  virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
+                              const CertPolicyId& policy,
+                              const SECItem& candidateCertDER,
+                              /*out*/ TrustLevel* trustLevel) = 0;
 
   class IssuerChecker
   {
   public:
     // potentialIssuerDER is the complete DER encoding of the certificate to
     // be checked as a potential issuer.
     //
     // If additionalNameConstraints is not nullptr then it must point to an
     // encoded NameConstraints extension value; in that case, those name
     // constraints will be checked in addition to any any name constraints
     // contained in potentialIssuerDER.
-    virtual SECStatus Check(const SECItem& potentialIssuerDER,
-               /*optional*/ const SECItem* additionalNameConstraints,
-                    /*out*/ bool& keepGoing) = 0;
+    virtual Result Check(const SECItem& potentialIssuerDER,
+                         /*optional*/ const SECItem* additionalNameConstraints,
+                         /*out*/ bool& keepGoing) = 0;
   protected:
     IssuerChecker();
     virtual ~IssuerChecker();
   private:
     IssuerChecker(const IssuerChecker&) /*= delete*/;
     void operator=(const IssuerChecker&) /*= delete*/;
   };
 
@@ -260,18 +260,18 @@ public:
   //              [...]
   //                TrustDomain::FindIssuer
   //                  [...]
   //                    IssuerChecker::Check
   //                      [...]
   //
   // checker.Check is responsible for limiting the recursion to a reasonable
   // limit.
-  virtual SECStatus FindIssuer(const SECItem& encodedIssuerName,
-                               IssuerChecker& checker, PRTime time) = 0;
+  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+                            IssuerChecker& checker, PRTime time) = 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
@@ -284,57 +284,56 @@ public:
   // 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;
+  virtual Result 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;
+  virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
+                                 const CertID& certID, PRTime time,
+                    /*optional*/ const SECItem* stapledOCSPresponse,
+                    /*optional*/ const SECItem* aiaExtension) = 0;
+
+  // Check that the key size, algorithm, and parameters of the given public key
+  // are acceptable.
+  //
+  // VerifySignedData() should do the same checks that this function does, but
+  // mainly for efficiency, some keys are not passed to VerifySignedData().
+  // This function is called instead for those keys.
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) = 0;
 
   // Verify the given signature using the given public key.
   //
   // Most implementations of this function should probably forward the call
   // directly to mozilla::pkix::VerifySignedData.
   //
   // In any case, the implementation must perform checks on the public key
   // similar to how mozilla::pkix::CheckPublicKey() does.
-  virtual SECStatus VerifySignedData(const SignedDataWithSignature& signedData,
-                                     const SECItem& subjectPublicKeyInfo) = 0;
+  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
+                                  const SECItem& subjectPublicKeyInfo) = 0;
 
   // Compute the SHA-1 hash of the data in the current item.
   //
   // item contains the data to hash.
   // digestBuf must point to a buffer to where the SHA-1 hash will be written.
   // digestBufLen must be DIGEST_LENGTH (20, the length of a SHA-1 hash).
   //
   // TODO(bug 966856): Add SHA-2 support
   // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
   // other, extensive, memory safety efforts in mozilla::pkix, and we should
   // find a way to provide a more-obviously-safe interface.
   static const size_t DIGEST_LENGTH = 20; // length of SHA-1 digest
-  virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
-                              size_t digestBufLen) = 0;
-
-  // Check that the key size, algorithm, and parameters of the given public key
-  // are acceptable.
-  //
-  // VerifySignedData() should do the same checks that this function does, but
-  // mainly for efficiency, some keys are not passed to VerifySignedData().
-  // This function is called instead for those keys.
-  virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo) = 0;
-
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen) = 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
@@ -54,158 +54,152 @@ public:
                    unsigned int subCACount)
     : trustDomain(trustDomain)
     , subject(subject)
     , time(time)
     , requiredEKUIfPresent(requiredEKUIfPresent)
     , requiredPolicy(requiredPolicy)
     , stapledOCSPResponse(stapledOCSPResponse)
     , subCACount(subCACount)
-    , result(SEC_ERROR_LIBRARY_FAILURE)
+    , result(Result::FATAL_ERROR_LIBRARY_FAILURE)
     , resultWasSet(false)
   {
   }
 
-  SECStatus Check(const SECItem& potentialIssuerDER,
-                  /*optional*/ const SECItem* additionalNameConstraints,
-                  /*out*/ bool& keepGoing);
+  Result Check(const SECItem& potentialIssuerDER,
+               /*optional*/ const SECItem* additionalNameConstraints,
+               /*out*/ bool& keepGoing);
 
   Result CheckResult() const;
 
 private:
   TrustDomain& trustDomain;
   const BackCert& subject;
   const PRTime time;
   const KeyPurposeId requiredEKUIfPresent;
   const CertPolicyId& requiredPolicy;
   /*optional*/ SECItem const* const stapledOCSPResponse;
   const unsigned int subCACount;
 
-  SECStatus RecordResult(PRErrorCode currentResult, /*out*/ bool& keepGoing);
-  PRErrorCode result;
+  Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
+  Result result;
   bool resultWasSet;
 
   PathBuildingStep(const PathBuildingStep&) /*= delete*/;
   void operator=(const PathBuildingStep&) /*= delete*/;
 };
 
-SECStatus
-PathBuildingStep::RecordResult(PRErrorCode newResult,
-                               /*out*/ bool& keepGoing)
+Result
+PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing)
 {
-  if (newResult == SEC_ERROR_UNTRUSTED_CERT) {
-    newResult = SEC_ERROR_UNTRUSTED_ISSUER;
+  if (newResult == Result::ERROR_UNTRUSTED_CERT) {
+    newResult = Result::ERROR_UNTRUSTED_ISSUER;
   }
+
   if (resultWasSet) {
-    if (result == 0) {
+    if (result == Success) {
       PR_NOT_REACHED("RecordResult called after finding a chain");
-      PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-      return SECFailure;
+      return Result::FATAL_ERROR_INVALID_STATE;
     }
     // If every potential issuer has the same problem (e.g. expired) and/or if
     // there is only one bad potential issuer, then return a more specific
     // error. Otherwise, punt on trying to decide which error should be
-    // returned by returning the generic SEC_ERROR_UNKNOWN_ISSUER error.
-    if (newResult != 0 && newResult != result) {
-      newResult = SEC_ERROR_UNKNOWN_ISSUER;
+    // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error.
+    if (newResult != Success && newResult != result) {
+      newResult = Result::ERROR_UNKNOWN_ISSUER;
     }
   }
 
   result = newResult;
   resultWasSet = true;
-  keepGoing = result != 0;
-  return SECSuccess;
+  keepGoing = result != Success;
+  return Success;
 }
 
 Result
 PathBuildingStep::CheckResult() const
 {
   if (!resultWasSet) {
-    return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER);
+    return Result::ERROR_UNKNOWN_ISSUER;
   }
-  if (result == 0) {
-    return Success;
-  }
-  PR_SetError(result, 0);
-  return MapSECStatus(SECFailure);
+  return result;
 }
 
 // The code that executes in the inner loop of BuildForward
-SECStatus
+Result
 PathBuildingStep::Check(const SECItem& potentialIssuerDER,
                         /*optional*/ const SECItem* additionalNameConstraints,
                         /*out*/ bool& keepGoing)
 {
   BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
                            &subject);
   Result rv = potentialIssuer.Init();
   if (rv != Success) {
-    return RecordResult(PR_GetError(), keepGoing);
+    return RecordResult(rv, keepGoing);
   }
 
   // RFC5280 4.2.1.1. Authority Key Identifier
   // RFC5280 4.2.1.2. Subject Key Identifier
 
   // Loop prevention, done as recommended by RFC4158 Section 5.2
   // TODO: this doesn't account for subjectAltNames!
   // TODO(perf): This probably can and should be optimized in some way.
   bool loopDetected = false;
   for (const BackCert* prev = potentialIssuer.childCert;
        !loopDetected && prev != nullptr; prev = prev->childCert) {
     if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(),
                               &prev->GetSubjectPublicKeyInfo()) &&
         SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(),
                               &prev->GetSubject())) {
       // XXX: error code
-      return RecordResult(SEC_ERROR_UNKNOWN_ISSUER, keepGoing);
+      return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
     }
   }
 
   if (potentialIssuer.GetNameConstraints()) {
     rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
                               subject, requiredEKUIfPresent);
     if (rv != Success) {
-       return RecordResult(PR_GetError(), keepGoing);
+       return RecordResult(rv, keepGoing);
     }
   }
 
   if (additionalNameConstraints) {
     rv = CheckNameConstraints(*additionalNameConstraints, subject,
                               requiredEKUIfPresent);
     if (rv != Success) {
-       return RecordResult(PR_GetError(), keepGoing);
+       return RecordResult(rv, 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, KeyUsage::keyCertSign,
                     requiredEKUIfPresent, requiredPolicy, nullptr, subCACount);
   if (rv != Success) {
-    return RecordResult(PR_GetError(), keepGoing);
+    return RecordResult(rv, keepGoing);
   }
 
-  SECStatus srv = trustDomain.VerifySignedData(
-                                subject.GetSignedData(),
-                                potentialIssuer.GetSubjectPublicKeyInfo());
-  if (srv != SECSuccess) {
-    return RecordResult(PR_GetError(), keepGoing);
+  rv = trustDomain.VerifySignedData(subject.GetSignedData(),
+                                    potentialIssuer.GetSubjectPublicKeyInfo());
+  if (rv != Success) {
+    return RecordResult(rv, keepGoing);
   }
 
   CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
                 subject.GetSerialNumber());
-  srv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
-                                    stapledOCSPResponse,
-                                    subject.GetAuthorityInfoAccess());
-  if (srv != SECSuccess) {
-    return RecordResult(PR_GetError(), keepGoing);
+  rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
+                                   stapledOCSPResponse,
+                                   subject.GetAuthorityInfoAccess());
+  if (rv != Success) {
+    return RecordResult(rv, keepGoing);
   }
 
-  return RecordResult(0/*PRErrorCode::success*/, keepGoing);
+  return RecordResult(Success, keepGoing);
 }
 
 // Recursively build the path from the given subject certificate to the root.
 //
 // Be very careful about changing the order of checks. The order is significant
 // because it affects which error we return when a certificate or certificate
 // chain has multiple problems. See the error ranking documentation in
 // pkix/pkix.h.
@@ -224,116 +218,107 @@ BuildForward(TrustDomain& trustDomain,
   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,
                                         requiredKeyUsageIfPresent,
                                         requiredEKUIfPresent, requiredPolicy,
                                         subCACount, &trustLevel);
-  PRErrorCode deferredEndEntityError = 0;
+  Result deferredEndEntityError = Success;
   if (rv != Success) {
     if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
         trustLevel != TrustLevel::TrustAnchor) {
-      deferredEndEntityError = PR_GetError();
+      deferredEndEntityError = rv;
     } else {
       return rv;
     }
   }
 
   if (trustLevel == TrustLevel::TrustAnchor) {
     // End of the recursion.
 
     NonOwningDERArray chain;
     for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
-      Result rv = chain.Append(cert->GetDER());
+      rv = chain.Append(cert->GetDER());
       if (rv != Success) {
         PR_NOT_REACHED("NonOwningDERArray::SetItem failed.");
         return rv;
       }
     }
 
     // This must be done here, after the chain is built but before any
     // revocation checks have been done.
-    SECStatus srv = trustDomain.IsChainValid(chain);
-    if (srv != SECSuccess) {
-      return MapSECStatus(srv);
-    }
-
-    return Success;
+    return trustDomain.IsChainValid(chain);
   }
 
   if (subject.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);
+      return Result::ERROR_UNKNOWN_ISSUER;
     }
     ++subCACount;
   } else {
     PR_ASSERT(subCACount == 0);
   }
 
   // Find a trusted issuer.
 
   PathBuildingStep pathBuilder(trustDomain, subject, time,
                                requiredEKUIfPresent, requiredPolicy,
                                stapledOCSPResponse, subCACount);
 
   // TODO(bug 965136): Add SKI/AKI matching optimizations
-  if (trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time)
-        != SECSuccess) {
-    return MapSECStatus(SECFailure);
+  rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time);
+  if (rv != Success) {
+    return rv;
   }
 
   rv = pathBuilder.CheckResult();
   if (rv != Success) {
     return rv;
   }
 
   // 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);
+  if (deferredEndEntityError != Success) {
+    return deferredEndEntityError;
   }
 
   // We've built a valid chain from the subject cert up to a trusted root.
   return Success;
 }
 
-SECStatus
+Result
 BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER,
                PRTime time, EndEntityOrCA endEntityOrCA,
                KeyUsage requiredKeyUsageIfPresent,
                KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
                /*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 cert(certDER, endEntityOrCA, nullptr);
   Result rv = cert.Init();
   if (rv != Success) {
-    return SECFailure;
+    return rv;
   }
 
   // See documentation for CheckPublicKey() in pkixtypes.h for why the public
   // key also needs to be checked here when trustDomain.VerifySignedData()
   // should already be doing it.
-  if (trustDomain.CheckPublicKey(cert.GetSubjectPublicKeyInfo()) != SECSuccess) {
-    return SECFailure;
+  rv = trustDomain.CheckPublicKey(cert.GetSubjectPublicKeyInfo());
+  if (rv != Success) {
+    return rv;
   }
 
-  rv = BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
-                    requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse,
-                    0/*subCACount*/);
-  if (rv != Success) {
-    return SECFailure;
-  }
-
-  return SECSuccess;
+  return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
+                      requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse,
+                      0/*subCACount*/);
 }
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcert.cpp
+++ b/security/pkix/lib/pkixcert.cpp
@@ -238,21 +238,21 @@ BackCert::RememberExtension(Input& extnI
   } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
     out = &authorityInfoAccess;
   }
 
   if (out) {
     // Don't allow an empty value for any extension we understand. This way, we
     // can test out->len to check for duplicates.
     if (extnValue.len == 0) {
-      return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     if (out->len != 0) {
       // Duplicate extension
-      return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     *out = extnValue;
     understood = true;
   }
 
   return Success;
 }
 
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -23,41 +23,42 @@
  */
 
 #include "cert.h"
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
 #include "pkix/ScopedPtr.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
+#include "pkix/pkixnss.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 Result
 CheckValidity(const SECItem& encodedValidity, PRTime time)
 {
   Input validity;
   if (validity.Init(encodedValidity.data, encodedValidity.len) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
+    return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   PRTime notBefore;
   if (der::TimeChoice(validity, notBefore) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
+    return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   if (time < notBefore) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
+    return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
   PRTime notAfter;
   if (der::TimeChoice(validity, notAfter) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
+    return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   if (time > notAfter) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
+    return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
   return der::End(validity);
 }
 
 // 4.2.1.3. Key Usage (id-ce-keyUsage)
 
 // As explained in the comment in CheckKeyUsage, bit 0 is the most significant
@@ -73,46 +74,46 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
               KeyUsage requiredKeyUsageIfPresent)
 {
   if (!encodedKeyUsage) {
     // TODO(bug 970196): Reject certificates that are being used to verify
     // certificate signatures unless the certificate is a trust anchor, to
     // reduce the chances of an end-entity certificate being abused as a CA
     // certificate.
     // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) {
-    //   return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    //   return Result::ERROR_INADEQUATE_KEY_USAGE;
     // }
     //
     // TODO: Users may configure arbitrary certificates as trust anchors, not
     // just roots. We should only allow a certificate without a key usage to be
     // used as a CA when it is self-issued and self-signed.
     return Success;
   }
 
   Input input;
   if (input.Init(encodedKeyUsage->data, encodedKeyUsage->len) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
   Input value;
   if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
 
   uint8_t numberOfPaddingBits;
   if (value.Read(numberOfPaddingBits) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
   if (numberOfPaddingBits > 7) {
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
 
   uint8_t bits;
   if (value.Read(bits) != Success) {
     // Reject empty bit masks.
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
 
   // The most significant bit is numbered 0 (digitalSignature) and the least
   // significant bit is numbered 7 (encipherOnly), and the padding is in the
   // least significant bits of the last byte. The numbering of bits in a byte
   // is backwards from how we usually interpret them.
   //
   // For example, let's say bits is encoded in one byte with of value 0xB0 and
@@ -143,41 +144,41 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
   //
   // Note that since the KeyUsage enumeration is limited to values 0-7, we
   // only ever need to examine the first byte test for
   // requiredKeyUsageIfPresent.
 
   if (requiredKeyUsageIfPresent != KeyUsage::noParticularKeyUsageRequired) {
     // Check that the required key usage bit is set.
     if ((bits & KeyUsageToBitMask(requiredKeyUsageIfPresent)) == 0) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+      return Result::ERROR_INADEQUATE_KEY_USAGE;
     }
   }
 
   if (endEntityOrCA != EndEntityOrCA::MustBeCA) {
     // RFC 5280 says "The keyCertSign bit is asserted when the subject public
     // key is used for verifying signatures on public key certificates. If the
     // keyCertSign bit is asserted, then the cA bit in the basic constraints
     // extension (Section 4.2.1.9) MUST also be asserted."
     if ((bits & KeyUsageToBitMask(KeyUsage::keyCertSign)) != 0) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+      return Result::ERROR_INADEQUATE_KEY_USAGE;
     }
   }
 
   // The padding applies to the last byte, so skip to the last byte.
   while (!value.AtEnd()) {
     if (value.Read(bits) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+      return Result::ERROR_INADEQUATE_KEY_USAGE;
     }
   }
 
   // All of the padding bits must be zero, according to DER rules.
   uint8_t paddingMask = static_cast<uint8_t>((1 << numberOfPaddingBits) - 1);
   if ((bits & paddingMask) != 0) {
-    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
 
   return Success;
 }
 
 // RFC5820 4.2.1.4. Certificate Policies
 
 // "The user-initial-policy-set contains the special value any-policy if the
@@ -234,63 +235,63 @@ Result
 CheckCertificatePolicies(EndEntityOrCA endEntityOrCA,
                          const SECItem* encodedCertificatePolicies,
                          const SECItem* encodedInhibitAnyPolicy,
                          TrustLevel trustLevel,
                          const CertPolicyId& requiredPolicy)
 {
   if (requiredPolicy.numBytes == 0 ||
       requiredPolicy.numBytes > sizeof requiredPolicy.bytes) {
-    return Fail(FatalError, SEC_ERROR_INVALID_ARGS);
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   // Ignore all policy information if the caller indicates any policy is
   // acceptable. See TrustDomain::GetCertTrust and the policy part of
   // BuildCertChain's documentation.
   if (requiredPolicy.IsAnyPolicy()) {
     return Success;
   }
 
   // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when
   // inhibitAnyPolicy extension is present and we need to evaluate certificate
   // policies.
   if (encodedInhibitAnyPolicy) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
 
   // The root CA certificate may omit the policies that it has been
   // trusted for, so we cannot require the policies to be present in those
   // certificates. Instead, the determination of which roots are trusted for
   // which policies is made by the TrustDomain's GetCertTrust method.
   if (trustLevel == TrustLevel::TrustAnchor &&
       endEntityOrCA == EndEntityOrCA::MustBeCA) {
     return Success;
   }
 
   if (!encodedCertificatePolicies) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
 
   bool found = false;
 
   Input input;
   if (input.Init(encodedCertificatePolicies->data,
                  encodedCertificatePolicies->len) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
   if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No,
                     bind(CheckPolicyInformation, _1, endEntityOrCA,
                          requiredPolicy, ref(found))) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
   if (der::End(input) != Success) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
   if (!found) {
-    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+    return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
 
   return Success;
 }
 
 static const long UNLIMITED_PATH_LEN = -1; // must be less than zero
 
 //  BasicConstraints ::= SEQUENCE {
@@ -302,25 +303,25 @@ DecodeBasicConstraints(Input& input, /*o
 {
   // TODO(bug 989518): cA is by default false. According to DER, default
   // values must not be explicitly encoded in a SEQUENCE. So, if this
   // value is present and false, it is an encoding error. However, Go Daddy
   // has issued many certificates with this improper encoding, so we can't
   // enforce this yet (hence passing true for allowInvalidExplicitEncoding
   // to der::OptionalBoolean).
   if (der::OptionalBoolean(input, true, isCA) != Success) {
-    return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+    return Result::ERROR_EXTENSION_VALUE_INVALID;
   }
 
   // TODO(bug 985025): If isCA is false, pathLenConstraint MUST NOT
   // be included (as per RFC 5280 section 4.2.1.9), but for compatibility
   // reasons, we don't check this for now.
   if (der::OptionalInteger(input, UNLIMITED_PATH_LEN, pathLenConstraint)
         != Success) {
-    return Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+    return Result::ERROR_EXTENSION_VALUE_INVALID;
   }
 
   return Success;
 }
 
 // RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints)
 Result
 CheckBasicConstraints(EndEntityOrCA endEntityOrCA,
@@ -330,25 +331,25 @@ CheckBasicConstraints(EndEntityOrCA endE
 {
   bool isCA = false;
   long pathLenConstraint = UNLIMITED_PATH_LEN;
 
   if (encodedBasicConstraints) {
     Input input;
     if (input.Init(encodedBasicConstraints->data,
                    encodedBasicConstraints->len) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     if (der::Nested(input, der::SEQUENCE,
                     bind(DecodeBasicConstraints, _1, ref(isCA),
                          ref(pathLenConstraint))) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     if (der::End(input) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
+      return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
   } else {
     // "If the basic constraints extension is not present in a version 3
     //  certificate, or the extension is present but the cA boolean is not
     //  asserted, then the certified public key MUST NOT be used to verify
     //  certificate signatures."
     //
     // For compatibility, we must accept v1 trust anchors without basic
@@ -360,102 +361,103 @@ CheckBasicConstraints(EndEntityOrCA endE
       isCA = true;
     }
   }
 
   if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
     // CA certificates are not trusted as EE certs.
 
     if (isCA) {
-      // XXX: We use SEC_ERROR_CA_CERT_INVALID here so we can distinguish
-      // this error from other errors, given that NSS does not have a "CA cert
-      // used as end-entity" error code since it doesn't have such a
+      // TODO(bug 1040446): We use Result::ERROR_CA_CERT_INVALID here so we can
+      // distinguish this error from other errors, given that NSS does not have
+      // a "CA cert used as end-entity" error code since it doesn't have such a
       // prohibition. We should add such an error code and stop abusing
-      // SEC_ERROR_CA_CERT_INVALID this way.
+      // Result::ERROR_CA_CERT_INVALID this way.
       //
       // Note, in particular, that this check prevents a delegated OCSP
       // response signing certificate with the CA bit from successfully
       // validating when we check it from pkixocsp.cpp, which is a good thing.
       //
-      return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
+      return Result::ERROR_CA_CERT_INVALID;
     }
 
     return Success;
   }
 
   PORT_Assert(endEntityOrCA == EndEntityOrCA::MustBeCA);
 
   // End-entity certificates are not allowed to act as CA certs.
   if (!isCA) {
-    return Fail(RecoverableError, SEC_ERROR_CA_CERT_INVALID);
+    return Result::ERROR_CA_CERT_INVALID;
   }
 
   if (pathLenConstraint >= 0 &&
       static_cast<long>(subCACount) > pathLenConstraint) {
-    return Fail(RecoverableError, SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
+    return Result::ERROR_PATH_LEN_CONSTRAINT_INVALID;
   }
 
   return Success;
 }
 
 // 4.2.1.10. Name Constraints
 
 inline void
 PORT_FreeArena_false(PLArenaPool* arena) {
   // PL_FreeArenaPool can't be used because it doesn't actually free the
   // memory, which doesn't work well with memory analysis tools
   return PORT_FreeArena(arena, PR_FALSE);
 }
 
+// TODO: remove #include "pkix/pkixnss.h" when this is rewritten to be
+// independent of NSS.
 Result
 CheckNameConstraints(const SECItem& encodedNameConstraints,
                      const BackCert& firstChild,
                      KeyPurposeId requiredEKUIfPresent)
 {
   ScopedPtr<PLArenaPool, PORT_FreeArena_false>
     arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
-    return MapSECStatus(SECFailure);
+    return Result::FATAL_ERROR_NO_MEMORY;
   }
 
   // Owned by arena
   const CERTNameConstraints* constraints =
     CERT_DecodeNameConstraintsExtension(arena.get(), &encodedNameConstraints);
   if (!constraints) {
-    return MapSECStatus(SECFailure);
+    return MapPRErrorCodeToResult(PR_GetError());
   }
 
   for (const BackCert* child = &firstChild; child; child = child->childCert) {
     ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
       nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                       const_cast<SECItem*>(&child->GetDER()),
                                       nullptr, false, true));
     if (!nssCert) {
-      return MapSECStatus(SECFailure);
+      return MapPRErrorCodeToResult(PR_GetError());
     }
 
     bool includeCN = child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
                      requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth;
     // owned by arena
     const CERTGeneralName*
       names(CERT_GetConstrainedCertificateNames(nssCert.get(), arena.get(),
                                                 includeCN));
     if (!names) {
-      return MapSECStatus(SECFailure);
+      return MapPRErrorCodeToResult(PR_GetError());
     }
 
     CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names);
     do {
       if (CERT_CheckNameSpace(arena.get(), constraints, currentName)
             != SECSuccess) {
         // XXX: It seems like CERT_CheckNameSpace doesn't always call
-        // PR_SetError when it fails. We set the error code here, though this
-        // may be papering over some fatal errors. NSS's
-        // cert_VerifyCertChainOld does something similar.
-        return Fail(RecoverableError, SEC_ERROR_CERT_NOT_IN_NAME_SPACE);
+        // PR_SetError when it fails, so we ignore what PR_GetError would
+        // return. NSS's cert_VerifyCertChainOld does something similar.
+        return Result::ERROR_CERT_NOT_IN_NAME_SPACE;
       }
       currentName = CERT_GetNextGeneralName(currentName);
     } while (currentName != names);
   }
 
   return Success;
 }
 
@@ -517,21 +519,21 @@ MatchEKU(Input& value, KeyPurposeId requ
         break;
 
       case KeyPurposeId::id_kp_OCSPSigning:
         match = value.MatchRest(ocsp);
         break;
 
       case KeyPurposeId::anyExtendedKeyUsage:
         PR_NOT_REACHED("anyExtendedKeyUsage should start with found==true");
-        return Fail(SEC_ERROR_LIBRARY_FAILURE);
+        return Result::FATAL_ERROR_LIBRARY_FAILURE;
 
       default:
         PR_NOT_REACHED("unrecognized EKU");
-        return Fail(SEC_ERROR_LIBRARY_FAILURE);
+        return Result::FATAL_ERROR_LIBRARY_FAILURE;
     }
   }
 
   if (match) {
     found = true;
     if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
       foundOCSPSigning = true;
     }
@@ -544,45 +546,45 @@ MatchEKU(Input& value, KeyPurposeId requ
   return Success;
 }
 
 Result
 CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
                       const SECItem* encodedExtendedKeyUsage,
                       KeyPurposeId requiredEKU)
 {
-  // XXX: We're using SEC_ERROR_INADEQUATE_CERT_TYPE here so that callers can
-  // distinguish EKU mismatch from KU mismatch from basic constraints mismatch.
-  // We should probably add a new error code that is more clear for this type
-  // of problem.
+  // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers
+  // can distinguish EKU mismatch from KU mismatch from basic constraints
+  // mismatch. We should probably add a new error code that is more clear for
+  // this type of problem.
 
   bool foundOCSPSigning = false;
 
   if (encodedExtendedKeyUsage) {
     bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage;
 
     Input input;
     if (input.Init(encodedExtendedKeyUsage->data,
                    encodedExtendedKeyUsage->len) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
     if (der::NestedOf(input, der::SEQUENCE, der::OIDTag, der::EmptyAllowed::No,
                       bind(MatchEKU, _1, requiredEKU, endEntityOrCA,
                            ref(found), ref(foundOCSPSigning)))
           != Success) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
     if (der::End(input) != Success) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
 
     // If the EKU extension was included, then the required EKU must be in the
     // list.
     if (!found) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
   }
 
   // pkixocsp.cpp depends on the following additional checks.
 
   if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
     // When validating anything other than an delegated OCSP signing cert,
     // reject any cert that also claims to be an OCSP responder, because such
@@ -592,29 +594,29 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
     // That said, we accept CA certificates with id-kp-OCSPSigning because
     // some CAs in Mozilla's CA program have issued such intermediate
     // certificates, and because some CAs have reported some Microsoft server
     // software wrongly requires CA certificates to have id-kp-OCSPSigning.
     // Allowing this exception does not cause any security issues because we
     // require delegated OCSP response signing certificates to be end-entity
     // certificates.
     if (foundOCSPSigning && requiredEKU != KeyPurposeId::id_kp_OCSPSigning) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
     // http://tools.ietf.org/html/rfc6960#section-4.2.2.2:
     // "OCSP signing delegation SHALL be designated by the inclusion of
     // id-kp-OCSPSigning in an extended key usage certificate extension
     // included in the OCSP response signer's certificate."
     //
     // id-kp-OCSPSigning is the only EKU that isn't implicitly assumed when the
     // EKU extension is missing from an end-entity certificate. However, any CA
     // certificate can issue a delegated OCSP response signing certificate, so
     // we can't require the EKU be explicitly included for CA certificates.
     if (!foundOCSPSigning && requiredEKU == KeyPurposeId::id_kp_OCSPSigning) {
-      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
+      return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
   }
 
   return Success;
 }
 
 Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
@@ -626,29 +628,28 @@ CheckIssuerIndependentProperties(TrustDo
                                  unsigned int subCACount,
                 /*optional out*/ TrustLevel* trustLevelOut)
 {
   Result rv;
 
   const EndEntityOrCA endEntityOrCA = cert.endEntityOrCA;
 
   TrustLevel trustLevel;
-  rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy,
-                                             cert.GetDER(), &trustLevel));
+  rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(),
+                                &trustLevel);
   if (rv != Success) {
     return rv;
   }
   if (trustLevel == TrustLevel::ActivelyDistrusted) {
-    return Fail(RecoverableError, SEC_ERROR_UNTRUSTED_CERT);
+    return Result::ERROR_UNTRUSTED_CERT;
   }
   if (trustLevel != TrustLevel::TrustAnchor &&
       trustLevel != TrustLevel::InheritsTrust) {
     // The TrustDomain returned a trust level that we weren't expecting.
-    PORT_SetError(PR_INVALID_STATE_ERROR);
-    return FatalError;
+    return Result::FATAL_ERROR_INVALID_STATE;
   }
   if (trustLevelOut) {
     *trustLevelOut = trustLevel;
   }
 
   // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136).
 
   // 4.2.1.2. Subject Key Identifier is ignored (see bug 965136).
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -39,17 +39,17 @@ ExpectTagAndGetLength(Input& input, uint
   uint8_t tag;
   Result rv;
   rv = input.Read(tag);
   if (rv != Success) {
     return rv;
   }
 
   if (tag != expectedTag) {
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   // The short form of length is a single byte with the high order bit set
   // to zero. The long form of length is one byte with the high order bit
   // set, followed by N bytes, where N is encoded in the lowest 7 bits of
   // the first byte.
   uint8_t length1;
   rv = input.Read(length1);
@@ -61,31 +61,31 @@ ExpectTagAndGetLength(Input& input, uint
   } else if (length1 == 0x81) {
     uint8_t length2;
     rv = input.Read(length2);
     if (rv != Success) {
       return rv;
     }
     if (length2 < 128) {
       // Not shortest possible encoding
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
     length = length2;
   } else if (length1 == 0x82) {
     rv = input.Read(length);
     if (rv != Success) {
       return rv;
     }
     if (length < 256) {
       // Not shortest possible encoding
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
   } else {
     // We don't support lengths larger than 2^16 - 1.
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   // Ensure the input is long enough for the length it says it has.
   return input.EnsureLength(length);
 }
 
 } // namespace internal
 
@@ -127,17 +127,17 @@ DigestAlgorithmOIDValue(Input& algorithm
     algorithm = DigestAlgorithm::sha1;
   } else if (algorithmID.MatchRest(id_sha256)) {
     algorithm = DigestAlgorithm::sha256;
   } else if (algorithmID.MatchRest(id_sha384)) {
     algorithm = DigestAlgorithm::sha384;
   } else if (algorithmID.MatchRest(id_sha512)) {
     algorithm = DigestAlgorithm::sha512;
   } else {
-    return Fail(SEC_ERROR_INVALID_ALGORITHM);
+    return Result::ERROR_INVALID_ALGORITHM;
   }
 
   return Success;
 }
 
 Result
 SignatureAlgorithmOIDValue(Input& algorithmID,
                            /*out*/ SignatureAlgorithm& algorithm)
@@ -222,17 +222,17 @@ SignatureAlgorithmOIDValue(Input& algori
   } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) {
     algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha512;
   } else if (algorithmID.MatchRest(id_dsa_with_sha1)) {
     algorithm = SignatureAlgorithm::dsa_with_sha1;
   } else if (algorithmID.MatchRest(id_dsa_with_sha256)) {
     algorithm = SignatureAlgorithm::dsa_with_sha256;
   } else {
     // Any MD5-based signature algorithm, or any unknown signature algorithm.
-    return Fail(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+    return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
   }
 
   return Success;
 }
 
 template <typename OidValueParser, typename Algorithm>
 Result
 AlgorithmIdentifier(OidValueParser oidValueParser, Input& input,
@@ -299,43 +299,43 @@ SignedData(Input& input, /*out*/ Input& 
     return rv;
   }
 
   rv = ExpectTagAndGetValue(input, BIT_STRING, signedData.signature);
   if (rv != Success) {
     return rv;
   }
   if (signedData.signature.len == 0) {
-    return Fail(SEC_ERROR_BAD_SIGNATURE);
+    return Result::ERROR_BAD_SIGNATURE;
   }
   unsigned int unusedBitsAtEnd = signedData.signature.data[0];
   // XXX: Really the constraint should be that unusedBitsAtEnd must be less
   // than 7. But, we suspect there are no real-world OCSP responses or X.509
   // certificates with non-zero unused bits. It seems like NSS assumes this in
   // various places, so we enforce it too in order to simplify this code. If we
   // find compatibility issues, we'll know we're wrong and we'll have to figure
   // out how to shift the bits around.
   if (unusedBitsAtEnd != 0) {
-    return Fail(SEC_ERROR_BAD_SIGNATURE);
+    return Result::ERROR_BAD_SIGNATURE;
   }
   ++signedData.signature.data;
   --signedData.signature.len;
 
   return Success;
 }
 
 static inline Result
 ReadDigit(Input& input, /*out*/ int& value)
 {
   uint8_t b;
   if (input.Read(b) != Success) {
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
   if (b < '0' || b > '9') {
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
   value = b - '0';
   return Success;
 }
 
 static inline Result
 ReadTwoDigits(Input& input, int minValue, int maxValue, /*out*/ int& value)
 {
@@ -346,17 +346,17 @@ ReadTwoDigits(Input& input, int minValue
   }
   int lo;
   rv = ReadDigit(input, lo);
   if (rv != Success) {
     return rv;
   }
   value = (hi * 10) + lo;
   if (value < minValue || value > maxValue) {
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
   return Success;
 }
 
 inline int
 daysBeforeYear(int year)
 {
   return (365 * (year - 1))
@@ -397,22 +397,22 @@ TimeChoice(Input& tagged, uint8_t expect
   } else if (expectedTag == UTCTime) {
     rv = ReadTwoDigits(input, 0, 99, yearLo);
     if (rv != Success) {
       return rv;
     }
     yearHi = yearLo >= 50 ? 19 : 20;
   } else {
     PR_NOT_REACHED("invalid tag given to TimeChoice");
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
   int year = (yearHi * 100) + yearLo;
   if (year < 1970) {
     // We don't support dates before January 1, 1970 because that is the epoch.
-    return Fail(SEC_ERROR_INVALID_TIME); // TODO: better error code
+    return Result::ERROR_INVALID_TIME;
   }
   if (year > 1970) {
     // This is NOT equivalent to daysBeforeYear(year - 1970) because the
     // leap year calculations in daysBeforeYear only works on absolute years.
     days = daysBeforeYear(year) - daysBeforeYear(1970);
     // We subtract 1 because we're interested in knowing how many days there
     // were *before* the given year, relative to 1970.
   } else {
@@ -461,17 +461,17 @@ TimeChoice(Input& tagged, uint8_t expect
     case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun +
                                         jul + aug + sep + oct;
              break;
     case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun +
                                         jul + aug + sep + oct + nov;
              break;
     default:
       PR_NOT_REACHED("month already bounds-checked by ReadTwoDigits");
-      return Fail(PR_INVALID_STATE_ERROR);
+      return Result::FATAL_ERROR_INVALID_STATE;
   }
 
   int dayOfMonth;
   rv = ReadTwoDigits(input, 1, daysInMonth, dayOfMonth);
   if (rv != Success) {
     return rv;
   }
   days += dayOfMonth - 1;
@@ -489,23 +489,23 @@ TimeChoice(Input& tagged, uint8_t expect
   int seconds;
   rv = ReadTwoDigits(input, 0, 59, seconds);
   if (rv != Success) {
     return rv;
   }
 
   uint8_t b;
   if (input.Read(b) != Success) {
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
   if (b != 'Z') {
-    return Fail(SEC_ERROR_INVALID_TIME); // TODO: better error code?
+    return Result::ERROR_INVALID_TIME;
   }
   if (End(input) != Success) {
-    return Fail(SEC_ERROR_INVALID_TIME);
+    return Result::ERROR_INVALID_TIME;
   }
 
   int64_t totalSeconds = (static_cast<int64_t>(days) * 24 * 60 * 60) +
                          (static_cast<int64_t>(hours)     * 60 * 60) +
                          (static_cast<int64_t>(minutes)        * 60) +
                          seconds;
 
   time = totalSeconds * PR_USEC_PER_SEC;
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -87,17 +87,17 @@ ExpectTagAndLength(Input& input, uint8_t
   if (rv != Success) {
     return rv;
   }
 
   uint16_t expectedTagAndLength = static_cast<uint16_t>(expectedTag << 8);
   expectedTagAndLength |= expectedLength;
 
   if (tagAndLength != expectedTagAndLength) {
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   return Success;
 }
 
 namespace internal {
 
 Result
@@ -162,17 +162,17 @@ ExpectTagAndGetTLV(Input& input, uint8_t
   }
   return input.GetSECItem(siBuffer, mark, tlv);
 }
 
 inline Result
 End(Input& input)
 {
   if (!input.AtEnd()) {
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   return Success;
 }
 
 template <typename Decoder>
 inline Result
 Nested(Input& input, uint8_t tag, Decoder decoder)
@@ -233,17 +233,17 @@ NestedOf(Input& input, uint8_t outerTag,
   Input inner;
   Result rv = ExpectTagAndGetValue(input, outerTag, inner);
   if (rv != Success) {
     return rv;
   }
 
   if (inner.AtEnd()) {
     if (mayBeEmpty != EmptyAllowed::Yes) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
     return Success;
   }
 
   do {
     rv = Nested(inner, innerTag, decoder);
     if (rv != Success) {
       return rv;
@@ -270,17 +270,17 @@ IntegralValue(Input& input, uint8_t tag,
     return rv;
   }
   uint8_t valueByte;
   rv = input.Read(valueByte);
   if (rv != Success) {
     return rv;
   }
   if (valueByte & 0x80) { // negative
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
   value = valueByte;
   return Success;
 }
 
 } // namespace internal
 
 inline Result
@@ -295,17 +295,17 @@ Boolean(Input& input, /*out*/ bool& valu
   rv = input.Read(intValue);
   if (rv != Success) {
     return rv;
   }
   switch (intValue) {
     case 0: value = false; return Success;
     case 0xFF: value = true; return Success;
     default:
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
   }
 }
 
 // This is for any BOOLEAN DEFAULT FALSE.
 // (If it is present and false, this is a bad encoding.)
 // TODO(bug 989518): For compatibility reasons, in some places we allow
 // invalid encodings with the explicit default value.
 inline Result
@@ -314,17 +314,17 @@ OptionalBoolean(Input& input, bool allow
 {
   value = false;
   if (input.Peek(BOOLEAN)) {
     Result rv = Boolean(input, value);
     if (rv != Success) {
       return rv;
     }
     if (!allowInvalidExplicitEncoding && !value) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
   }
   return Success;
 }
 
 // This parser will only parse values between 0..127. If this range is
 // increased then callers will need to be changed.
 inline Result
@@ -377,17 +377,17 @@ Integer(Input& input, /*out*/ uint8_t& v
 // -1; defaultValue is only a parameter to make it clear in the calling code
 // what the default value is.
 inline Result
 OptionalInteger(Input& input, long defaultValue, /*out*/ long& value)
 {
   // If we need to support a different default value in the future, we need to
   // test that parsedValue != defaultValue.
   if (defaultValue != -1) {
-    return Fail(SEC_ERROR_INVALID_ARGS);
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   if (!input.Peek(INTEGER)) {
     value = defaultValue;
     return Success;
   }
 
   uint8_t parsedValue;
@@ -434,28 +434,28 @@ CertificateSerialNumber(Input& input, /*
   //   gracefully handle such certificates."
 
   Result rv = ExpectTagAndGetValue(input, INTEGER, value);
   if (rv != Success) {
     return rv;
   }
 
   if (value.len == 0) {
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   // Check for overly-long encodings. If the first byte is 0x00 then the high
   // bit on the second byte must be 1; otherwise the same *positive* value
   // could be encoded without the leading 0x00 byte. If the first byte is 0xFF
   // then the second byte must NOT have its high bit set; otherwise the same
   // *negative* value could be encoded without the leading 0xFF byte.
   if (value.len > 1) {
     if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) ||
         (value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) {
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
     }
   }
 
   return Success;
 }
 
 // x.509 and OCSP both use this same version numbering scheme, though OCSP
 // only supports v1.
@@ -488,17 +488,17 @@ OptionalVersion(Input& input, /*out*/ Ve
   }
   switch (integerValue) {
     case static_cast<uint8_t>(Version::v3): version = Version::v3; break;
     case static_cast<uint8_t>(Version::v2): version = Version::v2; break;
     // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
     // do here for compatibility reasons.
     case static_cast<uint8_t>(Version::v1): version = Version::v1; break;
     default:
-      return Fail(SEC_ERROR_BAD_DER);
+      return Result::ERROR_BAD_DER;
   }
   return Success;
 }
 
 template <typename ExtensionHandler>
 inline Result
 OptionalExtensions(Input& input, uint8_t tag, ExtensionHandler extensionHandler)
 {
@@ -563,17 +563,17 @@ OptionalExtensions(Input& input, uint8_t
     }
 
     bool understood = false;
     rv = extensionHandler(extnID, extnValue, understood);
     if (rv != Success) {
       return rv;
     }
     if (critical && !understood) {
-      return Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+      return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
     }
   }
 
   return Success;
 }
 
 Result DigestAlgorithmIdentifier(Input& input,
                                  /*out*/ DigestAlgorithm& algorithm);
deleted file mode 100644
--- a/security/pkix/lib/pkixkey.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This code is made available to you under your choice of the following sets
- * of licensing terms:
- */
-/* 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/.
- */
-/* Copyright 2013 Mozilla Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * 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.
- */
-
-#include <limits>
-#include <stdint.h>
-
-#include "cert.h"
-#include "cryptohi.h"
-#include "keyhi.h"
-#include "pk11pub.h"
-#include "pkix/pkix.h"
-#include "pkix/ScopedPtr.h"
-#include "prerror.h"
-#include "secerr.h"
-
-namespace mozilla { namespace pkix {
-
-typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> ScopedSECKeyPublicKey;
-
-SECStatus
-CheckPublicKeySize(const SECItem& subjectPublicKeyInfo,
-                   /*out*/ ScopedSECKeyPublicKey& publicKey)
-{
-  ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
-    spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo));
-  if (!spki) {
-    return SECFailure;
-  }
-  publicKey = SECKEY_ExtractPublicKey(spki.get());
-  if (!publicKey) {
-    return SECFailure;
-  }
-
-  static const unsigned int MINIMUM_NON_ECC_BITS = 1024;
-
-  switch (publicKey.get()->keyType) {
-    case ecKey:
-      // TODO(bug 622859): We should check which curve.
-      return SECSuccess;
-    case dsaKey: // fall through
-    case rsaKey:
-      // TODO(bug 622859): Enforce a minimum of 2048 bits for EV certs.
-      if (SECKEY_PublicKeyStrengthInBits(publicKey.get()) < MINIMUM_NON_ECC_BITS) {
-        // TODO(bug 1031946): Create a new error code.
-        PR_SetError(SEC_ERROR_INVALID_KEY, 0);
-        return SECFailure;
-      }
-      break;
-    case nullKey:
-    case fortezzaKey:
-    case dhKey:
-    case keaKey:
-    case rsaPssKey:
-    case rsaOaepKey:
-    default:
-      PR_SetError(SEC_ERROR_UNSUPPORTED_KEYALG, 0);
-      return SECFailure;
-  }
-
-  return SECSuccess;
-}
-
-SECStatus
-CheckPublicKey(const SECItem& subjectPublicKeyInfo)
-{
-  ScopedSECKeyPublicKey unused;
-  return CheckPublicKeySize(subjectPublicKeyInfo, unused);
-}
-
-SECStatus
-VerifySignedData(const SignedDataWithSignature& sd,
-                 const SECItem& subjectPublicKeyInfo, void* pkcs11PinArg)
-{
-  if (!sd.data.data || !sd.signature.data) {
-    PR_NOT_REACHED("invalid args to VerifySignedData");
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-
-  // See bug 921585.
-  if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-
-  SECOidTag pubKeyAlg;
-  SECOidTag digestAlg;
-  switch (sd.algorithm) {
-    case SignatureAlgorithm::ecdsa_with_sha512:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA512;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha384:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA384;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha256:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA256;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha1:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA1;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha512:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA512;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha384:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA384;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha256:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA256;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha1:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA1;
-      break;
-    case SignatureAlgorithm::dsa_with_sha256:
-      pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE;
-      digestAlg = SEC_OID_SHA256;
-      break;
-    case SignatureAlgorithm::dsa_with_sha1:
-      pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE;
-      digestAlg = SEC_OID_SHA1;
-      break;
-    default:
-      PR_NOT_REACHED("unknown signature algorithm");
-      PR_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 0);
-      return SECFailure;
-  }
-
-  ScopedSECKeyPublicKey pubKey;
-  if (CheckPublicKeySize(subjectPublicKeyInfo, pubKey) != SECSuccess) {
-    return SECFailure;
-  }
-
-  // The static_cast is safe according to the check above that references
-  // bug 921585.
-  return VFY_VerifyDataDirect(sd.data.data, static_cast<int>(sd.data.len),
-                              pubKey.get(), &sd.signature, pubKeyAlg,
-                              digestAlg, nullptr, pkcs11PinArg);
-}
-
-SECStatus
-DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
-{
-  static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH,
-                "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)");
-  if (digestBufLen != TrustDomain::DIGEST_LENGTH) {
-    PR_NOT_REACHED("invalid hash length");
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-  if (item.len >
-      static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) {
-    PR_NOT_REACHED("large OCSP responses should have already been rejected");
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-  return PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data,
-                      static_cast<int32_t>(item.len));
-}
-
-} } // namespace mozilla::pkix
new file mode 100644
--- /dev/null
+++ b/security/pkix/lib/pkixnss.cpp
@@ -0,0 +1,312 @@
+/*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+
+#include "pkix/pkixnss.h"
+
+#include <limits>
+#include <stdint.h>
+
+#include "cert.h"
+#include "cryptohi.h"
+#include "keyhi.h"
+#include "pk11pub.h"
+#include "pkix/pkix.h"
+#include "pkix/ScopedPtr.h"
+#include "secerr.h"
+
+namespace mozilla { namespace pkix {
+
+typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> ScopedSECKeyPublicKey;
+
+Result
+CheckPublicKeySize(const SECItem& subjectPublicKeyInfo,
+                   /*out*/ ScopedSECKeyPublicKey& publicKey)
+{
+  ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
+    spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo));
+  if (!spki) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+  publicKey = SECKEY_ExtractPublicKey(spki.get());
+  if (!publicKey) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+
+  static const unsigned int MINIMUM_NON_ECC_BITS = 1024;
+
+  switch (publicKey.get()->keyType) {
+    case ecKey:
+      // TODO(bug 622859): We should check which curve.
+      return Success;
+    case dsaKey: // fall through
+    case rsaKey:
+      // TODO(bug 622859): Enforce a minimum of 2048 bits for EV certs.
+      if (SECKEY_PublicKeyStrengthInBits(publicKey.get()) < MINIMUM_NON_ECC_BITS) {
+        // TODO(bug 1031946): Create a new error code.
+        return Result::ERROR_INVALID_KEY;
+      }
+      break;
+    case nullKey:
+    case fortezzaKey:
+    case dhKey:
+    case keaKey:
+    case rsaPssKey:
+    case rsaOaepKey:
+    default:
+      return Result::ERROR_UNSUPPORTED_KEYALG;
+  }
+
+  return Success;
+}
+
+Result
+CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+{
+  ScopedSECKeyPublicKey unused;
+  return CheckPublicKeySize(subjectPublicKeyInfo, unused);
+}
+
+Result
+VerifySignedData(const SignedDataWithSignature& sd,
+                 const SECItem& subjectPublicKeyInfo, void* pkcs11PinArg)
+{
+  if (!sd.data.data || !sd.signature.data) {
+    PR_NOT_REACHED("invalid args to VerifySignedData");
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+
+  // See bug 921585.
+  if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+
+  SECOidTag pubKeyAlg;
+  SECOidTag digestAlg;
+  switch (sd.algorithm) {
+    case SignatureAlgorithm::ecdsa_with_sha512:
+      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+      digestAlg = SEC_OID_SHA512;
+      break;
+    case SignatureAlgorithm::ecdsa_with_sha384:
+      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+      digestAlg = SEC_OID_SHA384;
+      break;
+    case SignatureAlgorithm::ecdsa_with_sha256:
+      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+      digestAlg = SEC_OID_SHA256;
+      break;
+    case SignatureAlgorithm::ecdsa_with_sha1:
+      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+      digestAlg = SEC_OID_SHA1;
+      break;
+    case SignatureAlgorithm::rsa_pkcs1_with_sha512:
+      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+      digestAlg = SEC_OID_SHA512;
+      break;
+    case SignatureAlgorithm::rsa_pkcs1_with_sha384:
+      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+      digestAlg = SEC_OID_SHA384;
+      break;
+    case SignatureAlgorithm::rsa_pkcs1_with_sha256:
+      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+      digestAlg = SEC_OID_SHA256;
+      break;
+    case SignatureAlgorithm::rsa_pkcs1_with_sha1:
+      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
+      digestAlg = SEC_OID_SHA1;
+      break;
+    case SignatureAlgorithm::dsa_with_sha256:
+      pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE;
+      digestAlg = SEC_OID_SHA256;
+      break;
+    case SignatureAlgorithm::dsa_with_sha1:
+      pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE;
+      digestAlg = SEC_OID_SHA1;
+      break;
+    default:
+      PR_NOT_REACHED("unknown signature algorithm");
+      return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+  }
+
+  Result rv;
+  ScopedSECKeyPublicKey pubKey;
+  rv = CheckPublicKeySize(subjectPublicKeyInfo, pubKey);
+  if (rv != Success) {
+    return rv;
+  }
+
+  // The static_cast is safe according to the check above that references
+  // bug 921585.
+  SECStatus srv = VFY_VerifyDataDirect(sd.data.data,
+                                       static_cast<int>(sd.data.len),
+                                       pubKey.get(), &sd.signature, pubKeyAlg,
+                                       digestAlg, nullptr, pkcs11PinArg);
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+
+  return Success;
+}
+
+Result
+DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+{
+  static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH,
+                "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)");
+  if (digestBufLen != TrustDomain::DIGEST_LENGTH) {
+    PR_NOT_REACHED("invalid hash length");
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+  if (item.len >
+      static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) {
+    PR_NOT_REACHED("large OCSP responses should have already been rejected");
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+  SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data,
+                               static_cast<int32_t>(item.len));
+  if (srv != SECSuccess) {
+    return MapPRErrorCodeToResult(PR_GetError());
+  }
+  return Success;
+}
+
+#define MAP_LIST \
+    MAP(Result::Success, 0) \
+    MAP(Result::ERROR_BAD_DER, SEC_ERROR_BAD_DER) \
+    MAP(Result::ERROR_CA_CERT_INVALID, SEC_ERROR_CA_CERT_INVALID) \
+    MAP(Result::ERROR_BAD_SIGNATURE, SEC_ERROR_BAD_SIGNATURE) \
+    MAP(Result::ERROR_CERT_BAD_ACCESS_LOCATION, SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \
+    MAP(Result::ERROR_CERT_NOT_IN_NAME_SPACE, SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \
+    MAP(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \
+    MAP(Result::ERROR_CONNECT_REFUSED, PR_CONNECT_REFUSED_ERROR) \
+    MAP(Result::ERROR_EXPIRED_CERTIFICATE, SEC_ERROR_EXPIRED_CERTIFICATE) \
+    MAP(Result::ERROR_EXTENSION_VALUE_INVALID, SEC_ERROR_EXTENSION_VALUE_INVALID) \
+    MAP(Result::ERROR_INADEQUATE_CERT_TYPE, SEC_ERROR_INADEQUATE_CERT_TYPE) \
+    MAP(Result::ERROR_INADEQUATE_KEY_USAGE, SEC_ERROR_INADEQUATE_KEY_USAGE) \
+    MAP(Result::ERROR_INVALID_ALGORITHM, SEC_ERROR_INVALID_ALGORITHM) \
+    MAP(Result::ERROR_INVALID_TIME, SEC_ERROR_INVALID_TIME) \
+    MAP(Result::ERROR_KEY_PINNING_FAILURE, MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \
+    MAP(Result::ERROR_PATH_LEN_CONSTRAINT_INVALID, SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \
+    MAP(Result::ERROR_POLICY_VALIDATION_FAILED, SEC_ERROR_POLICY_VALIDATION_FAILED) \
+    MAP(Result::ERROR_REVOKED_CERTIFICATE, SEC_ERROR_REVOKED_CERTIFICATE) \
+    MAP(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \
+    MAP(Result::ERROR_UNKNOWN_ERROR, PR_UNKNOWN_ERROR) \
+    MAP(Result::ERROR_UNKNOWN_ISSUER, SEC_ERROR_UNKNOWN_ISSUER) \
+    MAP(Result::ERROR_UNTRUSTED_CERT, SEC_ERROR_UNTRUSTED_CERT) \
+    MAP(Result::ERROR_UNTRUSTED_ISSUER, SEC_ERROR_UNTRUSTED_ISSUER) \
+    MAP(Result::ERROR_OCSP_BAD_SIGNATURE, SEC_ERROR_OCSP_BAD_SIGNATURE) \
+    MAP(Result::ERROR_OCSP_INVALID_SIGNING_CERT, SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \
+    MAP(Result::ERROR_OCSP_MALFORMED_REQUEST, SEC_ERROR_OCSP_MALFORMED_REQUEST) \
+    MAP(Result::ERROR_OCSP_MALFORMED_RESPONSE, SEC_ERROR_OCSP_MALFORMED_RESPONSE) \
+    MAP(Result::ERROR_OCSP_OLD_RESPONSE, SEC_ERROR_OCSP_OLD_RESPONSE) \
+    MAP(Result::ERROR_OCSP_REQUEST_NEEDS_SIG, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \
+    MAP(Result::ERROR_OCSP_RESPONDER_CERT_INVALID, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \
+    MAP(Result::ERROR_OCSP_SERVER_ERROR, SEC_ERROR_OCSP_SERVER_ERROR) \
+    MAP(Result::ERROR_OCSP_TRY_SERVER_LATER, SEC_ERROR_OCSP_TRY_SERVER_LATER) \
+    MAP(Result::ERROR_OCSP_UNAUTHORIZED_REQUEST, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \
+    MAP(Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \
+    MAP(Result::ERROR_OCSP_UNKNOWN_CERT, SEC_ERROR_OCSP_UNKNOWN_CERT) \
+    MAP(Result::ERROR_OCSP_FUTURE_RESPONSE, SEC_ERROR_OCSP_FUTURE_RESPONSE) \
+    MAP(Result::ERROR_INVALID_KEY, SEC_ERROR_INVALID_KEY) \
+    MAP(Result::ERROR_UNSUPPORTED_KEYALG, SEC_ERROR_UNSUPPORTED_KEYALG) \
+    MAP(Result::FATAL_ERROR_INVALID_ARGS, SEC_ERROR_INVALID_ARGS) \
+    MAP(Result::FATAL_ERROR_INVALID_STATE, PR_INVALID_STATE_ERROR) \
+    MAP(Result::FATAL_ERROR_LIBRARY_FAILURE, SEC_ERROR_LIBRARY_FAILURE) \
+    MAP(Result::FATAL_ERROR_NO_MEMORY, SEC_ERROR_NO_MEMORY) \
+    /* nothing here */
+
+Result
+MapPRErrorCodeToResult(PRErrorCode error)
+{
+  switch (error)
+  {
+#define MAP(mozilla_pkix_result, nss_result) \
+    case nss_result: return mozilla_pkix_result;
+
+    MAP_LIST
+
+#undef MAP
+
+    default:
+      return Result::ERROR_UNKNOWN_ERROR;
+  }
+}
+
+PRErrorCode
+MapResultToPRErrorCode(Result result)
+{
+  switch (result)
+  {
+#define MAP(mozilla_pkix_result, nss_result) \
+    case mozilla_pkix_result: return nss_result;
+
+    MAP_LIST
+
+#undef MAP
+
+    default:
+      PR_NOT_REACHED("Unknown error code in MapResultToPRErrorCode");
+      return SEC_ERROR_LIBRARY_FAILURE;
+  }
+}
+
+const char*
+MapResultToName(Result result)
+{
+  switch (result)
+  {
+#define MAP(mozilla_pkix_result, nss_result) \
+    case mozilla_pkix_result: return #mozilla_pkix_result;
+
+    MAP_LIST
+
+#undef MAP
+
+    default:
+      PR_NOT_REACHED("Unknown error code in MapResultToName");
+      return nullptr;
+  }
+}
+
+void
+RegisterErrorTable()
+{
+  static const struct PRErrorMessage ErrorTableText[] = {
+    { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE",
+      "The server uses key pinning (HPKP) but no trusted certificate chain "
+      "could be constructed that matches the pinset. Key pinning violations "
+      "cannot be overridden." }
+  };
+
+  static const struct PRErrorTable ErrorTable = {
+    ErrorTableText,
+    "pkixerrors",
+    ERROR_BASE,
+    PR_ARRAY_SIZE(ErrorTableText)
+  };
+
+  (void) PR_ErrorInstallTable(&ErrorTable);
+}
+
+} } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -24,19 +24,16 @@
 
 #include <limits>
 
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
 
-// TODO: use typed/qualified typedefs everywhere?
-// TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE?
-
 namespace mozilla { namespace pkix {
 
 static const PRTime ONE_DAY
   = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
 static const PRTime SLOP = ONE_DAY;
 
 // These values correspond to the tag values in the ASN.1 CertStatus
 MOZILLA_PKIX_ENUM_CLASS CertStatus : uint8_t {
@@ -125,31 +122,28 @@ CheckOCSPResponseSignerCert(TrustDomain&
   }
 
   // It is possible that there exists a certificate with the same key as the
   // issuer but with a different name, so we need to compare names
   // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name
   // comparison.
   // TODO: needs test
   if (!SECITEM_ItemsAreEqual(&potentialSigner.GetIssuer(), &issuerSubject)) {
-    return Fail(RecoverableError, SEC_ERROR_OCSP_RESPONDER_CERT_INVALID);
+    return Result::ERROR_OCSP_RESPONDER_CERT_INVALID;
   }
 
   // TODO(bug 926260): check name constraints
-  SECStatus srv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(),
-                                               issuerSubjectPublicKeyInfo);
-  if (srv != SECSuccess) {
-    return MapSECStatus(srv);
-  }
+  rv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(),
+                                    issuerSubjectPublicKeyInfo);
 
   // TODO: check for revocation of the OCSP responder certificate unless no-check
   // or the caller forcing no-check. To properly support the no-check policy, we'd
   // need to enforce policy constraints from the issuerChain.
 
-  return Success;
+  return rv;
 }
 
 MOZILLA_PKIX_ENUM_CLASS ResponderIDType : uint8_t
 {
   byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2
 };
 
@@ -205,32 +199,30 @@ MatchResponderID(TrustDomain& trustDomai
       if (rv != Success) {
         return rv;
       }
       return MatchKeyHash(trustDomain, keyHash,
                           potentialSignerSubjectPublicKeyInfo, match);
     }
 
     default:
-      return Fail(RecoverableError, SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+      return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
 }
 
 static Result
 VerifyOCSPSignedData(TrustDomain& trustDomain,
                      const SignedDataWithSignature& signedResponseData,
                      const SECItem& spki)
 {
-  SECStatus srv = trustDomain.VerifySignedData(signedResponseData, spki);
-  if (srv != SECSuccess) {
-    if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) {
-      PR_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE, 0);
-    }
+  Result rv = trustDomain.VerifySignedData(signedResponseData, spki);
+  if (rv == Result::ERROR_BAD_SIGNATURE) {
+    rv = Result::ERROR_OCSP_BAD_SIGNATURE;
   }
-  return MapSECStatus(srv);
+  return rv;
 }
 
 // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of
 // the cert or it must be a delegated OCSP response signing cert directly
 // issued by the issuer. If the OCSP responder is a delegated OCSP response
 // signer, then its certificate is (probably) embedded within the OCSP
 // response and we'll need to verify that it is a valid certificate that chains
 // *directly* to issuerCert.
@@ -257,101 +249,98 @@ VerifySignature(Context& context, Respon
     BackCert cert(certs[i], EndEntityOrCA::MustBeEndEntity, nullptr);
     rv = cert.Init();
     if (rv != Success) {
       return rv;
     }
     rv = MatchResponderID(context.trustDomain, responderIDType, responderID,
                           cert.GetSubject(), cert.GetSubjectPublicKeyInfo(),
                           match);
-    if (rv == FatalError) {
-      return rv;
-    }
-    if (rv == RecoverableError) {
+    if (rv != Success) {
+      if (IsFatalError(rv)) {
+        return rv;
+      }
       continue;
     }
 
     if (match) {
       rv = CheckOCSPResponseSignerCert(context.trustDomain, cert,
                                        context.certID.issuer,
                                        context.certID.issuerSubjectPublicKeyInfo,
                                        context.time);
-      if (rv == FatalError) {
-        return rv;
-      }
-      if (rv == RecoverableError) {
+      if (rv != Success) {
+        if (IsFatalError(rv)) {
+          return rv;
+        }
         continue;
       }
 
       return VerifyOCSPSignedData(context.trustDomain, signedResponseData,
                                   cert.GetSubjectPublicKeyInfo());
     }
   }
 
-  return Fail(RecoverableError, SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+  return Result::ERROR_OCSP_INVALID_SIGNING_CERT;
 }
 
-static inline void
-SetErrorToMalformedResponseOnBadDERError()
+static inline Result
+MapBadDERToMalformedOCSPResponse(Result rv)
 {
-  if (PR_GetError() == SEC_ERROR_BAD_DER) {
-    PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
+  if (rv == Result::ERROR_BAD_DER) {
+    return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
+  return rv;
 }
 
-SECStatus
+Result
 VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
                           PRTime time, uint16_t maxOCSPLifetimeInDays,
                           const SECItem& encodedResponse,
                           /*out*/ bool& expired,
                           /*optional out*/ PRTime* thisUpdate,
                           /*optional out*/ PRTime* validThrough)
 {
   // Always initialize this to something reasonable.
   expired = false;
 
   Input input;
-  if (input.Init(encodedResponse.data, encodedResponse.len) != Success) {
-    SetErrorToMalformedResponseOnBadDERError();
-    return SECFailure;
+  Result rv = input.Init(encodedResponse.data, encodedResponse.len);
+  if (rv != Success) {
+    return MapBadDERToMalformedOCSPResponse(rv);
   }
+
   Context context(trustDomain, certID, time, maxOCSPLifetimeInDays,
                   thisUpdate, validThrough);
 
-  if (der::Nested(input, der::SEQUENCE,
-                  bind(OCSPResponse, _1, ref(context))) != Success) {
-    SetErrorToMalformedResponseOnBadDERError();
-    return SECFailure;
+  rv = der::Nested(input, der::SEQUENCE, bind(OCSPResponse, _1, ref(context)));
+  if (rv != Success) {
+    return MapBadDERToMalformedOCSPResponse(rv);
   }
 
-  if (der::End(input) != Success) {
-    SetErrorToMalformedResponseOnBadDERError();
-    return SECFailure;
+  rv = der::End(input);
+  if (rv != Success) {
+    return MapBadDERToMalformedOCSPResponse(rv);
   }
 
   expired = context.expired;
 
   switch (context.certStatus) {
     case CertStatus::Good:
       if (expired) {
-        PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
-        return SECFailure;
+        return Result::ERROR_OCSP_OLD_RESPONSE;
       }
-      return SECSuccess;
+      return Success;
     case CertStatus::Revoked:
-      PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
-      return SECFailure;
+      return Result::ERROR_REVOKED_CERTIFICATE;
     case CertStatus::Unknown:
-      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
-      return SECFailure;
+      return Result::ERROR_OCSP_UNKNOWN_CERT;
   }
 
   PR_NOT_REACHED("unknown CertStatus");
-  PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
-  return SECFailure;
+  return Result::ERROR_OCSP_UNKNOWN_CERT;
 }
 
 // OCSPResponse ::= SEQUENCE {
 //       responseStatus         OCSPResponseStatus,
 //       responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
 //
 static inline Result
 OCSPResponse(Input& input, Context& context)
@@ -368,22 +357,22 @@ OCSPResponse(Input& input, Context& cont
   uint8_t responseStatus;
 
   Result rv = der::Enumerated(input, responseStatus);
   if (rv != Success) {
     return rv;
   }
   switch (responseStatus) {
     case 0: break; // successful
-    case 1: return Fail(SEC_ERROR_OCSP_MALFORMED_REQUEST);
-    case 2: return Fail(SEC_ERROR_OCSP_SERVER_ERROR);
-    case 3: return Fail(SEC_ERROR_OCSP_TRY_SERVER_LATER);
-    case 5: return Fail(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
-    case 6: return Fail(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
-    default: return Fail(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS);
+    case 1: return Result::ERROR_OCSP_MALFORMED_REQUEST;
+    case 2: return Result::ERROR_OCSP_SERVER_ERROR;
+    case 3: return Result::ERROR_OCSP_TRY_SERVER_LATER;
+    case 5: return Result::ERROR_OCSP_REQUEST_NEEDS_SIG;
+    case 6: return Result::ERROR_OCSP_UNAUTHORIZED_REQUEST;
+    default: return Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS;
   }
 
   return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
                      der::SEQUENCE, bind(ResponseBytes, _1, ref(context)));
 }
 
 // ResponseBytes ::=       SEQUENCE {
 //     responseType   OBJECT IDENTIFIER,
@@ -411,18 +400,18 @@ ResponseBytes(Input& input, Context& con
 //    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
 Result
 BasicResponse(Input& input, Context& context)
 {
   Input tbsResponseData;
   SignedDataWithSignature signedData;
   Result rv = der::SignedData(input, tbsResponseData, signedData);
   if (rv != Success) {
-    if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) {
-      return Fail(RecoverableError, SEC_ERROR_OCSP_BAD_SIGNATURE);
+    if (rv == Result::ERROR_BAD_SIGNATURE) {
+      return Result::ERROR_OCSP_BAD_SIGNATURE;
     }
     return rv;
   }
 
   // Parse certificates, if any
 
   SECItem certs[8];
   size_t numCerts = 0;
@@ -443,17 +432,17 @@ BasicResponse(Input& input, Context& con
     rv = der::ExpectTagAndSkipLength(input, der::SEQUENCE);
     if (rv != Success) {
       return rv;
     }
 
     // sequence of certificates
     while (!input.AtEnd()) {
       if (numCerts == PR_ARRAY_SIZE(certs)) {
-        return Fail(SEC_ERROR_BAD_DER);
+        return Result::ERROR_BAD_DER;
       }
 
       rv = der::ExpectTagAndGetTLV(input, der::SEQUENCE, certs[numCerts]);
       if (rv != Success) {
         return rv;
       }
       ++numCerts;
     }
@@ -475,17 +464,17 @@ ResponseData(Input& input, Context& cont
 {
   der::Version version;
   Result rv = der::OptionalVersion(input, version);
   if (rv != Success) {
     return rv;
   }
   if (version != der::Version::v1) {
     // TODO: more specific error code for bad version?
-    return Fail(SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
 
   // ResponderID ::= CHOICE {
   //    byName              [1] Name,
   //    byKey               [2] KeyHash }
   SECItem responderID;
   ResponderIDType responderIDType
     = input.Peek(static_cast<uint8_t>(ResponderIDType::byName))
@@ -606,46 +595,46 @@ SingleResponse(Input& input, Context& co
 
   PRTime thisUpdate;
   rv = der::GeneralizedTime(input, thisUpdate);
   if (rv != Success) {
     return rv;
   }
 
   if (thisUpdate > context.time + SLOP) {
-    return Fail(SEC_ERROR_OCSP_FUTURE_RESPONSE);
+    return Result::ERROR_OCSP_FUTURE_RESPONSE;
   }
 
   PRTime notAfter;
   static const uint8_t NEXT_UPDATE_TAG =
     der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0;
   if (input.Peek(NEXT_UPDATE_TAG)) {
     PRTime nextUpdate;
     rv = der::Nested(input, NEXT_UPDATE_TAG,
                     bind(der::GeneralizedTime, _1, ref(nextUpdate)));
     if (rv != Success) {
       return rv;
     }
 
     if (nextUpdate < thisUpdate) {
-      return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+      return Result::ERROR_OCSP_MALFORMED_RESPONSE;
     }
     if (nextUpdate - thisUpdate <= maxLifetime) {
       notAfter = nextUpdate;
     } else {
       notAfter = thisUpdate + maxLifetime;
     }
   } else {
     // NSS requires all OCSP responses without a nextUpdate to be recent.
     // Match that stricter behavior.
     notAfter = thisUpdate + ONE_DAY;
   }
 
   if (context.time < SLOP) { // prevent underflow
-    return Fail(SEC_ERROR_INVALID_ARGS);
+    return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   if (context.time - SLOP > notAfter) {
     context.expired = true;
   }
 
   rv = der::OptionalExtensions(input,
                                der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
@@ -672,17 +661,17 @@ SingleResponse(Input& input, Context& co
 static inline Result
 CertID(Input& input, const Context& context, /*out*/ bool& match)
 {
   match = false;
 
   DigestAlgorithm hashAlgorithm;
   Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm);
   if (rv != Success) {
-    if (PR_GetError() == SEC_ERROR_INVALID_ALGORITHM) {
+    if (rv == Result::ERROR_INVALID_ALGORITHM) {
       // Skip entries that are hashed with algorithms we don't support.
       input.SkipToEnd();
       return Success;
     }
     return rv;
   }
 
   SECItem issuerNameHash;
@@ -715,26 +704,27 @@ CertID(Input& input, const Context& cont
 
   if (hashAlgorithm != DigestAlgorithm::sha1) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
     return Success;
   }
 
   if (issuerNameHash.len != TrustDomain::DIGEST_LENGTH) {
-    return Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+    return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
 
   // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
   // "The hash shall be calculated over the DER encoding of the
   // issuer's name field in the certificate being checked."
   uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
-  if (context.trustDomain.DigestBuf(context.certID.issuer, hashBuf,
-                                    sizeof(hashBuf)) != SECSuccess) {
-    return MapSECStatus(SECFailure);
+  rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf,
+                                     sizeof(hashBuf));
+  if (rv != Success) {
+    return rv;
   }
   if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
     return Success;
   }
 
   return MatchKeyHash(context.trustDomain, issuerKeyHash,
@@ -751,17 +741,17 @@ CertID(Input& input, const Context& cont
 //                          -- BIT STRING subjectPublicKey [excluding
 //                          -- the tag, length, and number of unused
 //                          -- bits] in the responder's certificate)
 static Result
 MatchKeyHash(TrustDomain& trustDomain, const SECItem& keyHash,
              const SECItem& subjectPublicKeyInfo, /*out*/ bool& match)
 {
   if (keyHash.len != TrustDomain::DIGEST_LENGTH)  {
-    return Fail(RecoverableError, SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+    return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
   static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
   Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf,
                       sizeof hashBuf);
   if (rv != Success) {
     return rv;
   }
   match = !memcmp(hashBuf, keyHash.data, keyHash.len);
@@ -769,71 +759,71 @@ MatchKeyHash(TrustDomain& trustDomain, c
 }
 
 // TODO(bug 966856): support SHA-2 hashes
 Result
 KeyHash(TrustDomain& trustDomain, const SECItem& subjectPublicKeyInfo,
         /*out*/ uint8_t* hashBuf, size_t hashBufSize)
 {
   if (!hashBuf || hashBufSize != TrustDomain::DIGEST_LENGTH) {
-    return Fail(FatalError, SEC_ERROR_LIBRARY_FAILURE);
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   // RFC 5280 Section 4.1
   //
   // SubjectPublicKeyInfo  ::=  SEQUENCE  {
   //    algorithm            AlgorithmIdentifier,
   //    subjectPublicKey     BIT STRING  }
 
   Input spki;
+  Result rv;
 
   {
     // The scope of input is limited to reduce the possibility of confusing it
     // with spki in places we need to be using spki below.
     Input input;
-    if (input.Init(subjectPublicKeyInfo.data, subjectPublicKeyInfo.len)
-          != Success) {
-      return MapSECStatus(SECFailure);
+    rv = input.Init(subjectPublicKeyInfo.data, subjectPublicKeyInfo.len);
+    if (rv != Success) {
+      return rv;
     }
-
-    if (der::ExpectTagAndGetValue(input, der::SEQUENCE, spki) != Success) {
-      return MapSECStatus(SECFailure);
+    rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, spki);
+    if (rv != Success) {
+      return rv;
     }
-    if (der::End(input) != Success) {
-      return MapSECStatus(SECFailure);
+    rv = der::End(input);
+    if (rv != Success) {
+      return rv;
     }
   }
 
   // Skip AlgorithmIdentifier
-  if (der::ExpectTagAndSkipValue(spki, der::SEQUENCE) != Success) {
-    return MapSECStatus(SECFailure);
+  rv = der::ExpectTagAndSkipValue(spki, der::SEQUENCE);
+  if (rv != Success) {
+    return rv;
   }
 
   SECItem subjectPublicKey;
-  if (der::ExpectTagAndGetValue(spki, der::BIT_STRING, subjectPublicKey)
-        != Success) {
-    return MapSECStatus(SECFailure);
+  rv = der::ExpectTagAndGetValue(spki, der::BIT_STRING, subjectPublicKey);
+  if (rv != Success) {
+    return rv;
   }
 
-  if (der::End(spki) != Success) {
-    return MapSECStatus(SECFailure);
+  rv = der::End(spki);
+  if (rv != Success) {
+    return rv;
   }
 
   // Assume/require that the number of unused bits in the public key is zero.
   if (subjectPublicKey.len == 0 || subjectPublicKey.data[0] != 0) {
-    return Fail(RecoverableError, SEC_ERROR_BAD_DER);
+    return Result::ERROR_BAD_DER;
   }
   ++subjectPublicKey.data;
   --subjectPublicKey.len;
 
-  if (trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize)
-        != SECSuccess) {
-    return MapSECStatus(SECFailure);
-  }
-  return Success;
+  return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize);
 }
 
 Result
 ExtensionNotUnderstood(Input& /*extnID*/, const SECItem& /*extnValue*/,
                        /*out*/ bool& understood)
 {
   understood = false;
   return Success;
@@ -857,25 +847,21 @@ ExtensionNotUnderstood(Input& /*extnID*/
 //   Responses whose thisUpdate time is later than the local system time
 //   SHOULD be considered unreliable.
 //
 //   If nextUpdate is not set, the responder is indicating that newer
 //   revocation information is available all the time.
 //
 // http://tools.ietf.org/html/rfc5019#section-4
 
-SECItem*
-CreateEncodedOCSPRequest(TrustDomain& trustDomain, PLArenaPool* arena,
-                         const struct CertID& certID)
+Result
+CreateEncodedOCSPRequest(TrustDomain& trustDomain, const struct CertID& certID,
+                         /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH],
+                         /*out*/ size_t& outLen)
 {
-  if (!arena) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return nullptr;
-  }
-
   // We do not add any extensions to the request.
 
   // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response
   // types it understands. To do so, it SHOULD use an extension with the OID
   // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11
   // on Windows 8.1 does not include any extensions, whereas NSS has always
   // included the id-pkix-ocsp-response extension. Avoiding the sending the
   // extension is better for OCSP GET because it makes the request smaller,
@@ -906,63 +892,62 @@ CreateEncodedOCSPRequest(TrustDomain& tr
     + 2;                            //           serialNumber (header)
 
   // The only way we could have a request this large is if the serialNumber was
   // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST
   // NOT use serialNumber values longer than 20 octets." With this restriction,
   // we allow for some amount of non-conformance with that requirement while
   // still ensuring we can encode the length values in the ASN.1 TLV structures
   // in a single byte.
-  if (certID.serialNumber.len > 127u - totalLenWithoutSerialNumberData) {
-    PR_SetError(SEC_ERROR_BAD_DATA, 0);
-    return nullptr;
+  static_assert(totalLenWithoutSerialNumberData < OCSP_REQUEST_MAX_LENGTH,
+                "totalLenWithoutSerialNumberData too big");
+  if (certID.serialNumber.len >
+        OCSP_REQUEST_MAX_LENGTH - totalLenWithoutSerialNumberData) {
+    return Result::ERROR_BAD_DER;
   }
 
-  uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData +
-    certID.serialNumber.len);
+  outLen = totalLenWithoutSerialNumberData + certID.serialNumber.len;
 
-  SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen);
-  if (!encodedRequest) {
-    return nullptr;
-  }
+  uint8_t totalLen = static_cast<uint8_t>(outLen);
 
-  uint8_t* d = encodedRequest->data;
+  uint8_t* d = out;
   *d++ = 0x30; *d++ = totalLen - 2u;  // OCSPRequest (SEQUENCE)
   *d++ = 0x30; *d++ = totalLen - 4u;  //   tbsRequest (SEQUENCE)
   *d++ = 0x30; *d++ = totalLen - 6u;  //     requestList (SEQUENCE OF)
   *d++ = 0x30; *d++ = totalLen - 8u;  //       Request (SEQUENCE)
   *d++ = 0x30; *d++ = totalLen - 10u; //         reqCert (CertID SEQUENCE)
 
   // reqCert.hashAlgorithm
   for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) {
     *d++ = hashAlgorithm[i];
   }
 
   // reqCert.issuerNameHash (OCTET STRING)
   *d++ = 0x04;
   *d++ = hashLen;
-  if (trustDomain.DigestBuf(certID.issuer, d, hashLen) != SECSuccess) {
-    return nullptr;
+  Result rv = trustDomain.DigestBuf(certID.issuer, d, hashLen);
+  if (rv != Success) {
+    return rv;
   }
   d += hashLen;
 
   // reqCert.issuerKeyHash (OCTET STRING)
   *d++ = 0x04;
   *d++ = hashLen;
-  if (KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen)
-        != Success) {
-    return nullptr;
+  rv = KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen);
+  if (rv != Success) {
+    return rv;
   }
   d += hashLen;
 
   // reqCert.serialNumber (INTEGER)
   *d++ = 0x02; // INTEGER
   *d++ = static_cast<uint8_t>(certID.serialNumber.len);
   for (size_t i = 0; i < certID.serialNumber.len; ++i) {
     *d++ = certID.serialNumber.data[i];
   }
 
-  PR_ASSERT(d == encodedRequest->data + totalLen);
+  PR_ASSERT(d == out + totalLen);
 
-  return encodedRequest;
+  return Success;
 }
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -21,19 +21,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixutil_h
 #define mozilla_pkix__pkixutil_h
 
 #include "pkixder.h"
-#include "prerror.h"
-#include "seccomon.h"
-#include "secerr.h"
 
 namespace mozilla { namespace pkix {
 
 // During path building and verification, we build a linked list of BackCerts
 // from the current cert toward the end-entity certificate. The linked list
 // is used to verify properties that aren't local to the current certificate
 // and/or the direct link between the current certificate and its issuer,
 // such as name constraints.
@@ -165,17 +162,17 @@ public:
   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(SEC_ERROR_INVALID_ARGS);
+      return Result::FATAL_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;
--- a/security/pkix/moz.build
+++ b/security/pkix/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'lib/pkixbind.cpp',
     'lib/pkixbuild.cpp',
     'lib/pkixcert.cpp',
     'lib/pkixcheck.cpp',
     'lib/pkixder.cpp',
-    'lib/pkixkey.cpp',
+    'lib/pkixnss.cpp',
     'lib/pkixocsp.cpp',
 ]
 
 LOCAL_INCLUDES += [
     'include',
 ]
 
 TEST_DIRS += [
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -11,17 +11,16 @@ SOURCES += [
     'pkixcheck_CheckKeyUsage_tests.cpp',
     'pkixcheck_CheckValidity_tests.cpp',
 
     # The naming conventions are described in ./README.txt.
 
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
-    'pkixgtest.cpp',
     'pkixocsp_CreateEncodedOCSPRequest_tests.cpp',
     'pkixocsp_VerifyEncodedOCSPResponse.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../../include',
     '../../lib',
     '../lib',
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -19,16 +19,19 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * 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.
  */
 
 #include "nssgtest.h"
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "pkixgtest.h"
+#include "pkixtestutil.h"
 #include "prinit.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 static bool
 CreateCert(PLArenaPool* arena, const char* issuerStr,
@@ -107,82 +110,80 @@ public:
         return false;
       }
     }
 
     return true;
   }
 
 private:
-  SECStatus GetCertTrust(EndEntityOrCA,
-                         const CertPolicyId&,
-                         const SECItem& candidateCert,
-                         /*out*/ TrustLevel* trustLevel)
+  virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
+                              const SECItem& candidateCert,
+                              /*out*/ TrustLevel* trustLevel)
   {
     if (SECITEM_ItemsAreEqual(&candidateCert, &certChainTail[0]->derCert)) {
       *trustLevel = TrustLevel::TrustAnchor;
     } else {
       *trustLevel = TrustLevel::InheritsTrust;
     }
-    return SECSuccess;
+    return Success;
   }
 
-  SECStatus FindIssuer(const SECItem& encodedIssuerName,
-                       IssuerChecker& checker, PRTime time)
+  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+                            IssuerChecker& checker, PRTime time)
   {
     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,
-                                      nullptr/*additionalNameConstraints*/,
-                                      keepGoing);
-        if (srv != SECSuccess) {
-          return SECFailure;
+        Result rv = checker.Check(n->cert->derCert,
+                                  nullptr/*additionalNameConstraints*/,
+                                  keepGoing);
+        if (rv != Success) {
+          return rv;
         }
         if (!keepGoing) {
           break;
         }
       }
     }
 
-    return SECSuccess;
+    return Success;
   }
 
-  SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                            /*optional*/ const SECItem*,
-                            /*optional*/ const SECItem*)
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+                                 /*optional*/ const SECItem*,
+                                 /*optional*/ const SECItem*)
   {
-    return SECSuccess;
+    return Success;
   }
 
-  virtual SECStatus IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&)
   {
-    return SECSuccess;
+    return Success;
   }
 
-  SECStatus VerifySignedData(const SignedDataWithSignature& signedData,
-                             const SECItem& subjectPublicKeyInfo)
+  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
+                                  const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t *digestBuf,
-                              size_t digestBufLen)
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 
   // We hold references to CERTCertificates in the cert chain tail so that we
   // CERT_CreateSubjectCertList can find them.
   ScopedCERTCertificate certChainTail[7];
 
@@ -210,64 +211,63 @@ public:
 protected:
   static TestTrustDomain trustDomain;
 };
 
 /*static*/ TestTrustDomain pkixbuild::trustDomain;
 
 TEST_F(pkixbuild, MaxAcceptableCertChainLength)
 {
-  ASSERT_SECSuccess(BuildCertChain(trustDomain,
-                                   trustDomain.GetLeafCACert()->derCert,
-                                   now, EndEntityOrCA::MustBeCA,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::id_kp_serverAuth,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Success,
+            BuildCertChain(trustDomain, trustDomain.GetLeafCACert()->derCert,
+                           now, EndEntityOrCA::MustBeCA,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::id_kp_serverAuth,
+                           CertPolicyId::anyPolicy,
+                           nullptr/*stapledOCSPResponse*/));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(),
                          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/*stapledOCSPResponse*/));
+  ASSERT_EQ(Success,
+            BuildCertChain(trustDomain, cert->derCert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::id_kp_serverAuth,
+                           CertPolicyId::anyPolicy,
+                           nullptr/*stapledOCSPResponse*/));
 }
 
 TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
 {
   ScopedSECKEYPrivateKey caPrivateKey;
   ScopedCERTCertificate caCert;
   ASSERT_TRUE(CreateCert(arena.get(),
                          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/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
+            BuildCertChain(trustDomain, caCert->derCert, now,
+                           EndEntityOrCA::MustBeCA,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::id_kp_serverAuth,
+                           CertPolicyId::anyPolicy,
+                           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/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
+            BuildCertChain(trustDomain, cert->derCert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::id_kp_serverAuth,
+                           CertPolicyId::anyPolicy,
+                           nullptr/*stapledOCSPResponse*/));
 }
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -19,16 +19,18 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * 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.
  */
 
 #include "nssgtest.h"
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "pkixtestutil.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 // Creates a self-signed certificate with the given extension.
 static const SECItem*
 CreateCert(PLArenaPool* arena, const char* subjectStr,
@@ -68,60 +70,57 @@ CreateCert(PLArenaPool* arena, const cha
 {
   const SECItem * extensions[] = { extension, nullptr };
   return CreateCert(arena, subjectStr, extensions, subjectKey);
 }
 
 class TrustEverythingTrustDomain : public TrustDomain
 {
 private:
-  SECStatus GetCertTrust(EndEntityOrCA,
-                         const CertPolicyId&,
-                         const SECItem& candidateCert,
-                         /*out*/ TrustLevel* trustLevel)
+  virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
+                              const SECItem& candidateCert,
+                              /*out*/ TrustLevel* trustLevel)
   {
     *trustLevel = TrustLevel::TrustAnchor;
-    return SECSuccess;
+    return Success;
   }
 
-  SECStatus FindIssuer(const SECItem& /*encodedIssuerName*/,
-                       IssuerChecker& /*checker*/, PRTime /*time*/)
+  virtual Result FindIssuer(const SECItem& /*encodedIssuerName*/,
+                            IssuerChecker& /*checker*/, PRTime /*time*/)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                            /*optional*/ const SECItem*,
-                            /*optional*/ const SECItem*)
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+                                 /*optional*/ const SECItem*,
+                                 /*optional*/ const SECItem*)
   {
-    return SECSuccess;
+    return Success;
   }
 
-  virtual SECStatus IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&)
   {
-    return SECSuccess;
+    return Success;
   }
 
-  SECStatus VerifySignedData(const SignedDataWithSignature& signedData,
-                             const SECItem& subjectPublicKeyInfo)
+  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
+                                  const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  SECStatus DigestBuf(const SECItem&, /*out*/ uint8_t *, size_t)
+  virtual Result DigestBuf(const SECItem&, /*out*/ uint8_t*, size_t)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 };
 
 class pkixcert_extension: public NSSTest
 {
 public:
@@ -156,23 +155,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);
-  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, *cert, now,
-                                   EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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)
@@ -188,22 +187,23 @@ 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);
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
-                                   EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Success,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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[] = {
@@ -220,23 +220,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);
-  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, *cert, now,
-                                   EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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.
@@ -255,22 +255,23 @@ 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);
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
-                                   EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Success,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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[] = {
@@ -286,23 +287,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);
-  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, *cert, now,
-                                   EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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)
@@ -318,22 +319,23 @@ 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);
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert,
-                                   now, EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Success,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           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,16 +351,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);
-  ASSERT_SECFailure(SEC_ERROR_EXTENSION_VALUE_INVALID,
-                    BuildCertChain(trustDomain, *cert,
-                                   now, EndEntityOrCA::MustBeEndEntity,
-                                   KeyUsage::noParticularKeyUsageRequired,
-                                   KeyPurposeId::anyExtendedKeyUsage,
-                                   CertPolicyId::anyPolicy,
-                                   nullptr/*stapledOCSPResponse*/));
+  ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID,
+            BuildCertChain(trustDomain, *cert, now,
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::anyExtendedKeyUsage,
+                           CertPolicyId::anyPolicy,
+                           nullptr/*stapledOCSPResponse*/));
 }
--- a/security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
@@ -17,40 +17,32 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * 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.
  */
 
-#include "pkixgtest.h"
+#include "gtest/gtest.h"
+#include "pkix/pkixtypes.h"
 
 using namespace mozilla::pkix;
-using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
 extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA,
                             const SECItem* encodedKeyUsage,
                             KeyUsage requiredKeyUsageIfPresent);
 
 } } // namespace mozilla::pkix
 
-class pkixcheck_CheckKeyUsage : public ::testing::Test
-{
-public:
-  pkixcheck_CheckKeyUsage()
-  {
-    PR_SetError(0, 0);
-  }
-};
+class pkixcheck_CheckKeyUsage : public ::testing::Test { };
 
-#define ASSERT_BAD(x) \
-  ASSERT_RecoverableError(SEC_ERROR_INADEQUATE_KEY_USAGE, x)
+#define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x)
 
 // Make it easy to define test data for the common, simplest cases.
 #define NAMED_SIMPLE_KU(name, unusedBits, bits) \
   const uint8_t name##_bytes[4] = { \
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \
   }; \
   const SECItem name = { \
     siBuffer, \
@@ -66,61 +58,61 @@ static const SECItem empty_nonnull = { s
 // certificates since we don't support cRLSign.
 
 TEST_F(pkixcheck_CheckKeyUsage, EE_none)
 {
   // The input SECItem is nullptr. This means the cert had no keyUsage
   // extension. This is always valid because no key usage in an end-entity
   // means that there are no key usage restrictions.
 
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::noParticularKeyUsageRequired));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::digitalSignature));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::nonRepudiation));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::keyEncipherment));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::dataEncipherment));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                               KeyUsage::keyAgreement));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::noParticularKeyUsageRequired));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::nonRepudiation));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::keyEncipherment));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::dataEncipherment));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                                   KeyUsage::keyAgreement));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, EE_empty)
 {
   // The input SECItem is empty. The cert had an empty keyUsage extension,
   // which is syntactically invalid.
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
                            KeyUsage::digitalSignature));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
                            KeyUsage::digitalSignature));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, CA_none)
 {
   // A CA certificate does not have a KU extension.
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                               KeyUsage::keyCertSign));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
+                                   KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, CA_empty)
 {
   // A CA certificate has an empty KU extension.
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
                            KeyUsage::keyCertSign));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
                            KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits)
 {
   NAMED_SIMPLE_KU(encoded, 7, 0x80);
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
-                               KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
+                                   KeyUsage::digitalSignature));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits)
 {
   static uint8_t oneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80
   };
   const SECItem oneValueByte = {
@@ -177,18 +169,19 @@ TEST_F(pkixcheck_CheckKeyUsage, NoValueB
                            KeyUsage::keyCertSign));
 }
 
 void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage)
 {
   // Test that only the right bit is accepted for the usage for both EE and CA
   // certs.
   NAMED_SIMPLE_KU(good, unusedBits, bits);
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage));
+  ASSERT_EQ(Success,
+            CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage));
 
   // We use (~bits >> unusedBits) << unusedBits) instead of using the same
   // calculation that is in CheckKeyUsage to validate that the calculation in
   // CheckKeyUsage is correct.
 
   // Test that none of the other non-padding bits are mistaken for the given
   // key usage in the single-byte value case.
   NAMED_SIMPLE_KU(notGood, unusedBits,
@@ -221,18 +214,18 @@ TEST_F(pkixcheck_CheckKeyUsage, simpleCa
 }
 
 // Only CAs are allowed to assert keyCertSign
 TEST_F(pkixcheck_CheckKeyUsage, keyCertSign)
 {
   NAMED_SIMPLE_KU(good, 2, 0x04);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good,
                            KeyUsage::keyCertSign));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good,
-                               KeyUsage::keyCertSign));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good,
+                                   KeyUsage::keyCertSign));
 
   // Test that none of the other non-padding bits are mistaken for the given
   // key usage in the one-byte value case.
   NAMED_SIMPLE_KU(notGood, 2, 0xFB);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood,
                            KeyUsage::keyCertSign));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood,
                            KeyUsage::keyCertSign));
@@ -257,21 +250,22 @@ TEST_F(pkixcheck_CheckKeyUsage, unusedBi
   static uint8_t controlOneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
   };
   const SECItem controlOneValueByte = {
     siBuffer,
     controlOneValueByteData,
     sizeof(controlOneValueByteData)
   };
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
-                               &controlOneValueByte,
-                               KeyUsage::digitalSignature));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlOneValueByte,
-                               KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+                                   &controlOneValueByte,
+                                   KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
+                                   &controlOneValueByte,
+                                   KeyUsage::digitalSignature));
 
   // single-byte test case
   static uint8_t oneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01
   };
   const SECItem oneValueByte = {
     siBuffer,
     oneValueByteData,
@@ -287,21 +281,22 @@ TEST_F(pkixcheck_CheckKeyUsage, unusedBi
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
     0x80 | 0x01, 0x80
   };
   const SECItem controlTwoValueBytes = {
     siBuffer,
     controlTwoValueBytesData,
     sizeof(controlTwoValueBytesData)
   };
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
-                               &controlTwoValueBytes,
-                               KeyUsage::digitalSignature));
-  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlTwoValueBytes,
-                               KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+                                   &controlTwoValueBytes,
+                                   KeyUsage::digitalSignature));
+  ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
+                                   &controlTwoValueBytes,
+                                   KeyUsage::digitalSignature));
 
   // two-byte test case
   static uint8_t twoValueBytesData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
     0x80 | 0x01, 0x80 | 0x01
   };
   const SECItem twoValueBytes = {
     siBuffer,
--- a/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
@@ -17,17 +17,18 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * 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.
  */
 
-#include "pkixgtest.h"
+#include "gtest/gtest.h"
+#include "pkix/pkixtypes.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
 Result CheckValidity(const SECItem& encodedValidity, PRTime time);
@@ -55,147 +56,134 @@ static const PRTime NOW(YMDHMS(2016, 12,
 
 #define NEWER_UTCTIME \
   0x17, 13,                               /* tag, length */ \
   '2', '1', '0', '1', '0', '1',           /* 2021-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
 static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
 
-class pkixcheck_CheckValidity : public ::testing::Test
-{
-public:
-  virtual void SetUp()
-  {
-    PR_SetError(0, 0);
-  }
-};
+class pkixcheck_CheckValidity : public ::testing::Test { };
 
 TEST_F(pkixcheck_CheckValidity, BothEmptyNull)
 {
   static const uint8_t DER[] = {
     0x17/*UTCTime*/, 0/*length*/,
     0x17/*UTCTime*/, 0/*length*/,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(validity, NOW));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, NotBeforeEmptyNull)
 {
   static const uint8_t DER[] = {
     0x17/*UTCTime*/, 0x00/*length*/,
     NEWER_UTCTIME
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(validity, NOW));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, NotAfterEmptyNull)
 {
   static const uint8_t DER[] = {
     NEWER_UTCTIME,
     0x17/*UTCTime*/, 0x00/*length*/,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(validity, NOW));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW));
 }
 
 static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = {
   OLDER_UTCTIME,
   NEWER_UTCTIME,
 };
 static const SECItem OLDER_UTCTIME_NEWER_UTCTIME = {
   siBuffer,
   const_cast<uint8_t*>(OLDER_UTCTIME_NEWER_UTCTIME_DATA),
   sizeof(OLDER_UTCTIME_NEWER_UTCTIME_DATA)
 };
 
 TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
 {
-  ASSERT_Success(CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW));
+  ASSERT_EQ(Success, CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
 {
   static const uint8_t DER[] = {
     OLDER_GENERALIZEDTIME,
     NEWER_GENERALIZEDTIME,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_Success(CheckValidity(validity, NOW));
+  ASSERT_EQ(Success, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME)
 {
   static const uint8_t DER[] = {
     OLDER_GENERALIZEDTIME,
     NEWER_UTCTIME,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_Success(CheckValidity(validity, NOW));
+  ASSERT_EQ(Success, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME)
 {
   static const uint8_t DER[] = {
     OLDER_UTCTIME,
     NEWER_GENERALIZEDTIME,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_Success(CheckValidity(validity, NOW));
+  ASSERT_EQ(Success, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
 {
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME,
-                                        PAST_TIME));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
+            CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, PAST_TIME));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter)
 {
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME,
-                                        FUTURE_TIME));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
+            CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, FUTURE_TIME));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidNotAfterBeforeNotBefore)
 {
   static const uint8_t DER[] = {
     NEWER_UTCTIME,
     OLDER_UTCTIME,
   };
   static const SECItem validity = {
     siBuffer,
     const_cast<uint8_t*>(DER),
     sizeof(DER)
   };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckValidity(validity, NOW));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW));
 }
--- a/security/pkix/test/gtest/pkixder_input_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_input_tests.cpp
@@ -21,34 +21,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <functional>
 #include <vector>
 #include <gtest/gtest.h>
 
-#include "pkixgtest.h"
 #include "pkix/bind.h"
 #include "pkixder.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::der;
-using namespace mozilla::pkix::test;
 
 namespace {
 
-class pkixder_input_tests : public ::testing::Test
-{
-protected:
-  virtual void SetUp()
-  {
-    PR_SetError(0, 0);
-  }
-};
+class pkixder_input_tests : public ::testing::Test { };
 
 static const uint8_t DER_SEQUENCE_EMPTY[] = {
   0x30,                       // SEQUENCE
   0x00,                       // length
 };
 
 static const uint8_t DER_SEQUENCE_NOT_EMPTY[] = {
   0x30,                       // SEQUENCE
@@ -90,65 +81,57 @@ const uint8_t DER_OVERRUN_SEQUENCE_OF_IN
 };
 
 const uint8_t DER_INT16[] = {
   0x02,                       // INTEGER
   0x02,                       // length
   0x12, 0x34                  // 0x1234
 };
 
-TEST_F(pkixder_input_tests, FailWithError)
-{
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Fail(SEC_ERROR_BAD_DER));
-  ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, Fail(SEC_ERROR_INVALID_ARGS));
-}
-
 TEST_F(pkixder_input_tests, InputInit)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 }
 
 TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
 {
   Input input;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Init(nullptr, 0));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 0));
 
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Init(nullptr, 100));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 100));
 
   // Though it seems odd to initialize with zero-length and non-null ptr, this
   // is working as intended. The Input class was intended to protect against
   // buffer overflows, and there's no risk with the current behavior. See bug
   // 1000354.
   ASSERT_EQ(Success, input.Init((const uint8_t*) "hello", 0));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, InputInitWithLargeData)
 {
   Input input;
   // Data argument length does not matter, it is not touched, just
   // needs to be non-null
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          input.Init((const uint8_t*) "", 0xffff+1));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init((const uint8_t*) "", 0xffff+1));
 
   ASSERT_EQ(Success, input.Init((const uint8_t*) "", 0xffff));
 }
 
 TEST_F(pkixder_input_tests, InputInitMultipleTimes)
 {
   Input input;
 
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
-  ASSERT_FatalError(SEC_ERROR_INVALID_ARGS,
-                    input.Init(DER_SEQUENCE_OF_INT8,
-                               sizeof DER_SEQUENCE_OF_INT8));
+  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 }
 
 TEST_F(pkixder_input_tests, ExpectSuccess)
 {
   Input input;
 
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
@@ -160,30 +143,28 @@ TEST_F(pkixder_input_tests, ExpectSucces
 TEST_F(pkixder_input_tests, ExpectMismatch)
 {
   Input input;
 
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
   const uint8_t expected[] = { 0x11, 0x22 };
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          input.Expect(expected, sizeof expected));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected));
 }
 
 TEST_F(pkixder_input_tests, ExpectTooMuch)
 {
   Input input;
 
   const uint8_t der[] = { 0x11, 0x22 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
   const uint8_t expected[] = { 0x11, 0x22, 0x33 };
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          input.Expect(expected, sizeof expected));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected));
 }
 
 TEST_F(pkixder_input_tests, PeekWithinBounds)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x11 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
   ASSERT_TRUE(input.Peek(0x11));
@@ -224,34 +205,34 @@ TEST_F(pkixder_input_tests, ReadBytePast
   // Initialize with too-short length
   ASSERT_EQ(Success, input.Init(der, 1));
 
   uint8_t readByte1 = 0;
   ASSERT_EQ(Success, input.Read(readByte1));
   ASSERT_EQ(0x11, readByte1);
 
   uint8_t readByte2 = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readByte2));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readByte2));
   ASSERT_NE(0x22, readByte2);
 }
 
 TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer)
 {
   // The original implementation of our buffer read overflow checks was
   // susceptible to integer overflows which could make the checks ineffective.
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
   Input input;
   ASSERT_EQ(Success, input.Init(der, 0));
   uint8_t b;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(b));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
 }
 
 TEST_F(pkixder_input_tests, ReadWord)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
@@ -271,45 +252,45 @@ TEST_F(pkixder_input_tests, ReadWordPast
   // Initialize with too-short length
   ASSERT_EQ(Success, input.Init(der, 2));
 
   uint16_t readWord1 = 0;
   ASSERT_EQ(Success, input.Read(readWord1));
   ASSERT_EQ(0x1122, readWord1);
 
   uint16_t readWord2 = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readWord2));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord2));
   ASSERT_NE(0x3344, readWord2);
 }
 
 TEST_F(pkixder_input_tests, ReadWordWithInsufficentData)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22 };
   ASSERT_EQ(Success, input.Init(der, 1));
 
   uint16_t readWord1 = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(readWord1));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1));
   ASSERT_NE(0x1122, readWord1);
 }
 
 TEST_F(pkixder_input_tests, ReadWordWrapAroundPointer)
 {
   // The original implementation of our buffer read overflow checks was
   // susceptible to integer overflows which could make the checks ineffective.
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
   Input input;
   ASSERT_EQ(Success, input.Init(der, 0));
   uint16_t b;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Read(b));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
 }
 
 TEST_F(pkixder_input_tests, InputSkip)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
@@ -336,17 +317,17 @@ TEST_F(pkixder_input_tests, InputSkipToE
 }
 
 TEST_F(pkixder_input_tests, InputSkipPastEnd)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Skip(sizeof der + 1));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1));
 }
 
 TEST_F(pkixder_input_tests, InputSkipToNewInput)
 {
   Input input;
   const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
@@ -373,18 +354,17 @@ TEST_F(pkixder_input_tests, InputSkipToN
 
 TEST_F(pkixder_input_tests, InputSkipToNewInputPastEnd)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
   Input skippedInput;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          input.Skip(sizeof der * 2, skippedInput));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput));
 }
 
 TEST_F(pkixder_input_tests, InputSkipToSECItem)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
@@ -405,28 +385,27 @@ TEST_F(pkixder_input_tests, SkipWrapArou
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
   Input input;
   ASSERT_EQ(Success, input.Init(der, 0));
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, input.Skip(1));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1));
 }
 
 TEST_F(pkixder_input_tests, SkipToSECItemPastEnd)
 {
   Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
   SECItem skippedSECItem;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          input.Skip(sizeof der + 1, skippedSECItem));
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skippedSECItem));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
@@ -435,27 +414,26 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
 
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndSkipValue(input, SEQUENCE));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_OVERRUN_SEQUENCE_OF_INT8,
                                 sizeof DER_OVERRUN_SEQUENCE_OF_INT8));
   ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input));
+  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput)
 {
   Input input;
   ASSERT_TRUE(input.AtEnd());
 }
 
@@ -507,18 +485,18 @@ TEST_F(pkixder_input_tests, MarkAndGetSE
   ASSERT_EQ(Success, input.Init(der, sizeof der));
 
   Input another;
   Input::Mark mark = another.GetMark();
 
   ASSERT_EQ(Success, input.Skip(3));
 
   SECItem item;
-  ASSERT_FatalError(SEC_ERROR_INVALID_ARGS,
-                    input.GetSECItem(siBuffer, mark, item));
+  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
+            input.GetSECItem(siBuffer, mark, item));
 }
 #endif
 
 TEST_F(pkixder_input_tests, ExpectTagAndLength)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
@@ -528,28 +506,26 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
 
   // Wrong length
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndLength(input, INTEGER, 4));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, INTEGER, 4));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongTag)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
 
   // Wrong type
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndLength(input, OCTET_STRING, 2));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, OCTET_STRING, 2));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLength)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
@@ -563,31 +539,29 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongTag)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
   uint16_t length = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          der::internal::ExpectTagAndGetLength(input, INTEGER,
-                                                               length));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            der::internal::ExpectTagAndGetLength(input, INTEGER, length));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
 
   uint16_t length = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          der::internal::ExpectTagAndGetLength(input, SEQUENCE,
-                                                               length));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            der::internal::ExpectTagAndGetLength(input, SEQUENCE, length));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
   Input value;
@@ -610,38 +584,38 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 TEST_F(pkixder_input_tests,
        ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
                        sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
   Input value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, SEQUENCE, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
   Input value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, SEQUENCE, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLength_Input_InvalidWrongTag)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
   Input value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, INTEGER, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, INTEGER, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_ValidEmpty)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
   SECItem value = { siBuffer, nullptr, 5 };
@@ -667,38 +641,38 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 TEST_F(pkixder_input_tests,
        ExpectTagAndGetValue_SECItem_InvalidNotEmptyValueTruncated)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
                        sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
   SECItem value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, SEQUENCE, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_InvalidWrongLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
   SECItem value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, SEQUENCE, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLength_SECItem_InvalidWrongTag)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
   SECItem value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetValue(input, INTEGER, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndGetValue(input, INTEGER, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_ValidEmpty)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
   SECItem tlv = { siBuffer, nullptr, 5 };
@@ -727,77 +701,74 @@ TEST_F(pkixder_input_tests, ExpectTagAnd
 TEST_F(pkixder_input_tests,
        ExpectTagAndGetTLV_SECItem_InvalidNotEmptyValueTruncated)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
                        sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
   SECItem tlv;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetTLV(input, SEQUENCE, tlv));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
   SECItem tlv;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetTLV(input, SEQUENCE, tlv));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongTag)
 {
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
   SECItem tlv;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndGetTLV(input, INTEGER, tlv));
+  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipLength)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
   ASSERT_EQ(Success, ExpectTagAndSkipLength(input, INTEGER));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipLengthWithWrongTag)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
 
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          ExpectTagAndSkipLength(input, OCTET_STRING));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            ExpectTagAndSkipLength(input, OCTET_STRING));
 }
 
 TEST_F(pkixder_input_tests, EndAtEnd)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
   ASSERT_EQ(Success, input.Skip(4));
   ASSERT_EQ(Success, End(input));
 }
 
 TEST_F(pkixder_input_tests, EndBeforeEnd)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
   ASSERT_EQ(Success, input.Skip(2));
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input));
+  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 TEST_F(pkixder_input_tests, EndAtBeginning)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, End(input));
+  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 // TODO: Need tests for Nested too?
 
 Result NestedOfHelper(Input& input, std::vector<uint8_t>& readValues)
 {
   uint8_t value = 0;
   Result rv = input.Read(value);
@@ -829,21 +800,20 @@ TEST_F(pkixder_input_tests, NestedOf)
 
 TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
 {
   Input input;
   ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
                                 sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
 
   std::vector<uint8_t> readValues;
-  ASSERT_RecoverableError(
-    SEC_ERROR_BAD_DER,
-    NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
-             mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
-                                 mozilla::pkix::ref(readValues))));
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
+                     mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
+                                         mozilla::pkix::ref(readValues))));
   ASSERT_EQ((size_t) 0, readValues.size());
 }
 
 TEST_F(pkixder_input_tests, MatchRestAtEnd)
 {
   Input input;
   static const uint8_t der[1] = { };
   ASSERT_EQ(Success, input.Init(der, 0));
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -23,32 +23,24 @@
  */
 
 #include <functional>
 #include <vector>
 
 #include "nssgtest.h"
 #include "pkix/pkixtypes.h"
 #include "pkixder.h"
-#include "pkixgtest.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::der;
 using namespace mozilla::pkix::test;
 
 namespace {
 
-class pkixder_pki_types_tests : public ::testing::Test
-{
-protected:
-  virtual void SetUp()
-  {
-    PR_SetError(0, 0);
-  }
-};
+class pkixder_pki_types_tests : public ::testing::Test { };
 
 TEST_F(pkixder_pki_types_tests, CertificateSerialNumber)
 {
   const uint8_t DER_CERT_SERIAL[] = {
     0x02,                       // INTEGER
     8,                          // length
     0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
   };
@@ -108,36 +100,34 @@ TEST_F(pkixder_pki_types_tests, Certific
     0x00                        // length
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_ZERO_LENGTH,
                                 sizeof DER_CERT_SERIAL_ZERO_LENGTH));
 
   SECItem item;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          CertificateSerialNumber(input, item));
+  ASSERT_EQ(Result::ERROR_BAD_DER, CertificateSerialNumber(input, item));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed)
 {
   const uint8_t DER_OPTIONAL_VERSION_V1[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x00              // INTEGER(0)
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_V1,
                                 sizeof DER_OPTIONAL_VERSION_V1));
 
   // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
   // do here for compatibility reasons.
   // Version version;
-  // ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-  //                         OptionalVersion(input, version));
+  // ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
   der::Version version = der::Version::v3;
   ASSERT_EQ(Success, OptionalVersion(input, version));
   ASSERT_EQ(der::Version::v1, version);
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV2)
 {
   const uint8_t DER_OPTIONAL_VERSION_V2[] = {
@@ -177,32 +167,32 @@ TEST_F(pkixder_pki_types_tests, Optional
     0x02, 0x01, 0x42              // INTEGER(0x42)
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID,
                                 sizeof DER_OPTIONAL_VERSION_INVALID));
 
   der::Version version = der::Version::v1;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, OptionalVersion(input, version));
+  ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionInvalidTooLong)
 {
   const uint8_t DER_OPTIONAL_VERSION_INVALID_TOO_LONG[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x02, 0x12, 0x34        // INTEGER(0x1234)
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID_TOO_LONG,
                                 sizeof DER_OPTIONAL_VERSION_INVALID_TOO_LONG));
 
   der::Version version;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, OptionalVersion(input, version));
+  ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionMissing)
 {
   const uint8_t DER_OPTIONAL_VERSION_MISSING[] = {
     0x02, 0x11, 0x22              // INTEGER
   };
 
@@ -296,34 +286,34 @@ TEST_F(pkixder_DigestAlgorithmIdentifier
   static const uint8_t DER[] = {
     0x30, 0x0a, 0x06, 0x08,
     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   DigestAlgorithm alg;
-  ASSERT_RecoverableError(SEC_ERROR_INVALID_ALGORITHM,
-                          DigestAlgorithmIdentifier(input, alg));
+  ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
+            DigestAlgorithmIdentifier(input, alg));
 }
 
 TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_Digest_ECDSA_WITH_SHA256)
 {
   // The OID identifies ecdsa-with-SHA256 (1.2.840.10045.4.3.2). It is invalid
   // because ECDSA-with-SHA256 is not a hash algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0a, 0x06, 0x08,
     0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, //
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   DigestAlgorithm alg;
-  ASSERT_RecoverableError(SEC_ERROR_INVALID_ALGORITHM,
-                          DigestAlgorithmIdentifier(input, alg));
+  ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
+            DigestAlgorithmIdentifier(input, alg));
 }
 
 static const AlgorithmIdentifierTestInfo<SignatureAlgorithm>
   SIGNATURE_ALGORITHM_TEST_INFO[] =
 {
   { SignatureAlgorithm::ecdsa_with_sha512,
     { 0x30, 0x0a, 0x06, 0x08,
       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 },
@@ -427,29 +417,29 @@ TEST_F(pkixder_SignatureAlgorithmIdentif
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   SignatureAlgorithm alg;
-  ASSERT_RecoverableError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
-                          SignatureAlgorithmIdentifier(input, alg));
+  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
+            SignatureAlgorithmIdentifier(input, alg));
 }
 
 TEST_F(pkixder_SignatureAlgorithmIdentifier, Invalid_SignatureAlgorithm_SHA256)
 {
   // The OID identifies id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid
   // because SHA-256 is not a signature algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   SignatureAlgorithm alg;
-  ASSERT_RecoverableError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
-                          SignatureAlgorithmIdentifier(input, alg));
+  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
+            SignatureAlgorithmIdentifier(input, alg));
 }
 
 } // unnamed namespace
--- a/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -23,66 +23,58 @@
  */
 
 #include <limits>
 #include <vector>
 #include <gtest/gtest.h>
 
 #include "pkix/bind.h"
 #include "pkixder.h"
-#include "pkixgtest.h"
 #include "pkixtestutil.h"
 #include "stdint.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::der;
 using namespace mozilla::pkix::test;
 using namespace std;
 
 namespace {
 
-class pkixder_universal_types_tests : public ::testing::Test
-{
-protected:
-  virtual void SetUp()
-  {
-    PR_SetError(0, 0);
-  }
-};
+class pkixder_universal_types_tests : public ::testing::Test { };
 
 TEST_F(pkixder_universal_types_tests, BooleanTrue01)
 {
   const uint8_t DER_BOOLEAN_TRUE_01[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x01                        // invalid
   };
 
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_BOOLEAN_TRUE_01, sizeof DER_BOOLEAN_TRUE_01));
 
   bool value = false;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanTrue42)
 {
   const uint8_t DER_BOOLEAN_TRUE_42[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x42                        // invalid
   };
 
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_BOOLEAN_TRUE_42, sizeof DER_BOOLEAN_TRUE_42));
 
   bool value = false;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 static const uint8_t DER_BOOLEAN_TRUE[] = {
   0x01,                       // BOOLEAN
   0x01,                       // length
   0xff                        // true
 };
 
@@ -121,32 +113,32 @@ TEST_F(pkixder_universal_types_tests, Bo
     0x42, 0x42                  // invalid
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_LENGTH,
                                 sizeof DER_BOOLEAN_INVALID_LENGTH));
 
   bool value = true;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanInvalidZeroLength)
 {
   const uint8_t DER_BOOLEAN_INVALID_ZERO_LENGTH[] = {
     0x01,                       // BOOLEAN
     0x00                        // length
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_ZERO_LENGTH,
                                 sizeof DER_BOOLEAN_INVALID_ZERO_LENGTH));
 
   bool value = true;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Boolean(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 // OptionalBoolean implements decoding of OPTIONAL BOOLEAN DEFAULT FALSE.
 // If the field is present, it must be a valid encoding of a BOOLEAN with
 // value TRUE. If the field is not present, it defaults to FALSE. For
 // compatibility reasons, OptionalBoolean can be told to accept an encoding
 // where the field is present with value FALSE (this is technically not a
 // valid DER encoding).
@@ -201,19 +193,18 @@ TEST_F(pkixder_universal_types_tests, Op
 
   Input input1;
   ASSERT_EQ(Success, input1.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE,
                                  sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE));
   bool value;
   // If the second parameter to OptionalBoolean is false, invalid encodings
   // that include the field even when it is the DEFAULT FALSE are rejected.
   bool allowInvalidEncodings = false;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          OptionalBoolean(input1, allowInvalidEncodings,
-                                          value)) <<
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            OptionalBoolean(input1, allowInvalidEncodings, value)) <<
     "Should reject an invalid encoding of present OPTIONAL BOOLEAN";
 
   Input input2;
   ASSERT_EQ(Success, input2.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE,
                                  sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE));
   value = true;
   // If the second parameter to OptionalBoolean is true, invalid encodings
   // that include the field even when it is the DEFAULT FALSE are accepted.
@@ -229,19 +220,18 @@ TEST_F(pkixder_universal_types_tests, Op
     0x42                        // (invalid value for a BOOLEAN)
   };
 
   Input input3;
   ASSERT_EQ(Success, input3.Init(DER_OPTIONAL_BOOLEAN_PRESENT_42,
                                  sizeof DER_OPTIONAL_BOOLEAN_PRESENT_42));
   // Even with the second parameter to OptionalBoolean as true, encodings
   // of BOOLEAN that are invalid altogether are rejected.
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER,
-                          OptionalBoolean(input3, allowInvalidEncodings,
-                                          value)) <<
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            OptionalBoolean(input3, allowInvalidEncodings, value)) <<
     "Should reject another invalid encoding of present OPTIONAL BOOLEAN";
 }
 
 TEST_F(pkixder_universal_types_tests, Enumerated)
 {
   const uint8_t DER_ENUMERATED[] = {
     0x0a,                       // ENUMERATED
     0x01,                       // length
@@ -262,17 +252,17 @@ TEST_F(pkixder_universal_types_tests, En
     0x0a,                       // ENUMERATED
     0x02,                       // length
     0x00, 0x01                  // value
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED));
   uint8_t value = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedOutOfAcceptedRange)
 {
   // Although this is a valid ENUMERATED value according to ASN.1, we
   // intentionally don't support these large values because there are no
   // ENUMERATED values in X.509 certs or OCSP this large, and we're trying to
   // keep the parser simple and fast.
@@ -282,32 +272,32 @@ TEST_F(pkixder_universal_types_tests, En
     0x12, 0x34                  // value
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_LENGTH,
                                 sizeof DER_ENUMERATED_INVALID_LENGTH));
 
   uint8_t value = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
 {
   const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = {
     0x0a,                       // ENUMERATED
     0x00                        // length
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_ZERO_LENGTH,
                                 sizeof DER_ENUMERATED_INVALID_ZERO_LENGTH));
 
   uint8_t value = 0;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Enumerated(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 ////////////////////////////////////////
 // GeneralizedTime and TimeChoice
 //
 // From RFC 5280 section 4.1.2.5.2
 //
 //   For the purposes of this profile, GeneralizedTime values MUST be
@@ -391,34 +381,32 @@ template <uint16_t LENGTH>
 void
 ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
 {
   // GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME,
-                            GeneralizedTime(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 
   // TimeChoice: UTCTime
   {
     PRTime value;
-    ASSERT_RecoverableError(
-      SEC_ERROR_INVALID_TIME,
-      TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME,
+              TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
   }
 }
 
 // Control value: a valid time
 TEST_F(pkixder_universal_types_tests, ValidControl)
 {
   const uint8_t GT_DER[] = {
     0x18,                           // Generalized Time
@@ -448,34 +436,34 @@ TEST_F(pkixder_universal_types_tests, Ti
 
   PRTime value;
 
   // GeneralizedTime
   Input gt;
   ASSERT_EQ(Success,
             gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
                     sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
-  ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, GeneralizedTime(gt, value));
+  ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(gt, value));
 
   // TimeChoice: GeneralizedTime
   Input tc_gt;
   ASSERT_EQ(Success,
             tc_gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
                        sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
-  ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(tc_gt, value));
+  ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_gt, value));
 
   // TimeChoice: UTCTime
   const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = {
     0x17, // UTCTime
     0x00  // Length = 0
   };
   Input tc_utc;
   ASSERT_EQ(Success, tc_utc.Init(DER_UTCTIME_INVALID_ZERO_LENGTH,
                                  sizeof DER_UTCTIME_INVALID_ZERO_LENGTH));
-  ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(tc_utc, value));
+  ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_utc, value));
 }
 
 // A non zulu time should fail
 TEST_F(pkixder_universal_types_tests, TimeInvalidLocal)
 {
   const uint8_t DER_GENERALIZED_TIME_INVALID_LOCAL[] = {
     0x18,                           // Generalized Time
     14,                             // Length = 14
@@ -745,26 +733,25 @@ TEST_F(pkixder_universal_types_tests, Ti
 
   // We don't use ExpectBadTime here because UTCTime can't represent 2100.
 
   // GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
     PRTime value;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME,
-                            GeneralizedTime(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
     PRTime value;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeHoursValidRange)
 {
   for (uint8_t i = 0; i <= 23; ++i) {
     const uint8_t DER[] = {
       0x18,                           // Generalized Time
@@ -886,28 +873,27 @@ TEST_F(pkixder_universal_types_tests, Ti
 
   // GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success,
               input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR,
                          sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR));
     PRTime value = 0;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME,
-                            GeneralizedTime(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success,
               input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR,
                          sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR));
     PRTime value = 0;
-    ASSERT_RecoverableError(SEC_ERROR_INVALID_TIME, TimeChoice(input, value));
+    ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 
   // This test is not applicable to TimeChoice: UTCTime
 }
 
 TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar)
 {
   const uint8_t DER_GENERALIZED_TIME_INVALID_YEAR_CHAR[] = {
@@ -980,17 +966,17 @@ TEST_F(pkixder_universal_types_tests, In
     0x01, // length
     0xff, // -1 (two's complement)
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof DER));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer_Negative128)
 {
   // This is a valid integer value but our integer parser cannot parse
   // negative values.
 
   static const uint8_t DER[] = {
@@ -998,17 +984,17 @@ TEST_F(pkixder_universal_types_tests, In
     0x01, // length
     0x80, // -128 (two's complement)
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof DER));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer_128)
 {
   // This is a valid integer value but our integer parser cannot parse
   // values that require more than one byte to encode.
 
   static const uint8_t DER[] = {
@@ -1016,17 +1002,17 @@ TEST_F(pkixder_universal_types_tests, In
     0x02, // length
     0x00, 0x80 // 128
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof DER));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer11223344)
 {
   // This is a valid integer value but our integer parser cannot parse
   // values that require more than one byte to be encoded.
 
   static const uint8_t DER[] = {
@@ -1034,94 +1020,94 @@ TEST_F(pkixder_universal_types_tests, In
     0x04,                       // length
     0x11, 0x22, 0x33, 0x44      // 0x11223344
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER, sizeof DER));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerTruncatedOneByte)
 {
   const uint8_t DER_INTEGER_TRUNCATED[] = {
     0x02,                       // INTEGER
     0x01,                       // length
     // MISSING DATA HERE
   };
 
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerTruncatedLarge)
 {
   const uint8_t DER_INTEGER_TRUNCATED[] = {
     0x02,                       // INTEGER
     0x04,                       // length
     0x11, 0x22                  // 0x1122
     // MISSING DATA HERE
   };
 
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
 
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerZeroLength)
 {
   const uint8_t DER_INTEGER_ZERO_LENGTH[] = {
     0x02,                       // INTEGER
     0x00                        // length
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INTEGER_ZERO_LENGTH,
                                 sizeof DER_INTEGER_ZERO_LENGTH));
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerOverlyLong1)
 {
   const uint8_t DER_INTEGER_OVERLY_LONG1[] = {
     0x02,                       // INTEGER
     0x02,                       // length
     0x00, 0x01                  //
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG1,
                                 sizeof DER_INTEGER_OVERLY_LONG1));
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2)
 {
   const uint8_t DER_INTEGER_OVERLY_LONG2[] = {
     0x02,                       // INTEGER
     0x02,                       // length
     0xff, 0x80                  //
   };
 
   Input input;
   ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG2,
                                 sizeof DER_INTEGER_OVERLY_LONG2));
   uint8_t value;
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Integer(input, value));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefault)
 {
   // The input is a BOOLEAN and not INTEGER for the input so we'll not parse
   // anything and instead use the default value.
   Input input;
   ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
@@ -1134,17 +1120,17 @@ TEST_F(pkixder_universal_types_tests, Op
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerUnsupportedDefault)
 {
   // The same as the previous test, except with an unsupported default value
   // passed in.
   Input input;
   ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
   long value;
-  ASSERT_FatalError(SEC_ERROR_INVALID_ARGS, OptionalInteger(input, 0, value));
+  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, OptionalInteger(input, 0, value));
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefaultAtEnd)
 {
   static const uint8_t dummy = 1;
 
   Input input;
   ASSERT_EQ(Success, input.Init(&dummy, 0));
@@ -1188,17 +1174,17 @@ TEST_F(pkixder_universal_types_tests, Nu
     0x01,
     0x00
   };
 
   Input input;
   ASSERT_EQ(Success,
             input.Init(DER_NULL_BAD_LENGTH, sizeof DER_NULL_BAD_LENGTH));
 
-  ASSERT_RecoverableError(SEC_ERROR_BAD_DER, Null(input));
+  ASSERT_EQ(Result::ERROR_BAD_DER, Null(input));
 }
 
 TEST_F(pkixder_universal_types_tests, OID)
 {
   const uint8_t DER_VALID_OID[] = {
     0x06,
     0x09,
     0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
deleted file mode 100644
--- a/security/pkix/test/gtest/pkixgtest.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This code is made available to you under your choice of the following sets
- * of licensing terms:
- */
-/* 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/.
- */
-/* Copyright 2014 Mozilla Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * 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.
- */
-
-#include "pkixgtest.h"
-
-namespace mozilla { namespace pkix { namespace test {
-
-std::ostream&
-operator<<(std::ostream& os, const ResultWithPRErrorCode& value)
-{
-  switch (value.mRv)
-  {
-    case Success:
-      os << "Success";
-      break;
-    case RecoverableError:
-      os << "RecoverableError";
-      break;
-    case SECFailure:
-      os << "FatalError";
-      break;
-    default:
-      os << "[Invalid Result: " << static_cast<int64_t>(value.mRv) << ']';
-      break;
-  }
-
-  if (value.mRv != Success) {
-    os << '(';
-    const char* name = PR_ErrorToName(value.mErrorCode);
-    if (name) {
-      os << name;
-    } else {
-      os << value.mErrorCode;
-    }
-    os << ')';
-  }
-
-  return os;
-}
-
-} } } // namespace mozilla::pkix::test
--- a/security/pkix/test/gtest/pkixgtest.h
+++ b/security/pkix/test/gtest/pkixgtest.h
@@ -22,76 +22,27 @@
  * limitations under the License.
  */
 #ifndef mozilla_pkix__pkixgtest_h
 #define mozilla_pkix__pkixgtest_h
 
 #include <ostream>
 
 #include "gtest/gtest.h"
-#include "pkixutil.h"
-#include "prerror.h"
-#include "stdint.h"
-
-namespace mozilla { namespace pkix { namespace test {
-
-class ResultWithPRErrorCode
-{
-public:
-  ResultWithPRErrorCode(Result rv, PRErrorCode errorCode)
-    : mRv(rv)
-    , mErrorCode(errorCode)
-  {
-  }
+#include "pkix/pkixnss.h"
 
-  explicit ResultWithPRErrorCode(Result rv)
-    : mRv(rv)
-    , mErrorCode(rv == Success ? 0 : PR_GetError())
-  {
-  }
-
-  bool operator==(const ResultWithPRErrorCode& other) const
-  {
-    return mRv == other.mRv && mErrorCode == other.mErrorCode;
-  }
-
-private:
-  const Result mRv;
-  const PRErrorCode mErrorCode;
-
-  friend std::ostream& operator<<(std::ostream& os,
-                                  const ResultWithPRErrorCode & value);
+// PrintTo must be in the same namespace as the type we're overloading it for.
+namespace mozilla { namespace pkix {
 
-  void operator=(const ResultWithPRErrorCode&) /*= delete*/;
-};
-
-::std::ostream& operator<<(::std::ostream&, const ResultWithPRErrorCode &);
-
-#define ASSERT_Success(rv) \
-  ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                ::mozilla::pkix::Success, 0), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
-#define EXPECT_Success(rv) \
-  EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                ::mozilla::pkix::Success, 0), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
+inline void
+PrintTo(const Result& result, ::std::ostream* os)
+{
+  const char* stringified = MapResultToName(result);
+  if (stringified) {
+    *os << stringified;
+  } else {
+    *os << "mozilla::pkix::Result(" << static_cast<unsigned int>(result) << ")";
+  }
+}
 
-#define ASSERT_RecoverableError(expectedError, rv) \
-  ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                 ::mozilla::pkix::RecoverableError, expectedError), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
-#define EXPECT_RecoverableError(expectedError, rv) \
-  EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                 ::mozilla::pkix::RecoverableError, expectedError), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
-
-#define ASSERT_FatalError(expectedError, rv) \
-  ASSERT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                 ::mozilla::pkix::FatalError, expectedError), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
-#define EXPECT_FatalError(expectedError, rv) \
-  EXPECT_EQ(::mozilla::pkix::test::ResultWithPRErrorCode( \
-                 ::mozilla::pkix::FatalError, expectedError), \
-            ::mozilla::pkix::test::ResultWithPRErrorCode(rv))
-
-} } } // namespace mozilla::pkix::test
+} } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixgtest_h
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
+++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
@@ -19,71 +19,67 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * 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.
  */
 
 #include "nssgtest.h"
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
 #include "pkixder.h"
-#include "prerror.h"
+#include "pkixtestutil.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 class CreateEncodedOCSPRequestTrustDomain : public TrustDomain
 {
 private:
-  virtual SECStatus GetCertTrust(EndEntityOrCA, const CertPolicyId&,
-                                 const SECItem&, /*out*/ TrustLevel*)
+  virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
+                              const SECItem&, /*out*/ TrustLevel*)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus FindIssuer(const SECItem&, IssuerChecker&, PRTime)
+  virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                                    const SECItem*, const SECItem*)
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+                                 const SECItem*, const SECItem*)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus VerifySignedData(const SignedDataWithSignature&,
-                                     const SECItem&)
+  virtual Result VerifySignedData(const SignedDataWithSignature&,
+                                  const SECItem&)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t *digestBuf,
-                              size_t digestBufLen)
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen)
   {
     return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
   }
 
-  virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 };
 
 class pkixocsp_CreateEncodedOCSPRequest : public NSSTest
 {
 protected:
@@ -145,26 +141,33 @@ protected:
 // Test that the large length of the child serial number causes
 // CreateEncodedOCSPRequest to fail.
 TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest)
 {
   const SECItem* issuerDER;
   ScopedSECItem issuerSPKI;
   ASSERT_EQ(SECSuccess,
             MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
-  ASSERT_FALSE(CreateEncodedOCSPRequest(trustDomain, arena.get(),
-                                        CertID(*issuerDER, *issuerSPKI,
-                                               *unsupportedLongSerialNumber)));
-  ASSERT_EQ(SEC_ERROR_BAD_DATA, PR_GetError());
+  uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
+  size_t ocspRequestLength;
+  ASSERT_EQ(Result::ERROR_BAD_DER,
+            CreateEncodedOCSPRequest(trustDomain,
+                                     CertID(*issuerDER, *issuerSPKI,
+                                            *unsupportedLongSerialNumber),
+                                     ocspRequest, ocspRequestLength));
 }
 
 // Test that CreateEncodedOCSPRequest handles the longest serial number that
 // it's required to support (i.e. 20 octets).
 TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest)
 {
   const SECItem* issuerDER;
   ScopedSECItem issuerSPKI;
   ASSERT_EQ(SECSuccess,
             MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
-  ASSERT_TRUE(CreateEncodedOCSPRequest(trustDomain, arena.get(),
-                                        CertID(*issuerDER, *issuerSPKI,
-                                               *longestRequiredSerialNumber)));
+  uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
+  size_t ocspRequestLength;
+  ASSERT_EQ(Success,
+            CreateEncodedOCSPRequest(trustDomain,
+                                     CertID(*issuerDER, *issuerSPKI,
+                                            *longestRequiredSerialNumber),
+                                     ocspRequest, ocspRequestLength));
 }
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -20,84 +20,81 @@
  * 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.
  */
 
 #include "nss.h"
 #include "nssgtest.h"
 #include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "pkixgtest.h"
 #include "pkixtestutil.h"
 #include "prinit.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 const uint16_t END_ENTITY_MAX_LIFETIME_IN_DAYS = 10;
 
 class OCSPTestTrustDomain : public TrustDomain
 {
 public:
   OCSPTestTrustDomain()
   {
   }
 
-  virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA,
-                                 const CertPolicyId&,
-                                 const SECItem& candidateCert,
-                         /*out*/ TrustLevel* trustLevel)
+  virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
+                              const SECItem& candidateCert,
+                              /*out*/ TrustLevel* trustLevel)
   {
     EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
     EXPECT_TRUE(trustLevel);
     *trustLevel = TrustLevel::InheritsTrust;
-    return SECSuccess;
+    return Success;
   }
 
-  virtual SECStatus FindIssuer(const SECItem&, IssuerChecker&, PRTime)
+  virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&,
-                                    PRTime time,
-                                    /*optional*/ const SECItem*,
-                                    /*optional*/ const SECItem*)
+  virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&,
+                                 PRTime time, /*optional*/ const SECItem*,
+                                 /*optional*/ const SECItem*)
   {
     // TODO: I guess mozilla::pkix should support revocation of designated
     // OCSP responder eventually, but we don't now, so this function should
     // never get called.
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus IsChainValid(const DERArray&)
+  virtual Result IsChainValid(const DERArray&)
   {
     ADD_FAILURE();
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual SECStatus VerifySignedData(const SignedDataWithSignature& signedData,
-                                     const SECItem& subjectPublicKeyInfo)
+  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
+                                  const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  virtual SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
-                              size_t digestBufLen)
+  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen)
   {
     return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
   }
 
-  virtual SECStatus CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 
 private:
   OCSPTestTrustDomain(const OCSPTestTrustDomain&) /*delete*/;
   void operator=(const OCSPTestTrustDomain&) /*delete*/;
 };
@@ -168,29 +165,29 @@ public:
 /*static*/ long pkixocsp_VerifyEncodedResponse::rootIssuedCount = 0;
 
 ///////////////////////////////////////////////////////////////////////////////
 // responseStatus
 
 struct WithoutResponseBytes
 {
   uint8_t responseStatus;
-  PRErrorCode expectedError;
+  Result expectedError;
 };
 
 static const WithoutResponseBytes WITHOUT_RESPONSEBYTES[] = {
-  { OCSPResponseContext::successful, SEC_ERROR_OCSP_MALFORMED_RESPONSE },
-  { OCSPResponseContext::malformedRequest, SEC_ERROR_OCSP_MALFORMED_REQUEST },
-  { OCSPResponseContext::internalError, SEC_ERROR_OCSP_SERVER_ERROR },
-  { OCSPResponseContext::tryLater, SEC_ERROR_OCSP_TRY_SERVER_LATER },
-  { 4/*unused*/, SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS },
-  { OCSPResponseContext::sigRequired, SEC_ERROR_OCSP_REQUEST_NEEDS_SIG },
-  { OCSPResponseContext::unauthorized, SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST },
+  { OCSPResponseContext::successful, Result::ERROR_OCSP_MALFORMED_RESPONSE },
+  { OCSPResponseContext::malformedRequest, Result::ERROR_OCSP_MALFORMED_REQUEST },
+  { OCSPResponseContext::internalError, Result::ERROR_OCSP_SERVER_ERROR },
+  { OCSPResponseContext::tryLater, Result::ERROR_OCSP_TRY_SERVER_LATER },
+  { 4/*unused*/, Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS },
+  { OCSPResponseContext::sigRequired, Result::ERROR_OCSP_REQUEST_NEEDS_SIG },
+  { OCSPResponseContext::unauthorized, Result::ERROR_OCSP_UNAUTHORIZED_REQUEST },
   { OCSPResponseContext::unauthorized + 1,
-    SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
+    Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
   },
 };
 
 class pkixocsp_VerifyEncodedResponse_WithoutResponseBytes
   : public pkixocsp_VerifyEncodedResponse
   , public ::testing::WithParamInterface<WithoutResponseBytes>
 {
 protected:
@@ -207,20 +204,20 @@ protected:
 };
 
 TEST_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, CorrectErrorCode)
 {
   SECItem* response(CreateEncodedOCSPErrorResponse(
                       GetParam().responseStatus));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(GetParam().expectedError,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(GetParam().expectedError,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
 }
 
 INSTANTIATE_TEST_CASE_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes,
                         pkixocsp_VerifyEncodedResponse_WithoutResponseBytes,
                         testing::ValuesIn(WITHOUT_RESPONSEBYTES));
 
 ///////////////////////////////////////////////////////////////////////////////
 // "successful" responses
@@ -283,77 +280,80 @@ public:
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey)
 {
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, byKey,
                       rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       &oneDayAfterNow));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
+                                      now, END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName)
 {
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, rootName,
                       rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       &oneDayAfterNow));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate)
 {
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, byKey,
                       rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       nullptr));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked)
 {
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::revoked, *endEntityCertID, byKey,
                       rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       &oneDayAfterNow));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_REVOKED_CERTIFICATE,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown)
 {
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::unknown, *endEntityCertID, byKey,
                       rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       &oneDayAfterNow));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_UNKNOWN_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // indirect responses (signed by a delegated OCSP responder cert)
 
 class pkixocsp_VerifyEncodedResponse_DelegatedResponder
   : public pkixocsp_VerifyEncodedResponse_successful
@@ -453,72 +453,74 @@ protected:
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey)
 {
   SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
                       "CN=good_indirect_byKey", OCSPResponseContext::good,
                       byKey));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName)
 {
   SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
                       "CN=good_indirect_byName", OCSPResponseContext::good,
                       "CN=good_indirect_byName"));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_byKey_missing_signer)
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, byKey,
                       missingSignerPrivateKey, oneDayBeforeNow,
                       oneDayBeforeNow, nullptr));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_byName_missing_signer)
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, "CN=missing",
                       missingSignerPrivateKey, oneDayBeforeNow,
                       oneDayBeforeNow, nullptr));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_expired)
 {
   static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   static const char* signerName = "CN=good_indirect_expired";
 
@@ -541,20 +543,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID, signerName,
                       signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                       &oneDayAfterNow,
                       certs));
   ASSERT_TRUE(response);
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_future)
 {
   static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   static const char* signerName = "CN=good_indirect_future";
 
   const SECItem* extensions[] = {
@@ -575,49 +577,49 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem const* const certs[] = { signerDER, nullptr };
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                       OCSPResponseContext::good, *endEntityCertID,
                       signerName, signerPrivateKey, oneDayBeforeNow,
                       oneDayBeforeNow, &oneDayAfterNow, certs));
   ASSERT_TRUE(response);
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku)
 {
   SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
                       "CN=good_indirect_wrong_eku", OCSPResponseContext::good,
                       byKey, SEC_OID_UNKNOWN));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_wrong_eku)
 {
   SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
                       "CN=good_indirect_wrong_eku", OCSPResponseContext::good,
                       byKey, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
   ASSERT_TRUE(response);
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 // Test that signature of OCSP response signer cert is verified
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku)
 {
   SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
                       "CN=good_indirect_tampered_eku",
@@ -632,20 +634,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const uint8_t EKU_SERVER_AUTH[] = { EKU_PREFIX, 0x01 }; // serverAuth
   static const uint8_t EKU_OCSP_SIGNER[] = { EKU_PREFIX, 0x09 }; // OCSPSigning
 #undef EKU_PREFIX
   ASSERT_SECSuccess(TamperOnce(*response,
                                EKU_SERVER_AUTH, PR_ARRAY_SIZE(EKU_SERVER_AUTH),
                                EKU_OCSP_SIGNER, PR_ARRAY_SIZE(EKU_OCSP_SIGNER)));
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_unknown_issuer)
 {
   static const char* subCAName = "CN=good_indirect_unknown_issuer sub-CA";
   static const char* signerName = "CN=good_indirect_unknown_issuer OCSP signer";
 
@@ -672,20 +674,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem const* const certs[] = { signerDER, nullptr };
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                         OCSPResponseContext::good, *endEntityCertID,
                         signerName, signerPrivateKey, oneDayBeforeNow,
                         oneDayBeforeNow, &oneDayAfterNow, certs));
   ASSERT_TRUE(response);
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 // The CA that issued the OCSP responder cert is a sub-CA of the issuer of
 // the certificate that the OCSP response is for. That sub-CA cert is included
 // in the OCSP response before the OCSP responder cert.
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_subca_1_first)
@@ -729,20 +731,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                         OCSPResponseContext::good, *endEntityCertID, signerName,
                         signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
                         &oneDayAfterNow,
                         certs));
   ASSERT_TRUE(response);
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 // The CA that issued the OCSP responder cert is a sub-CA of the issuer of
 // the certificate that the OCSP response is for. That sub-CA cert is included
 // in the OCSP response after the OCSP responder cert.
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_subca_1_second)
@@ -785,20 +787,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem const* const certs[] = { signerDER, subCADER, nullptr };
   SECItem* response(CreateEncodedOCSPSuccessfulResponse(
                         OCSPResponseContext::good, *endEntityCertID,
                         signerName, signerPrivateKey, oneDayBeforeNow,
                         oneDayBeforeNow, &oneDayAfterNow, certs));
   ASSERT_TRUE(response);
 
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 class pkixocsp_VerifyEncodedResponse_GetCertTrust
   : public pkixocsp_VerifyEncodedResponse_DelegatedResponder {
 public:
   pkixocsp_VerifyEncodedResponse_GetCertTrust()
     : signerCertDER(nullptr)
@@ -828,63 +830,65 @@ public:
 
     bool SetCertTrust(const SECItem* certDER, TrustLevel certTrustLevel)
     {
       this->certDER = certDER;
       this->certTrustLevel = certTrustLevel;
       return true;
     }
   private:
-    virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA,
-                                   const CertPolicyId&,
-                                   const SECItem& candidateCert,
-                           /*out*/ TrustLevel* trustLevel)
+    virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
+                                const CertPolicyId&,
+                                const SECItem& candidateCert,
+                                /*out*/ TrustLevel* trustLevel)
     {
       EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
       EXPECT_TRUE(trustLevel);
       EXPECT_TRUE(certDER);
       EXPECT_TRUE(SECITEM_ItemsAreEqual(certDER, &candidateCert));
       *trustLevel = certTrustLevel;
-      return SECSuccess;
+      return Success;
     }
 
     const SECItem* certDER; // weak pointer
     TrustLevel certTrustLevel;
   };
 
   TrustDomain trustDomain;
   const SECItem* signerCertDER; // owned by arena
   SECItem* response; // owned by arena
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, InheritTrust)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::InheritsTrust));
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, TrustAnchor)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::TrustAnchor));
   bool expired;
-  ASSERT_SECSuccess(VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Success,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, ActivelyDistrusted)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::ActivelyDistrusted));
   bool expired;
-  ASSERT_SECFailure(SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
-                    VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
-                                              END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                              *response, expired));
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      *response, expired));
   ASSERT_FALSE(expired);
 }
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -28,19 +28,21 @@
 #include <limits>
 #include <new>
 
 #include "cryptohi.h"
 #include "hasht.h"
 #include "pk11pub.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
+#include "prerror.h"
 #include "prinit.h"
 #include "prprf.h"
 #include "secder.h"
+#include "secerr.h"
 
 using namespace std;
 
 namespace mozilla { namespace pkix { namespace test {
 
 const PRTime ONE_DAY = PRTime(24) * PRTime(60) * PRTime(60) * PR_USEC_PER_SEC;
 
 namespace {
@@ -158,20 +160,20 @@ public:
   // Makes a shallow copy of the input item. All input items must have a
   // lifetime that extends at least to where Squash is called.
   Result Add(const SECItem* item)
   {
     PR_ASSERT(item);
     PR_ASSERT(item->data);
 
     if (numItems >= MaxSequenceItems) {
-      return Fail(SEC_ERROR_INVALID_ARGS);
+      return Result::FATAL_ERROR_INVALID_ARGS;
     }
     if (length + item->len > 65535) {
-      return Fail(SEC_ERROR_INVALID_ARGS);
+      return Result::FATAL_ERROR_INVALID_ARGS;
     }
 
     contents[numItems] = item;
     numItems++;
     length += item->len;
     return Success;
   }