Bug 878932, Part 1: add insanity::pkix as an option for certificate verification, r=keeler, r=cviecco, a=sledru
authorBrian Smith <brian@briansmith.org>
Mon, 10 Feb 2014 11:41:12 -0800
changeset 177032 676c7d391cd5d83ecb80b0bf149951a0f04a3a2c
parent 177031 c340ceee90091571556ebeaded9c24078e12fa0d
child 177033 0939d42b51df3735500cfb60bb49a3bf8f551662
push idunknown
push userunknown
push dateunknown
reviewerskeeler, cviecco, sledru
bugs878932
milestone29.0a2
Bug 878932, Part 1: add insanity::pkix as an option for certificate verification, r=keeler, r=cviecco, a=sledru
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/SharedCertVerifier.h
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsNSSComponent.cpp
toolkit/components/telemetry/Histograms.json
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CertVerifier.h"
 
 #include <stdint.h>
 
-#include "insanity/pkixtypes.h"
+#include "insanity/pkix.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "cert.h"
 #include "ocsp.h"
 #include "secerr.h"
 #include "prerror.h"
 #include "sslerr.h"
 
@@ -27,24 +27,28 @@ static PRLogModuleInfo* gCertVerifierLog
 #endif
 
 namespace mozilla { namespace psm {
 
 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
 
 CertVerifier::CertVerifier(implementation_config ic,
+#ifndef NSS_NO_LIBPKIX
                            missing_cert_download_config mcdc,
                            crl_download_config cdc,
+#endif
                            ocsp_download_config odc,
                            ocsp_strict_config osc,
                            ocsp_get_config ogc)
   : mImplementation(ic)
+#ifndef NSS_NO_LIBPKIX
   , mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
   , mCRLDownloadEnabled(cdc == crl_download_allowed)
+#endif
   , mOCSPDownloadEnabled(odc == ocsp_on)
   , mOCSPStrict(osc == ocsp_strict)
   , mOCSPGETEnabled(ogc == ocsp_get_enabled)
 {
 }
 
 CertVerifier::~CertVerifier()
 {
@@ -104,47 +108,34 @@ ClassicVerifyCert(CERTCertificate* cert,
   if (validationChain) {
     switch(usage){
       case  certificateUsageSSLClient:
         enumUsage = certUsageSSLClient;
         break;
       case  certificateUsageSSLServer:
         enumUsage = certUsageSSLServer;
         break;
-      case certificateUsageSSLServerWithStepUp:
-        enumUsage = certUsageSSLServerWithStepUp;
-        break;
       case certificateUsageSSLCA:
         enumUsage = certUsageSSLCA;
         break;
       case certificateUsageEmailSigner:
         enumUsage = certUsageEmailSigner;
         break;
       case certificateUsageEmailRecipient:
         enumUsage = certUsageEmailRecipient;
         break;
       case certificateUsageObjectSigner:
         enumUsage = certUsageObjectSigner;
         break;
-      case certificateUsageUserCertImport:
-        enumUsage = certUsageUserCertImport;
-        break;
-      case certificateUsageVerifyCA:
-        enumUsage = certUsageVerifyCA;
-        break;
-      case certificateUsageProtectedObjectSigner:
-        enumUsage = certUsageProtectedObjectSigner;
-        break;
       case certificateUsageStatusResponder:
         enumUsage = certUsageStatusResponder;
         break;
-      case certificateUsageAnyCA:
-        enumUsage = certUsageAnyCA;
-        break;
-       default:
+      default:
+        PR_NOT_REACHED("unexpected usage");
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
   }
   if (usage == certificateUsageSSLServer) {
     // SSL server cert verification has always used CERT_VerifyCert, so we
     // continue to use it for SSL cert verification to minimize the risk of
     // there being any differnce in results between CERT_VerifyCert and
     // CERT_VerifyCertificate.
@@ -174,16 +165,191 @@ destroyCertListThatShouldNotExist(CERTCe
     // There SHOULD not be a validation chain on failure, asserion here for
     // the debug builds AND a fallback for production builds
     CERT_DestroyCertList(*certChain);
     *certChain = nullptr;
   }
 }
 #endif
 
+static SECStatus
+BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
+                             PRTime time, KeyUsages ku1, KeyUsages ku2,
+                             KeyUsages ku3, SECOidTag eku,
+                             ScopedCERTCertList& builtChain)
+{
+  PR_ASSERT(ku1);
+  PR_ASSERT(ku2);
+
+  SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                                ku1, eku, builtChain);
+  if (rv != SECSuccess && ku2 &&
+      PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+    rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                        ku2, eku, builtChain);
+    if (rv != SECSuccess && ku3 &&
+        PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                          ku3, eku, builtChain);
+      if (rv != SECSuccess) {
+        PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
+      }
+    }
+  }
+  return rv;
+}
+
+SECStatus
+CertVerifier::InsanityVerifyCert(
+                   CERTCertificate* cert,
+      /*optional*/ const SECItem* /*TODO: stapledOCSPResponse*/,
+                   const SECCertificateUsage usage,
+                   const PRTime time,
+                   void* pinArg,
+                   const Flags flags,
+  /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain)
+{
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of InsanityVerifyCert\n"));
+  SECStatus rv;
+
+  // TODO(bug 970750): anyExtendedKeyUsage
+  // TODO: encipherOnly/decipherOnly
+  // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
+  // S/MIME EKU:       http://tools.ietf.org/html/rfc3850#section-4.4.4
+
+  // TODO(bug 915931): Pass in stapled OCSP response in all calls to
+  //                   BuildCertChain.
+
+  insanity::pkix::ScopedCERTCertList builtChain;
+  switch (usage) {
+    case certificateUsageSSLClient: {
+      // XXX: We don't really have a trust bit for SSL client authentication so
+      // just use trustEmail as it is the closest alternative.
+      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
+                                       mOCSPStrict, pinArg);
+      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                          KU_DIGITAL_SIGNATURE,
+                          SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
+                          builtChain);
+      break;
+    }
+
+    case certificateUsageSSLServer: {
+      // TODO: When verifying a certificate in an SSL handshake, we should
+      // restrict the acceptable key usage based on the key exchange method
+      // chosen by the server.
+      NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
+                                       mOCSPStrict, pinArg);
+      rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
+                                        KU_DIGITAL_SIGNATURE, // ECDHE/DHE
+                                        KU_KEY_ENCIPHERMENT, // RSA
+                                        KU_KEY_AGREEMENT, // ECDH/DH
+                                        SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
+                                        builtChain);
+      break;
+    }
+
+    case certificateUsageSSLCA: {
+      NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
+                                       mOCSPStrict, pinArg);
+      rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
+                          KU_KEY_CERT_SIGN,
+                          SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
+                          builtChain);
+      break;
+    }
+
+    case certificateUsageEmailSigner: {
+      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
+                                       mOCSPStrict, pinArg);
+      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                          KU_DIGITAL_SIGNATURE,
+                          SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
+                          builtChain);
+      break;
+    }
+
+    case certificateUsageEmailRecipient: {
+      // TODO: The higher level S/MIME processing should pass in which key
+      // usage it is trying to verify for, and base its algorithm choices
+      // based on the result of the verification(s).
+      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
+                                       mOCSPStrict, pinArg);
+      rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
+                                        KU_KEY_ENCIPHERMENT, // RSA
+                                        KU_KEY_AGREEMENT, // ECDH/DH
+                                        0,
+                                        SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
+                                        builtChain);
+      break;
+    }
+
+    case certificateUsageObjectSigner: {
+      NSSCertDBTrustDomain trustDomain(trustObjectSigning,
+                                       mOCSPDownloadEnabled, mOCSPStrict,
+                                       pinArg);
+      rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
+                          KU_DIGITAL_SIGNATURE,
+                          SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
+                          builtChain);
+      break;
+    }
+
+    case certificateUsageVerifyCA:
+    case certificateUsageStatusResponder: {
+      // XXX This is a pretty useless way to verify a certificate. It is used
+      // by the implementation of window.crypto.importCertificates and in the
+      // certificate viewer UI. Because we don't know what trust bit is
+      // interesting, we just try them all.
+      insanity::pkix::EndEntityOrCA endEntityOrCA;
+      insanity::pkix::KeyUsages keyUsage;
+      SECOidTag eku;
+      if (usage == certificateUsageVerifyCA) {
+        endEntityOrCA = MustBeCA;
+        keyUsage = KU_KEY_CERT_SIGN;
+        eku = SEC_OID_UNKNOWN;
+      } else {
+        endEntityOrCA = MustBeEndEntity;
+        keyUsage = KU_DIGITAL_SIGNATURE;
+        eku = SEC_OID_OCSP_RESPONDER;
+      }
+
+      NSSCertDBTrustDomain sslTrust(trustSSL,
+                                    mOCSPDownloadEnabled, mOCSPStrict, pinArg);
+      rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
+                          keyUsage, eku, builtChain);
+      if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
+        NSSCertDBTrustDomain emailTrust(trustEmail, mOCSPDownloadEnabled,
+                                        mOCSPStrict, pinArg);
+        rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
+                            eku, builtChain);
+        if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
+          NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
+                                                  mOCSPDownloadEnabled,
+                                                  mOCSPStrict, pinArg);
+          rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
+                              keyUsage, eku, builtChain);
+        }
+      }
+
+      break;
+    }
+
+    default:
+      PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+      return SECFailure;
+  }
+
+  if (validationChain && rv == SECSuccess) {
+    *validationChain = builtChain.release();
+  }
+
+  return rv;
+}
+
 SECStatus
 CertVerifier::VerifyCert(CERTCertificate* cert,
             /*optional*/ const SECItem* stapledOCSPResponse,
                          const SECCertificateUsage usage,
                          const PRTime time,
                          void* pinArg,
                          const Flags flags,
                          /*optional out*/ ScopedCERTCertList* validationChain,
@@ -398,16 +564,21 @@ CertVerifier::VerifyCert(CERTCertificate
 #ifdef NSS_NO_LIBPKIX
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
 #else
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
 #endif
     return SECFailure;
   }
 
+  if (mImplementation == insanity) {
+    return InsanityVerifyCert(cert, stapledOCSPResponse, usage, time,
+                              pinArg, flags, validationChain);
+  }
+
   if (mImplementation == classic) {
     // XXX: we do not care about the localOnly flag (currently) as the
     // caller that wants localOnly should disable and reenable the fetching.
     return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
                              verifyLog);
   }
 
 #ifdef NSS_NO_LIBPKIX
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=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_psm__CertVerifier_h
 #define mozilla_psm__CertVerifier_h
 
