bug 1006710 - add class of PSM errors to SEC and SSL errors r=briansmith
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 28 May 2014 15:28:03 -0700
changeset 185639 6dcd584751cc23bea5b56dc3c455640dd1aa8c6c
parent 185638 ee8f081ebce6c5a1c3a315dfb0d168fc5bf62f84
child 185640 0d61bcf083620018a023b1da9f735f18b621601c
push id26859
push userkwierso@gmail.com
push dateFri, 30 May 2014 00:35:41 +0000
treeherdermozilla-central@9164a6ab85b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbriansmith
bugs1006710
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1006710 - add class of PSM errors to SEC and SSL errors r=briansmith
netwerk/base/public/nsINSSErrorsService.idl
netwerk/base/src/nsSocketTransport2.cpp
security/apps/AppSignatureVerification.cpp
security/certverifier/CertVerifier.cpp
security/certverifier/NSSCertDBTrustDomain.cpp
security/manager/ssl/src/NSSErrorsService.cpp
security/manager/ssl/src/NSSErrorsService.h
security/manager/ssl/src/ScopedNSSTypes.h
security/manager/ssl/src/moz.build
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_pinning.js
toolkit/identity/IdentityCryptoService.cpp
--- a/netwerk/base/public/nsINSSErrorsService.idl
+++ b/netwerk/base/public/nsINSSErrorsService.idl
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(3a5c7a0f-f5da-4a8b-a748-d7c5a528f33b)]
+[scriptable, uuid(dc8013f2-4aed-47a8-bc4e-9fd7a6cc60fa)]
 interface nsINSSErrorsService : nsISupports
 {
     /**
      *  @param aNSPRCode An error code obtained using PR_GetError()
      *  @return True if it is error code defined by the NSS library
      */
     boolean isNSSErrorCode(in int32_t aNSPRCode);
 
@@ -45,9 +45,21 @@ interface nsINSSErrorsService : nsISuppo
      *  the values might change in the future.
      *  The security module will perform a runtime check and assertion
      *  to ensure the values are in synch with NSS.
      */
     const long NSS_SEC_ERROR_BASE  = -(0x2000);
     const long NSS_SEC_ERROR_LIMIT = (NSS_SEC_ERROR_BASE + 1000);
     const long NSS_SSL_ERROR_BASE  = -(0x3000);
     const long NSS_SSL_ERROR_LIMIT = (NSS_SSL_ERROR_BASE + 1000);
+
+    /**
+     * The error codes within each module must fit in 16 bits. We want these
+     * errors to fit in the same module as the NSS errors but not overlap with
+     * any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
+     * involves negating the value of the error and then synthesizing an error
+     * in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at
+     * a negative value that both doesn't overlap with the current value
+     * ranges for NSS errors and that will fit in 16 bits when negated.
+     */
+    const long PSM_ERROR_BASE  = -(0x4000);
+    const long PSM_ERROR_LIMIT = (PSM_ERROR_BASE + 1000);
 };
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -18,22 +18,22 @@
 #include "nsProxyInfo.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "plstr.h"
 #include "prerr.h"
 #include "NetworkActivityMonitor.h"
+#include "NSSErrorsService.h"
 #include "mozilla/VisualEventTracer.h"
 #include "nsThreadUtils.h"
 #include "nsISocketProviderService.h"
 #include "nsISocketProvider.h"
 #include "nsISSLSocketControl.h"
-#include "nsINSSErrorsService.h"
 #include "nsIPipe.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsIClassInfoImpl.h"
 #include "nsURLHelper.h"
 #include "nsIDNSService.h"
 #include "nsIDNSRecord.h"
 #include "nsICancelable.h"
 #include <algorithm>
@@ -126,38 +126,16 @@ static PRErrorCode RandomizeConnectError
         SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
     }
     return code;
 }
 #endif
 
 //-----------------------------------------------------------------------------
 
