Bug 1039064 - Use strongly-typed enum instead of NSPR-style error handling. r=keeler, a=lmandel
authorBrian Smith <brian@briansmith.org>
Fri, 18 Jul 2014 11:48:49 -0700
changeset 216751 1f599d357743
parent 216750 bacdfedd7241
child 216752 93cd4a068e9d
push id3900
push userryanvm@gmail.com
push date2014-09-15 22:39 +0000
treeherdermozilla-beta@a6856f90ce36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, lmandel
bugs1039064
milestone33.0
Bug 1039064 - Use strongly-typed enum instead of NSPR-style error handling. r=keeler, a=lmandel
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/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"
@@ -82,25 +82,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.
@@ -108,60 +107,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
@@ -169,60 +166,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::PSM_ERROR_BASE,
+              "mozilla::pkix::ERROR_BASE and "
+                "nsINSSErrorsService::PSM_ERROR_BASE do not match.");
+static_assert(mozilla::pkix::ERROR_LIMIT ==
+                nsINSSErrorsService::PSM_ERROR_LIMIT,
+              "PSM_ERROR_LIMIT and "
+                "nsINSSErrorsService::PSM_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.PSM_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
@@ -180,17 +180,17 @@ BackCert::Init()
     // S/MIME Client       |  false                |  id_kp_emailProtection
     // Object Signing      |  false                |  id_kp_codeSigning
     // SSL Server CA       |  true                 |  id_pk_serverAuth
     // SSL Client CA       |  true                 |  id_kp_clientAuth
     // S/MIME CA           |  true                 |  id_kp_emailProtection
     // Object Signing CA   |  true                 |  id_kp_codeSigning
     if (criticalNetscapeCertificateType.len > 0 &&
         (basicConstraints.len == 0 || extKeyUsage.len == 0)) {
-      return Fail(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+      return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
     }
   }
 
   return der::End(tbsCertificate);
 }
 
 Result
 BackCert::RememberExtension(Input& extnID, const SECItem& extnValue,
@@ -274,21 +274,21 @@ BackCert::RememberExtension(Input& extnI
   } else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
     out = &criticalNetscapeCertificateType;
   }
 
   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)
@@ -234,17 +234,17 @@ SignatureAlgorithmOIDValue(Input& algori
     algorithm = SignatureAlgorithm::dsa_with_sha1;
   } else if (algorithmID.MatchRest(id_dsa_with_sha256)) {
     algorithm = SignatureAlgorithm::dsa_with_sha256;
   } else if (algorithmID.MatchRest(sha1WithRSASignature)) {
     // XXX(bug 1042479): recognize this old OID for compatibility.
     algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha1;
   } 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,
@@ -311,43 +311,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)
 {
@@ -358,17 +358,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))
@@ -409,22 +409,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 {
@@ -473,17 +473,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;
@@ -501,23 +501,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.
@@ -489,17 +489,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;
     case static_cast<uint8_t>(Version::v4): version = Version::v4; 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)
 {
@@ -564,17 +564,17 @@ OptionalExtensions(Input& input, uint8_t
     }
 
     bool understood = false;
     rv = extensionHandler(extnID, extnValue, critical, 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,72 @@ 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*/,
                        bool /*critical*/, /*out*/ bool& understood)
 {
   understood = false;
   return Success;
@@ -857,25 +848,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 +893,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.
@@ -166,17 +163,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
@@ -13,17 +13,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 },
@@ -434,29 +424,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 {
@@ -159,20 +161,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;
   }