-#include "certt.h"
 #include "insanity/pkixtypes.h"
 
 namespace mozilla { namespace psm {
 
 class CertVerifier
 {
 public:
   typedef unsigned int Flags;
@@ -44,36 +43,50 @@ public:
    /*optional out*/ SECOidTag* evOidPolicy = nullptr);
 
 
   enum implementation_config {
     classic = 0,
 #ifndef NSS_NO_LIBPKIX
     libpkix = 1,
 #endif
+    insanity = 2
   };
 
   enum missing_cert_download_config { missing_cert_download_off = 0, missing_cert_download_on };
   enum crl_download_config { crl_local_only = 0, crl_download_allowed };
   enum ocsp_download_config { ocsp_off = 0, ocsp_on };
   enum ocsp_strict_config { ocsp_relaxed = 0, ocsp_strict };
   enum ocsp_get_config { ocsp_get_disabled = 0, ocsp_get_enabled = 1 };
 
   bool IsOCSPDownloadEnabled() const { return mOCSPDownloadEnabled; }
 
-  CertVerifier(implementation_config ic, missing_cert_download_config ac,
-               crl_download_config cdc, ocsp_download_config odc,
-               ocsp_strict_config osc, ocsp_get_config ogc);
+  CertVerifier(implementation_config ic,
+#ifndef NSS_NO_LIBPKIX
+               missing_cert_download_config ac, crl_download_config cdc,
+#endif
+               ocsp_download_config odc, ocsp_strict_config osc,
+               ocsp_get_config ogc);
   ~CertVerifier();
 
-public:
   const implementation_config mImplementation;
+#ifndef NSS_NO_LIBPKIX
   const bool mMissingCertDownloadEnabled;
   const bool mCRLDownloadEnabled;
+#endif
   const bool mOCSPDownloadEnabled;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
+
+private:
+  SECStatus InsanityVerifyCert(CERTCertificate* cert,
+      /*optional*/ const SECItem* stapledOCSPResponse,
+      const SECCertificateUsage usage,
+      const PRTime time,
+      void* pinArg,
+      const Flags flags,
+      /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain);
 };
 
 void InitCertVerifierLog();
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -3,39 +3,129 @@
 /* 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 "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
-#include "insanity/ScopedPtr.h"
+#include "insanity/pkix.h"
 #include "certdb.h"
 #include "nss.h"
 #include "ocsp.h"
 #include "pk11pub.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "secerr.h"
 #include "secmod.h"
 
 using namespace insanity::pkix;
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gCertVerifierLog;
+#endif
+
 namespace mozilla { namespace psm {
 
 const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
 
 namespace {
 
 inline void PORT_Free_string(char* str) { PORT_Free(str); }
 
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
+} // unnamed namespace
+
+NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
+                                           bool /*ocspDownloadEnabled*/,
+                                           bool /*ocspStrict*/,
+                                           void* pinArg)
+  : mCertDBTrustType(certDBTrustType)
+//  , mOCSPDownloadEnabled(ocspDownloadEnabled)
+//  , mOCSPStrict(ocspStrict)
+  , mPinArg(pinArg)
+{
+}
+
+SECStatus
+NSSCertDBTrustDomain::FindPotentialIssuers(
+  const SECItem* encodedIssuerName, PRTime time,
+  /*out*/ insanity::pkix::ScopedCERTCertList& results)
+{
+  // TODO: normalize encodedIssuerName
+  // TODO: NSS seems to be ambiguous between "no potential issuers found" and
+  // "there was an error trying to retrieve the potential issuers."
+  results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
+                                       encodedIssuerName, time, true);
+  if (!results) {
+    return SECFailure;
+  }
+
+  return SECSuccess;
+}
+
+SECStatus
+NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+                                   const CERTCertificate* candidateCert,
+                                   /*out*/ TrustLevel* trustLevel)
+{
+  PORT_Assert(candidateCert);
+  PORT_Assert(trustLevel);
+  if (!candidateCert || !trustLevel) {
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
+    return SECFailure;
+  }
+
+  // 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;
+  if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
+    PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, mCertDBTrustType);
+
+    // 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
+    // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the
+    // relevant trust bit isn't set then that means the cert must be considered
+    // distrusted.
+    PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA ? CERTDB_TRUSTED_CA
+                                                          : CERTDB_TRUSTED;
+    if (((flags & (relevantTrustBit|CERTDB_TERMINAL_RECORD)))
+            == CERTDB_TERMINAL_RECORD) {
+      *trustLevel = ActivelyDistrusted;
+      return SECSuccess;
+    }
+
+    // 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) {
+      *trustLevel = TrustAnchor;
+      return SECSuccess;
+    }
+  }
+
+  *trustLevel = InheritsTrust;
+  return SECSuccess;
+}
+
+SECStatus
+NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
+                                       const CERTCertificate* cert)
+{
+  return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg);
+}
+
+namespace {
+
 static char*
 nss_addEscape(const char* string, char quote)
 {
   char* newString = 0;
   int escapes = 0, size = 0;
   const char* src;
   char* dest;
 
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -38,11 +38,38 @@ SetClassicOCSPBehavior(CertVerifier::ocs
                        CertVerifier::ocsp_strict_config strict,
                        CertVerifier::ocsp_get_config get);
 
 // Caller must free the result with PR_Free
 char* DefaultServerNicknameForCert(CERTCertificate* cert);
 
 void SaveIntermediateCerts(const insanity::pkix::ScopedCERTCertList& certList);
 
+class NSSCertDBTrustDomain : public insanity::pkix::TrustDomain
+{
+
+public:
+  NSSCertDBTrustDomain(SECTrustType certDBTrustType,
+                       bool ocspDownloadEnabled, bool ocspStrict,
+                       void* pinArg);
+
+  virtual SECStatus FindPotentialIssuers(
+                        const SECItem* encodedIssuerName,
+                        PRTime time,
+                /*out*/ insanity::pkix::ScopedCERTCertList& results);
+
+  virtual SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA,
+                                 const CERTCertificate* candidateCert,
+                         /*out*/ TrustLevel* trustLevel);
+
+  virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
+                                     const CERTCertificate* cert);
+
+private:
+  const SECTrustType mCertDBTrustType;
+//  const bool mOCSPDownloadEnabled;
+//  const bool mOCSPStrict;
+  void* mPinArg; // non-owning!
+};
+
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -737,16 +737,18 @@ AuthCertificate(CertVerifier& certVerifi
                 CERTCertificate* cert, SECItem* stapledOCSPResponse,
                 uint32_t providerFlags, PRTime time)
 {
   MOZ_ASSERT(infoObject);
   MOZ_ASSERT(cert);
 
   SECStatus rv;
 
+  // TODO: Remove this after we switch to insanity::pkix as the
+  // only option
   if (stapledOCSPResponse) {
     CERTCertDBHandle* handle = CERT_GetDefaultCertDB();
     rv = CERT_CacheOCSPResponseFromSideChannel(handle, cert, PR_Now(),
                                                stapledOCSPResponse,
                                                infoObject);
     if (rv != SECSuccess) {
       // Due to buggy servers that will staple expired OCSP responses
       // (see for example http://trac.nginx.org/nginx/ticket/425),
@@ -914,16 +916,22 @@ SSLServerCertVerificationJob::Run()
     Telemetry::ID failureTelemetry;
     switch (mCertVerifier->mImplementation) {
       case CertVerifier::classic:
         successTelemetry
           = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC;
         failureTelemetry
           = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_CLASSIC;
         break;
+      case CertVerifier::insanity:
+        successTelemetry
+          = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_INSANITY;
+        failureTelemetry
+          = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_INSANITY;
+        break;
 #ifndef NSS_NO_LIBPKIX
       case CertVerifier::libpkix:
         successTelemetry
           = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_LIBPKIX;
         failureTelemetry
           = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX;
         break;
 #endif
--- a/security/manager/ssl/src/SharedCertVerifier.h
+++ b/security/manager/ssl/src/SharedCertVerifier.h
@@ -10,20 +10,27 @@
 #include "mozilla/RefPtr.h"
 
 namespace mozilla { namespace psm {
 
 class SharedCertVerifier : public mozilla::psm::CertVerifier,
                            public mozilla::AtomicRefCounted<SharedCertVerifier>
 {
 public:
-  SharedCertVerifier(implementation_config ic, missing_cert_download_config ac,
-                     crl_download_config cdc, ocsp_download_config odc,
-                     ocsp_strict_config osc, ocsp_get_config ogc)
-    : mozilla::psm::CertVerifier(ic, ac, cdc, odc, osc, ogc)
+  SharedCertVerifier(implementation_config ic,
+#ifndef NSS_NO_LIBPKIX
+                     missing_cert_download_config ac, crl_download_config cdc,
+#endif
+                     ocsp_download_config odc, ocsp_strict_config osc,
+                     ocsp_get_config ogc)
+    : mozilla::psm::CertVerifier(ic,
+#ifndef NSS_NO_LIBPKIX
+                                 ac, cdc,
+#endif
+                                 odc, osc, ogc)
   {
   }
   ~SharedCertVerifier();
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__SharedCertVerifier_h
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -1733,18 +1733,17 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
   SECOidTag evOidPolicy;
   SECStatus srv;
 
   srv = certVerifier->VerifyCert(nssCert, nullptr,
                                  aUsage, PR_Now(),
                                  nullptr, // Assume no context
                                  aFlags,
                                  &resultChain,
-                                 &evOidPolicy,
-                                 nullptr);
+                                 &evOidPolicy);
 
   PRErrorCode error = PR_GetError();
 
   nsCOMPtr<nsIX509CertList> nssCertList;
   // This adopts the list
   nssCertList = new nsNSSCertList(resultChain, locker);
   NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE);
 
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -935,63 +935,71 @@ CipherSuiteChangeObserver::Observe(nsISu
 }
 
 } // anonymous namespace
 
 // Caller must hold a lock on nsNSSComponent::mutex when calling this function
 void nsNSSComponent::setValidationOptions(bool isInitialSetting,
                                           const MutexAutoLock& lock)
 {
-  bool crlDownloading = Preferences::GetBool("security.CRL_download.enabled",
-                                             false);
-
   // This preference controls whether we do OCSP fetching and does not affect
   // OCSP stapling.
   // 0 = disabled, 1 = enabled
   int32_t ocspEnabled = Preferences::GetInt("security.OCSP.enabled",
                                             OCSP_ENABLED_DEFAULT);
 
   bool ocspRequired = ocspEnabled &&
     Preferences::GetBool("security.OCSP.require", false);
 
   // We measure the setting of the pref at startup only to minimize noise by
   // addons that may muck with the settings, though it probably doesn't matter.
   if (isInitialSetting) {
     Telemetry::Accumulate(Telemetry::CERT_OCSP_ENABLED, ocspEnabled);
     Telemetry::Accumulate(Telemetry::CERT_OCSP_REQUIRED, ocspRequired);
   }
 
-  bool aiaDownloadEnabled = Preferences::GetBool("security.missing_cert_download.enabled",
-                                                 false);
+#ifndef NSS_NO_LIBPKIX
+  bool crlDownloading = Preferences::GetBool("security.CRL_download.enabled",
+                                             false);
+  bool aiaDownloadEnabled =
+    Preferences::GetBool("security.missing_cert_download.enabled", false);
 
+#endif
   bool ocspStaplingEnabled = Preferences::GetBool("security.ssl.enable_ocsp_stapling",
                                                   true);
   PublicSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled);
   PrivateSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled);
 
   CertVerifier::implementation_config certVerifierImplementation
     = CertVerifier::classic;
 
+  // The insanity::pkix pref overrides the libpkix pref
+  if (Preferences::GetBool("security.use_insanity_verification", false)) {
+    certVerifierImplementation = CertVerifier::insanity;
+  } else {
 #ifndef NSS_NO_LIBPKIX
   if (Preferences::GetBool("security.use_libpkix_verification", false)) {
     certVerifierImplementation = CertVerifier::libpkix;
   }
 #endif
+  }
 
   CertVerifier::ocsp_download_config odc;
   CertVerifier::ocsp_strict_config osc;
   CertVerifier::ocsp_get_config ogc;
 
   SetClassicOCSPBehaviorFromPrefs(&odc, &osc, &ogc, lock);
   mDefaultCertVerifier = new SharedCertVerifier(
       certVerifierImplementation,
+#ifndef NSS_NO_LIBPKIX
       aiaDownloadEnabled ?
         CertVerifier::missing_cert_download_on : CertVerifier::missing_cert_download_off,
       crlDownloading ?
         CertVerifier::crl_download_allowed : CertVerifier::crl_local_only,
+#endif
       odc, osc, ogc);
 }
 
 // Enable the TLS versions given in the prefs, defaulting to SSL 3.0 (min
 // version) and TLS 1.2 (max version) when the prefs aren't set or set to
 // invalid values.
 nsresult
 nsNSSComponent::setEnabledTLSVersions()
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4213,32 +4213,48 @@
   "SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "60000",
     "n_buckets": 50,
     "extended_statistics_ok": true,
     "description": "Time spent on a successful cert  verification in classic mode (ms)"
   },
-  "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX": {
+  "SSL_SUCCESFUL_CERT_VALIDATION_TIME_INSANITY" : {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "60000",
+    "n_buckets": 50,
+    "extended_statistics_ok": true,
+    "description": "Time spent on a successful cert verification in insanity mode (ms)"
+  },
+  "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX" : {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "60000",
     "n_buckets": 50,
     "extended_statistics_ok": true,
     "description": "Time spent on an initially failed cert verification in libpix mode (ms)"
   },
   "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_CLASSIC": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "60000",
     "n_buckets": 50,
     "extended_statistics_ok": true,
     "description": "Time spent on an initially failed cert  verification in classic mode (ms)"
   },
+  "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_INSANITY" : {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "60000",
+    "n_buckets": 50,
+    "extended_statistics_ok": true,
+    "description": "Time spent on an initially failed cert verification in insanity mode (ms)"
+  },
   "HEALTHREPORT_DB_OPEN_FIRSTRUN_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "20000",
     "n_buckets": 15,
     "description": "Time (ms) spent to open Firefox Health Report's database the first time, including schema setup."
   },
   "HEALTHREPORT_DB_OPEN_MS": {