-static bool
-IsNSSErrorCode(PRErrorCode code)
-{
-  return 
-    ((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) && 
-      (code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
-    ||
-    ((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) && 
-      (code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
-}
-
-// this logic is duplicated from the implementation of
-// nsINSSErrorsService::getXPCOMFromNSSError
-// It might have been better to implement that interface here...
-static nsresult
-GetXPCOMFromNSSError(PRErrorCode code)
-{
-    // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
-    return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-                                               -1 * code);
-}
-
 nsresult
 ErrorAccordingToNSPR(PRErrorCode errorCode)
 {
     nsresult rv = NS_ERROR_FAILURE;
     switch (errorCode) {
     case PR_WOULD_BLOCK_ERROR:
         rv = NS_BASE_STREAM_WOULD_BLOCK;
         break;
@@ -219,18 +197,19 @@ ErrorAccordingToNSPR(PRErrorCode errorCo
         break;
     case PR_NOT_DIRECTORY_ERROR:
         rv = NS_ERROR_FILE_NOT_DIRECTORY;
         break;
     case PR_READ_ONLY_FILESYSTEM_ERROR:
         rv = NS_ERROR_FILE_READ_ONLY;
         break;
     default:
-        if (IsNSSErrorCode(errorCode))
-            rv = GetXPCOMFromNSSError(errorCode);
+        if (mozilla::psm::IsNSSErrorCode(errorCode)) {
+            rv = mozilla::psm::GetXPCOMFromNSSError(errorCode);
+        }
         break;
 
     // NSPR's socket code can return these, but they're not worth breaking out
     // into their own error codes, distinct from NS_ERROR_FAILURE:
     //
     // PR_BAD_DESCRIPTOR_ERROR
     // PR_INVALID_ARGUMENT_ERROR
     // PR_NOT_SOCKET_ERROR
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -151,17 +151,17 @@ VerifyEntryContentDigest(nsIZipReader * 
   rv = stream->Available(&len64);
   NS_ENSURE_SUCCESS(rv, rv);
   if (len64 > UINT32_MAX) {
     return NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE;
   }
 
   ScopedPK11Context digestContext(PK11_CreateDigestContext(SEC_OID_SHA1));
   if (!digestContext) {
-    return PRErrorCode_to_nsresult(PR_GetError());
+    return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
   }
 
   rv = MapSECStatus(PK11_DigestBegin(digestContext));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t totalBytesRead = 0;
   for (;;) {
     uint32_t bytesRead;
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -3,25 +3,26 @@
 /* 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 "CertVerifier.h"
 
 #include <stdint.h>
 
-#include "pkix/pkix.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
+#include "NSSErrorsService.h"
 #include "PublicKeyPinningService.h"
 #include "cert.h"
 #include "ocsp.h"
+#include "pk11pub.h"
+#include "pkix/pkix.h"
+#include "prerror.h"
 #include "secerr.h"
-#include "pk11pub.h"
-#include "prerror.h"
 #include "sslerr.h"
 
 // ScopedXXX in this file are mozilla::pkix::ScopedXXX, not
 // mozilla::ScopedXXX.
 using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
 #ifdef PR_LOGGING
@@ -264,20 +265,20 @@ ClassicVerifyCert(CERTCertificate* cert,
       SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
                                               &chainOK);
       if (srv != SECSuccess) {
         return srv;
       }
       if (chainOK != PR_TRUE) {
         if (verifyLog) {
           insertErrorIntoVerifyLog(cert,
-                                   SEC_ERROR_APPLICATION_CALLBACK_ERROR,
+                                   PSM_ERROR_KEY_PINNING_FAILURE,
                                    verifyLog);
         }
-        PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
+        PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
         return SECFailure;
       }
     }
     if (rv == SECSuccess && validationChain) {
       *validationChain = certChain.release();
     }
   }
 
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
 #include "ExtendedValidation.h"
+#include "NSSErrorsService.h"
 #include "OCSPRequestor.h"
 #include "certdb.h"
 #include "mozilla/Telemetry.h"
 #include "nss.h"
 #include "ocsp.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
 #include "prerror.h"
@@ -456,17 +457,17 @@ NSSCertDBTrustDomain::IsChainValid(const
     return rv;
   }
   // rv = SECSuccess only implies successful call, now is time
   // to check the chain check status
   // we should only return success if the chain is valid
   if (chainOK) {
     return SECSuccess;
   }
-  PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
+  PR_SetError(PSM_ERROR_KEY_PINNING_FAILURE, 0);
   return SECFailure;
 }
 
 namespace {
 
 static char*
 nss_addEscape(const char* string, char quote)
 {
--- a/security/manager/ssl/src/NSSErrorsService.cpp
+++ b/security/manager/ssl/src/NSSErrorsService.cpp
@@ -10,16 +10,43 @@
 #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 bool
+IsPSMError(PRErrorCode error)
+{
+  return (error >= nsINSSErrorsService::PSM_ERROR_BASE &&
+          error < nsINSSErrorsService::PSM_ERROR_LIMIT);
+}
+
 NS_IMPL_ISUPPORTS(NSSErrorsService, nsINSSErrorsService)
 
 nsresult
 NSSErrorsService::Init()
 {
   nsresult rv;
   nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
   if (NS_FAILED(rv) || !bundleService) 
@@ -45,58 +72,77 @@ NSSErrorsService::Init()
 #error "Unexpected change of error code numbers in lib NSS, please adjust the mapping code"
 /*
  * Please ensure the NSS error codes are mapped into the positive range 0x1000 to 0xf000
  * Search for NS_ERROR_MODULE_SECURITY to ensure there are no conflicts.
  * The current code also assumes that NSS library error codes are negative.
  */
 #endif
 
+bool
+IsNSSErrorCode(PRErrorCode code)
+{
+  return IS_SEC_ERROR(code) || IS_SSL_ERROR(code) || IsPSMError(code);
+}
+
+nsresult
+GetXPCOMFromNSSError(PRErrorCode code)
+{
+  if (!code) {
+    MOZ_CRASH("Function failed without calling PR_GetError");
+  }
+
+  // The error codes within each module must be a 16 bit value.
+  // For simplicity we use the positive value of the NSS code.
+  return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
+                                             -1 * code);
+}
+
 NS_IMETHODIMP
 NSSErrorsService::IsNSSErrorCode(int32_t aNSPRCode, bool *_retval)
 {
-  if (!_retval)
-    return NS_ERROR_FAILURE;
+  if (!_retval) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
-  *_retval = IS_SEC_ERROR(aNSPRCode) || IS_SSL_ERROR(aNSPRCode);
+  *_retval = mozilla::psm::IsNSSErrorCode(aNSPRCode);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NSSErrorsService::GetXPCOMFromNSSError(int32_t aNSPRCode, nsresult *aXPCOMErrorCode)
 {
-  if (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
-    return NS_ERROR_FAILURE;
-
-  if (!aXPCOMErrorCode)
+  if (!aXPCOMErrorCode) {
     return NS_ERROR_INVALID_ARG;
+  }
 
-  // The error codes within each module may be a 16 bit value.
-  // For simplicity let's use the positive value of the NSS code.
-  // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
+  if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
-  *aXPCOMErrorCode =
-    (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-                                        -1 * aNSPRCode);
+  *aXPCOMErrorCode = mozilla::psm::GetXPCOMFromNSSError(aNSPRCode);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NSSErrorsService::GetErrorClass(nsresult aXPCOMErrorCode, uint32_t *aErrorClass)
 {
   NS_ENSURE_ARG(aErrorClass);
 
-  if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY
-      || NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR)
+  if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY ||
+      NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR) {
     return NS_ERROR_FAILURE;
+  }
   
   int32_t aNSPRCode = -1 * NS_ERROR_GET_CODE(aXPCOMErrorCode);
 
-  if (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
+  if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
     return NS_ERROR_FAILURE;
+  }
 
   switch (aNSPRCode)
   {
     // Overridable errors.
     case SEC_ERROR_UNKNOWN_ISSUER:
     case SEC_ERROR_UNTRUSTED_ISSUER:
     case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
     case SEC_ERROR_UNTRUSTED_CERT:
@@ -111,35 +157,38 @@ NSSErrorsService::GetErrorClass(nsresult
       break;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NSSErrorsService::GetErrorMessage(nsresult aXPCOMErrorCode, nsAString &aErrorMessage)
 {
-  if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY
-      || NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR)
+  if (NS_ERROR_GET_MODULE(aXPCOMErrorCode) != NS_ERROR_MODULE_SECURITY ||
+      NS_ERROR_GET_SEVERITY(aXPCOMErrorCode) != NS_ERROR_SEVERITY_ERROR) {
     return NS_ERROR_FAILURE;
+  }
   
   int32_t aNSPRCode = -1 * NS_ERROR_GET_CODE(aXPCOMErrorCode);
 
-  if (!IS_SEC_ERROR(aNSPRCode) && !IS_SSL_ERROR(aNSPRCode))
+  if (!mozilla::psm::IsNSSErrorCode(aNSPRCode)) {
     return NS_ERROR_FAILURE;
+  }
 
   nsCOMPtr<nsIStringBundle> theBundle = mPIPNSSBundle;
   const char *id_str = nsNSSErrors::getOverrideErrorStringName(aNSPRCode);
 
   if (!id_str) {
     id_str = nsNSSErrors::getDefaultErrorStringName(aNSPRCode);
     theBundle = mNSSErrorsBundle;
   }
 
-  if (!id_str || !theBundle)
+  if (!id_str || !theBundle) {
     return NS_ERROR_FAILURE;
+  }
 
   nsAutoString msg;
   nsresult rv =
     theBundle->GetStringFromName(NS_ConvertASCIItoUTF16(id_str).get(),
                                  getter_Copies(msg));
   if (NS_SUCCEEDED(rv)) {
     aErrorMessage = msg;
   }
--- a/security/manager/ssl/src/NSSErrorsService.h
+++ b/security/manager/ssl/src/NSSErrorsService.h
@@ -1,31 +1,46 @@
 /* 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 "nsIStringBundle.h"
+#include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
-#include "mozilla/Attributes.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();
 
 private:
   nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
   nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
 };
 
+bool IsNSSErrorCode(PRErrorCode code);
+nsresult GetXPCOMFromNSSError(PRErrorCode code);
+
 } // psm
 } // mozilla
 
 #define NS_NSSERRORSSERVICE_CID \
   { 0x9ef18451, 0xa157, 0x4d17, { 0x81, 0x32, 0x47, 0xaf, 0xef, 0x21, 0x36, 0x89 } }
+
+#endif // NSSErrorsService_h
--- a/security/manager/ssl/src/ScopedNSSTypes.h
+++ b/security/manager/ssl/src/ScopedNSSTypes.h
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ScopedNSSTypes_h
 #define mozilla_ScopedNSSTypes_h
 
+#include "NSSErrorsService.h"
 #include "mozilla/Likely.h"
 #include "mozilla/mozalloc_oom.h"
 #include "mozilla/Scoped.h"
 #include "nsError.h"
 #include "nsDebug.h"
 #include "prio.h"
 #include "cert.h"
 #include "cms.h"
@@ -37,47 +38,32 @@ char_ptr_cast(const uint8_t * p) { retur
 
 inline uint8_t *
 uint8_t_ptr_cast(char * p) { return reinterpret_cast<uint8_t*>(p); }
 
 inline const uint8_t *
 uint8_t_ptr_cast(const char * p) { return reinterpret_cast<const uint8_t*>(p); }
 
 // NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to
-// report success/failure. These funtions make it more convenient and *safer*
-// to translate NSPR/NSS results to nsresult. They are safer because they
-// refuse to traslate any bad PRStatus/SECStatus into an NS_OK, even when the
-// NSPR/NSS function forgot to call PR_SetError.
-
-// IMPORTANT: This must be called immediately after the function that set the
-// error code. Prefer using MapSECStatus to this.
-inline nsresult
-PRErrorCode_to_nsresult(PRErrorCode error)
-{
-  if (!error) {
-    MOZ_CRASH("Function failed without calling PR_GetError");
-  }
-
-  // From NSSErrorsService::GetXPCOMFromNSSError
-  // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
-  return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
-                                             -1 * error);
-}
-
+// report success/failure. This funtion makes it more convenient and *safer*
+// to translate NSPR/NSS results to nsresult. It is safer because it
+// refuses to traslate any bad PRStatus/SECStatus into an NS_OK, even when the
+// NSPR/NSS function forgot to call PR_SetError. The actual enforcement of
+// this happens in mozilla::psm::GetXPCOMFromNSSError.
 // IMPORTANT: This must be called immediately after the function returning the
 // SECStatus result. The recommended usage is:
 //    nsresult rv = MapSECStatus(f(x, y, z));
 inline nsresult
 MapSECStatus(SECStatus rv)
 {
-  if (rv == SECSuccess)
+  if (rv == SECSuccess) {
     return NS_OK;
+  }
 
-  PRErrorCode error = PR_GetError();
-  return PRErrorCode_to_nsresult(error);
+  return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
 }
 
 // Alphabetical order by NSS type
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
                                           PRFileDesc,
                                           PR_Close)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTCertificate,
                                           CERTCertificate,
--- a/security/manager/ssl/src/moz.build
+++ b/security/manager/ssl/src/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'CryptoTask.h',
     'nsCrypto.h',
     'nsNSSShutDown.h',
     'nsRandomGenerator.h',
+    'NSSErrorsService.h',
     'ScopedNSSTypes.h',
 ]
 
 EXPORTS.mozilla += [
     'PublicSSL.h',
 ]
 
 UNIFIED_SOURCES += [
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -46,16 +46,17 @@
 #include "nsIPrompt.h"
 #include "nsCertificatePrincipal.h"
 #include "nsIBufEntropyCollector.h"
 #include "nsITokenPasswordDialogs.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNSSShutDown.h"
 #include "GeneratedEvents.h"
 #include "SharedSSLState.h"
+#include "NSSErrorsService.h"
 
 #include "nss.h"
 #include "ssl.h"
 #include "sslproto.h"
 #include "secmod.h"
 #include "secmime.h"
 #include "ocsp.h"
 #include "secerr.h"
@@ -1275,16 +1276,18 @@ nsNSSComponent::InitializeNSS()
 
   mHttpForNSS.initTable();
   mHttpForNSS.registerHttpClient();
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
   LaunchSmartCardThreads();
 #endif
 
+  RegisterPSMErrorTable();
+
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n"));
   return NS_OK;
 }
 
 void
 nsNSSComponent::ShutdownNSS()
 {
   // Can be called both during init and profile change,
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -34,16 +34,17 @@
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsIConsoleService.h"
 #include "PSMRunnable.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
+#include "NSSErrorsService.h"
 
 #include "ssl.h"
 #include "sslproto.h"
 #include "secerr.h"
 #include "sslerr.h"
 #include "secder.h"
 #include "keyhi.h"
 
@@ -1091,17 +1092,17 @@ checkHandshake(int32_t bytesTransfered, 
     // socket. This might be reached at any time of the connection.
     //
     // The socketInfo->GetErrorCode() check is here to ensure we don't try to
     // do the synchronous dispatch to the main thread unnecessarily after we've
     // already handled a certificate error. (SSLErrorRunnable calls
     // nsHandleSSLError, which has logic to avoid replacing the error message,
     // so without the !socketInfo->GetErrorCode(), it would just be an
     // expensive no-op.)
-    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
+    if (!wantRetry && mozilla::psm::IsNSSErrorCode(err) &&
         !socketInfo->GetErrorCode()) {
       RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
                                                              PlainErrorMessage,
                                                              err));
       (void) runnable->DispatchToMainThreadAndWait();
     }
   } else if (wasReading && 0 == bytesTransfered) {
     // zero bytes on reading, socket closed
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -15,16 +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;
 
 // 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;
@@ -51,16 +52,18 @@ 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;
+
 // Supported Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
 const certificateUsageObjectSigner           = 0x0040;
 const certificateUsageVerifyCA               = 0x0100;
--- 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(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
+    getXPCOMStatusFromNSS(PSM_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(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
+    getXPCOMStatusFromNSS(PSM_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(SEC_ERROR_APPLICATION_CALLBACK_ERROR));
+    getXPCOMStatusFromNSS(PSM_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/toolkit/identity/IdentityCryptoService.cpp
+++ b/toolkit/identity/IdentityCryptoService.cpp
@@ -11,16 +11,17 @@
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
 #include "nsProxyRelease.h"
 #include "nsString.h"
 #include "mozilla/ArrayUtils.h" // ArrayLength
 #include "mozilla/Base64.h"
 #include "ScopedNSSTypes.h"
+#include "NSSErrorsService.h"
 
 #include "nss.h"
 #include "pk11pub.h"
 #include "secmod.h"
 #include "secerr.h"
 #include "keyhi.h"
 #include "cryptohi.h"
 
@@ -348,17 +349,17 @@ GenerateKeyPair(PK11SlotInfo * slot,
 {
   *publicKey = nullptr;
   *privateKey = PK11_GenerateKeyPair(slot, mechanism, params, publicKey,
                                      PR_FALSE /*isPerm*/,
                                      PR_TRUE /*isSensitive*/,
                                      nullptr /*&pwdata*/);
   if (!*privateKey) {
     MOZ_ASSERT(!*publicKey);
-    return PRErrorCode_to_nsresult(PR_GetError());
+    return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
   }
   if (!*publicKey) {
 	  SECKEY_DestroyPrivateKey(*privateKey);
 	  *privateKey = nullptr;
     MOZ_CRASH("PK11_GnerateKeyPair returned private key without public key");
   }
 
   return NS_OK;
@@ -506,19 +507,19 @@ SignRunnable::Run()
       mRv = NS_ERROR_NOT_AVAILABLE;
     } else {
       // We need the output in PKCS#11 format, not DER encoding, so we must use
       // PK11_HashBuf and PK11_Sign instead of SEC_SignData.
 
       SECItem sig = { siBuffer, nullptr, 0 };
       int sigLength = PK11_SignatureLen(mPrivateKey);
       if (sigLength <= 0) {
-        mRv = PRErrorCode_to_nsresult(PR_GetError());
+        mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
       } else if (!SECITEM_AllocItem(nullptr, &sig, sigLength)) {
-        mRv = PRErrorCode_to_nsresult(PR_GetError());
+        mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
       } else {
         uint8_t hash[32]; // big enough for SHA-1 or SHA-256
         SECOidTag hashAlg = mPrivateKey->keyType == dsaKey ? SEC_OID_SHA1
                                                            : SEC_OID_SHA256;
         SECItem hashItem = { siBuffer, hash,
                              hashAlg == SEC_OID_SHA1 ? 20u : 32u };
 
         mRv = MapSECStatus(PK11_HashBuf(hashAlg, hash,