Bug 1029247, Part 2: Parse certificates using mozilla::pkix::der, r=keeler
authorBrian Smith <brian@briansmith.org>
Thu, 03 Jul 2014 16:59:42 -0700
changeset 192738 911d02f2c02a13fbdf80083f6d00886e35523f20
parent 192737 5d696c6fe0a7d44afa8955d08fc0e7dc1d80cb3c
child 192739 d76669861f43548b1f5bd07fcd4341e31e24d906
push id27096
push usercbook@mozilla.com
push dateTue, 08 Jul 2014 12:41:54 +0000
treeherdermozilla-central@cfd7caadb22b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1029247
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1029247, Part 2: Parse certificates using mozilla::pkix::der, r=keeler
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/manager/ssl/tests/unit/test_cert_version.js
security/pkix/include/pkix/pkix.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/pkixcheck.h
security/pkix/lib/pkixder.cpp
security/pkix/lib/pkixder.h
security/pkix/lib/pkixkey.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/pkix_cert_chain_length_tests.cpp
security/pkix/test/gtest/pkix_cert_extension_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckTimes_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
security/pkix/test/gtest/pkixder_universal_types_tests.cpp
security/pkix/test/lib/pkixtestutil.cpp
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -537,17 +537,17 @@ VerifyCertificate(CERTCertificate* signe
   }
   const VerifyCertificateContext& context =
     *reinterpret_cast<const VerifyCertificateContext*>(voidContext);
 
   AppTrustDomain trustDomain(pinArg);
   if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
-  if (BuildCertChain(trustDomain, signerCert, PR_Now(),
+  if (BuildCertChain(trustDomain, signerCert->derCert, PR_Now(),
                      EndEntityOrCA::MustBeEndEntity,
                      KeyUsage::digitalSignature,
                      KeyPurposeId::id_kp_codeSigning,
                      CertPolicyId::anyPolicy,
                      nullptr, context.builtChain) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -157,17 +157,17 @@ AppTrustDomain::GetCertTrust(EndEntityOr
     return SECSuccess;
   }
 
   *trustLevel = TrustLevel::InheritsTrust;
   return SECSuccess;
 }
 
 SECStatus
-AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
+AppTrustDomain::VerifySignedData(const CERTSignedData& signedData,
                                  const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                            mPinArg);
 }
 
 SECStatus
 AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, PRTime time,
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -23,17 +23,17 @@ public:
   SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                          const mozilla::pkix::CertPolicyId& policy,
                          const SECItem& candidateCertDER,
                  /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
   SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                  PRTime time,
                          /*out*/ mozilla::pkix::ScopedCERTCertList& results)
                                  MOZ_OVERRIDE;
-  SECStatus VerifySignedData(const CERTSignedData* signedData,
+  SECStatus VerifySignedData(const CERTSignedData& signedData,
                              const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
   SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                             const mozilla::pkix::CertID& certID, PRTime time,
                             /*optional*/ const SECItem* stapledOCSPresponse,
                             /*optional*/ const SECItem* aiaExtension);
   SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
 
 private:
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -159,27 +159,27 @@ SECStatus chainValidationCallback(void* 
 static SECStatus
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
                              PRTime time, KeyUsage ku1, KeyUsage ku2,
                              KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
                              const SECItem* stapledOCSPResponse,
                              ScopedCERTCertList& builtChain)
 {
-  SECStatus rv = BuildCertChain(trustDomain, cert, time,
+  SECStatus rv = BuildCertChain(trustDomain, cert->derCert, time,
                                 EndEntityOrCA::MustBeEndEntity, ku1,
                                 eku, requiredPolicy,
                                 stapledOCSPResponse, builtChain);
   if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
-    rv = BuildCertChain(trustDomain, cert, time,
+    rv = BuildCertChain(trustDomain, cert->derCert, time,
                         EndEntityOrCA::MustBeEndEntity, ku2,
                         eku, requiredPolicy,
                         stapledOCSPResponse, builtChain);
     if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
-      rv = BuildCertChain(trustDomain, cert, time,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity, ku3,
                           eku, requiredPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv != SECSuccess) {
         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
       }
     }
   }
@@ -232,17 +232,17 @@ CertVerifier::VerifyCert(CERTCertificate
 
   mozilla::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
-      rv = BuildCertChain(trustDomain, cert, time,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse,
                           builtChain);
       break;
     }
 
@@ -298,63 +298,64 @@ CertVerifier::VerifyCert(CERTCertificate
                                         CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
-      rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
+                          EndEntityOrCA::MustBeCA,
                           KeyUsage::keyCertSign,
                           KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
-      rv = BuildCertChain(trustDomain, cert, time,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, 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, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
-      rv = BuildCertChain(trustDomain, cert, time,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
-        rv = BuildCertChain(trustDomain, cert, time,
+        rv = BuildCertChain(trustDomain, cert->derCert, time,
                             EndEntityOrCA::MustBeEndEntity,
                             KeyUsage::keyAgreement, // ECDH/DH
                             KeyPurposeId::id_kp_emailProtection,
                             CertPolicyId::anyPolicy,
                             stapledOCSPResponse, builtChain);
       }
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        mOCSPCache, pinArg, ocspGETConfig);
-      rv = BuildCertChain(trustDomain, cert, time,
+      rv = BuildCertChain(trustDomain, cert->derCert, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
@@ -374,32 +375,33 @@ CertVerifier::VerifyCert(CERTCertificate
       } else {
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
                                     pinArg, ocspGETConfig);
-      rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
+      rv = BuildCertChain(sslTrust, cert->derCert, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
                                         pinArg, ocspGETConfig);
-        rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
-                            eku, CertPolicyId::anyPolicy,
+        rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA,
+                            keyUsage, eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse, builtChain);
         if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
                                                   pinArg, ocspGETConfig);
-          rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
-                              keyUsage, eku, CertPolicyId::anyPolicy,
-                              stapledOCSPResponse, builtChain);
+          rv = BuildCertChain(objectSigningTrust, cert->derCert, time,
+                              endEntityOrCA, keyUsage, eku,
+                              CertPolicyId::anyPolicy, stapledOCSPResponse,
+                              builtChain);
         }
       }
 
       break;
     }
 
     default:
       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -142,17 +142,17 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
     }
   }
 
   *trustLevel = TrustLevel::InheritsTrust;
   return SECSuccess;
 }
 
 SECStatus
-NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
+NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData& signedData,
                                        const SECItem& subjectPublicKeyInfo)
 {
   return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                            mPinArg);
 }
 
 static PRIntervalTime
 OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -58,17 +58,17 @@ public:
                         PRTime time,
                 /*out*/ mozilla::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                  const mozilla::pkix::CertPolicyId& policy,
                                  const SECItem& candidateCertDER,
                          /*out*/ mozilla::pkix::TrustLevel* trustLevel);
 
-  virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
+  virtual SECStatus VerifySignedData(const CERTSignedData& signedData,
                                      const SECItem& subjectPublicKeyInfo);
 
   virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                     const mozilla::pkix::CertID& certID,
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPResponse,
                        /*optional*/ const SECItem* aiaExtension);
 
--- a/security/manager/ssl/tests/unit/test_cert_version.js
+++ b/security/manager/ssl/tests/unit/test_cert_version.js
@@ -48,19 +48,19 @@ function run_test() {
   load_cert("v1_ca", "CTu,CTu,CTu");
   load_cert("v1_ca_bc", "CTu,CTu,CTu");
   load_cert("v2_ca", "CTu,CTu,CTu");
   load_cert("v2_ca_bc", "CTu,CTu,CTu");
   load_cert("v3_ca", "CTu,CTu,CTu");
   load_cert("v3_ca_missing_bc", "CTu,CTu,CTu");
 
   check_ok_ca(cert_from_file('v1_ca.der'));
-  check_ca_err(cert_from_file('v1_ca_bc.der'), SEC_ERROR_EXTENSION_VALUE_INVALID);
+  check_ca_err(cert_from_file('v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_ca_err(cert_from_file('v2_ca.der'), SEC_ERROR_CA_CERT_INVALID);
-  check_ca_err(cert_from_file('v2_ca_bc.der'), SEC_ERROR_EXTENSION_VALUE_INVALID);
+  check_ca_err(cert_from_file('v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_ok_ca(cert_from_file('v3_ca.der'));
   check_ca_err(cert_from_file('v3_ca_missing_bc.der'), SEC_ERROR_CA_CERT_INVALID);
 
   // Classic allows v1 and v2 certs to be CA certs in trust anchor positions and
   // intermediates when they have a v3 basic constraints extenstion (which
   // makes them invalid certs). Insanity only allows v1 certs to be CA in
   // anchor position (even if they have invalid encodings), v2 certs are not
   // considered CAs in any position.
@@ -77,469 +77,417 @@ function run_test() {
   // v1 intermediate with v1 trust anchor
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v1_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v1_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
 
   // v1 intermediate with v3 extensions. CA is invalid.
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v1_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
 
   // A v2 intermediate with a v1 CA
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v1_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v1_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
 
-  // A v2 intermediate with basic constraints (not allowed in insanity)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v1_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
+  // A v2 intermediate with basic constraints (not allowed in mozilla::pkix)
+  check_ca_err(cert_from_file('v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
 
   // Section is OK. A x509 v3 CA MUST have bc
   // http://tools.ietf.org/html/rfc5280#section-4.2.1.9
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v1_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
 
   // It is valid for a v1 ca to sign a v3 intemediate.
   check_ok_ca(cert_from_file('v3_int-v1_ca.der'));
   check_ok(cert_from_file('v1_ee-v3_int-v1_ca.der'));
   check_ok(cert_from_file('v2_ee-v3_int-v1_ca.der'));
   check_ok(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca.der'));
   check_ok(cert_from_file('v3_bc_ee-v3_int-v1_ca.der'));
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
 
   // The next groups change the v1 ca for a v1 ca with base constraints
   // (invalid trust anchor). The error pattern is the same as the groups
   // above
 
   // Using A v1 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v1_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // Using a v1 intermediate with v3 extenstions (invalid).
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v1_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // Using v2 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v1_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // Using a v2 intermediate with basic constraints (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v1_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // Using a v3 intermediate that is missing basic constraints (invalid)
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v1_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // these should pass assuming we are OK with v1 ca signing v3 intermediates
   ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
   ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v3_int-v1_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v3_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v3_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
 
 
   //////////////
   // v2 CA supersection
   //////////////////
 
   // v2 ca, v1 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v2_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v2_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca.der'), ee_error)
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v1 intermediate with basic constraints (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v2_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v2 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v2_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v2_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v2 intermediate with basic constraints (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v2_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
+  check_ca_err(cert_from_file('v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v3 intermediate missing basic constraints
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v2_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v3 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int-v2_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca.der'), ee_error);
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v1 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v2_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v1 intermediate with bc (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v2_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v2 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v2_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, v2 intermediate with bc (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v2_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, invalid v3 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v2_ca_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error)
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 ca, valid v3 intermediate (is OK if we use 'classic' semantics)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v3_int-v2_ca_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v3_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
 
   //////////////
   // v3 CA supersection
   //////////////////
 
   // v3 ca, v1 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v3_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v3_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // A v1 intermediate with v3 extensions
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v3_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // reject a v2 cert as intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v3_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v3_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // v2 intermediate with bc (invalid)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v3_ca.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
+  check_ca_err(cert_from_file('v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // invalid v3 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v3_ca.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // I dont think that v3 intermediates should be allowed to sign v1 or v2
   // certs, but other thanthat this  is what we usually get in the wild.
   check_ok_ca(cert_from_file('v3_int-v3_ca.der'));
   check_ok(cert_from_file('v1_ee-v3_int-v3_ca.der'));
   check_ok(cert_from_file('v2_ee-v3_int-v3_ca.der'));
   check_ok(cert_from_file('v3_missing_bc_ee-v3_int-v3_ca.der'));
   check_ok(cert_from_file('v3_bc_ee-v3_int-v3_ca.der'));
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
 
   // v3 CA, invalid v3 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v1_int-v3_ca_missing_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 
   // Int v1 with BC that is just invalid (classic fail insanity OK)
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v1_int_bc-v3_ca_missing_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 
   // Good section (all fail)
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v2_int-v3_ca_missing_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 
   // v2 intermediate (even with basic constraints) is invalid
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_ca_err(cert_from_file('v2_int_bc-v3_ca_missing_bc.der'), ca_error);
-  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
+  check_ca_err(cert_from_file('v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 
   // v3 intermediate missing basic constraints is invalid
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int_missing_bc-v3_ca_missing_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 
   // With a v3 root missing bc and valid v3 intermediate
   ca_error = SEC_ERROR_CA_CERT_INVALID;
   ee_error = SEC_ERROR_CA_CERT_INVALID;
   check_ca_err(cert_from_file('v3_int-v3_ca_missing_bc.der'), ca_error);
   check_cert_err(cert_from_file('v1_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v2_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
   check_cert_err(cert_from_file('v3_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
-  ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
-  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
-  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
+  check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
+  check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
   check_cert_err(cert_from_file('v4_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
 }
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -84,28 +84,26 @@ namespace mozilla { namespace pkix {
 // 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.
 // TODO(bug 968451): Document more of these.
 
-SECStatus BuildCertChain(TrustDomain& trustDomain,
-                         const CERTCertificate* cert,
-                         PRTime time,
-                         EndEntityOrCA endEntityOrCA,
+SECStatus BuildCertChain(TrustDomain& trustDomain, const SECItem& cert,
+                         PRTime time, EndEntityOrCA endEntityOrCA,
                          KeyUsage requiredKeyUsageIfPresent,
                          KeyPurposeId requiredEKUIfPresent,
                          const CertPolicyId& requiredPolicy,
             /*optional*/ const SECItem* stapledOCSPResponse,
                  /*out*/ ScopedCERTCertList& results);
 
 // Verify the given signed data using the given public key.
-SECStatus VerifySignedData(const CERTSignedData* sd,
+SECStatus VerifySignedData(const CERTSignedData& sd,
                            const SECItem& subjectPublicKeyInfo,
                            void* pkcs11PinArg);
 
 // The return value, if non-null, is owned by the arena and MUST NOT be freed.
 SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CertID& certID);
 
 // The out parameter expired will be true if the response has expired. If the
 // response also indicates a revoked or unknown certificate, that error
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -153,17 +153,17 @@ public:
   virtual SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                          PRTime time,
                                  /*out*/ ScopedCERTCertList& results) = 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.
-  virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
+  virtual SECStatus VerifySignedData(const CERTSignedData& signedData,
                                      const SECItem& subjectPublicKeyInfo) = 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;
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -22,179 +22,56 @@
  * limitations under the License.
  */
 
 #include "pkix/pkix.h"
 
 #include <limits>
 
 #include "pkixcheck.h"
-#include "pkixder.h"
 
 namespace mozilla { namespace pkix {
 
-// We assume ext has been zero-initialized by its constructor and otherwise
-// not modified.
-//
-// TODO(perf): This sorting of extensions should be be moved into the
-// certificate decoder so that the results are cached with the certificate, so
-// that the decoding doesn't have to happen more than once per cert.
-Result
-BackCert::Init(const SECItem& certDER)
-{
-  // XXX: Currently-known uses of mozilla::pkix create CERTCertificate objects
-  // for all certs anyway, so the overhead of CERT_NewTempCertificate will be
-  // reduced to a lookup in NSS's SECItem* -> CERTCertificate cache and
-  // a CERT_DupCertificate. Eventually, we should parse the certificate using
-  // mozilla::pkix::der and avoid the need to create a CERTCertificate at all.
-  nssCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
-                                    const_cast<SECItem*>(&certDER),
-                                    nullptr, false, true);
-  if (!nssCert) {
-    return MapSECStatus(SECFailure);
-  }
-
-  if (nssCert->version.len == 1 &&
-      nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v3)) {
-    version = der::Version::v3;
-  } else if (nssCert->version.len == 1 &&
-             nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v2)) {
-    version = der::Version::v2;
-  } else if (nssCert->version.len == 1 &&
-             nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v2)) {
-    // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
-    // do here for compatibility reasons.
-    version = der::Version::v1;
-  } else if (nssCert->version.len == 0) {
-    version = der::Version::v1;
-  } else {
-    // Explicit encoding of v1 is not allowed. We do not support any other
-    // version except v3.
-    return Fail(RecoverableError, SEC_ERROR_BAD_DER);
-  }
-
-  const CERTCertExtension* const* exts = nssCert->extensions;
-  if (!exts) {
-    return Success;
-  }
-
-  // Extensions are only allowed in v3 certificates, not v1 or v2. Also, we
-  // use presence of the basic constraints extension with isCA==true to decide
-  // whether to treat a certificate as a CA certificate, and we don't want to
-  // allow v1 or v2 intermediate CA certificates; this check is part of that
-  // enforcement as well.
-  if (version != der::Version::v3) {
-    return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
-  }
-
-  const SECItem* dummyEncodedSubjectKeyIdentifier = nullptr;
-  const SECItem* dummyEncodedAuthorityKeyIdentifier = nullptr;
-  const SECItem* dummyEncodedSubjectAltName = nullptr;
-
-  for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) {
-    const SECItem** out = nullptr;
-
-    // python DottedOIDToCode.py id-ce 2.5.29
-    static const uint8_t id_ce[] = {
-      0x55, 0x1d
-    };
-
-    // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
-    static const uint8_t id_pe_authorityInfoAccess[] = {
-      0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
-    };
-
-    if (ext->id.len == PR_ARRAY_SIZE(id_ce) + 1 &&
-        !memcmp(ext->id.data, id_ce, PR_ARRAY_SIZE(id_ce))) {
-      switch (ext->id.data[ext->id.len - 1]) {
-        case 14: out = &dummyEncodedSubjectKeyIdentifier; break; // bug 965136
-        case 15: out = &encodedKeyUsage; break;
-        case 17: out = &dummyEncodedSubjectAltName; break; // bug 970542
-        case 19: out = &encodedBasicConstraints; break;
-        case 30: out = &encodedNameConstraints; break;
-        case 32: out = &encodedCertificatePolicies; break;
-        case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136
-        case 37: out = &encodedExtendedKeyUsage; break;
-        case 54: out = &encodedInhibitAnyPolicy; break; // Bug 989051
-      }
-    } else if (ext->id.len == PR_ARRAY_SIZE(id_pe_authorityInfoAccess) &&
-               !memcmp(ext->id.data, id_pe_authorityInfoAccess,
-                       PR_ARRAY_SIZE(id_pe_authorityInfoAccess))) {
-      // We should remember the value of the encoded AIA extension here, but
-      // since our TrustDomain implementations get the OCSP URI using
-      // CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
-      out = &encodedAuthorityInfoAccess;
-    }
-
-    // If this is an extension we don't understand and it's marked critical,
-    // we must reject this certificate.
-    // (The only valid explicit value of the critical flag is TRUE because
-    // it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.)
-    if (!out && ext->critical.data && ext->critical.len > 0) {
-      return Fail(RecoverableError, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
-    }
-
-    if (out) {
-      // This is an extension we understand. Save it in results unless we've
-      // already found the extension previously.
-      if (*out) {
-        // Duplicate extension
-        return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
-      }
-      *out = &ext->value;
-    }
-  }
-
-  return Success;
-}
-
-Result
-BackCert::VerifyOwnSignatureWithKey(TrustDomain& trustDomain,
-                                    const SECItem& subjectPublicKeyInfo) const
-{
-  return MapSECStatus(trustDomain.VerifySignedData(&nssCert->signatureWrap,
-                                                   subjectPublicKeyInfo));
-}
-
 static Result BuildForward(TrustDomain& trustDomain,
-                           BackCert& subject,
+                           const BackCert& subject,
                            PRTime time,
                            EndEntityOrCA endEntityOrCA,
                            KeyUsage requiredKeyUsageIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
                            const CertPolicyId& requiredPolicy,
                            /*optional*/ const SECItem* stapledOCSPResponse,
                            unsigned int subCACount,
                            /*out*/ ScopedCERTCertList& results);
 
 // The code that executes in the inner loop of BuildForward
 static Result
 BuildForwardInner(TrustDomain& trustDomain,
-                  BackCert& subject,
+                  const BackCert& subject,
                   PRTime time,
                   KeyPurposeId requiredEKUIfPresent,
                   const CertPolicyId& requiredPolicy,
                   const SECItem& potentialIssuerDER,
                   unsigned int subCACount,
                   /*out*/ ScopedCERTCertList& results)
 {
-  BackCert potentialIssuer(&subject, BackCert::IncludeCN::No);
-  Result rv = potentialIssuer.Init(potentialIssuerDER);
+  BackCert potentialIssuer(potentialIssuerDER, &subject,
+                           BackCert::IncludeCN::No);
+  Result rv = potentialIssuer.Init();
   if (rv != Success) {
     return rv;
   }
 
   // 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 (BackCert* prev = potentialIssuer.childCert;
+  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())) {
       return Fail(RecoverableError, SEC_ERROR_UNKNOWN_ISSUER); // XXX: error code
     }
   }
@@ -209,29 +86,35 @@ BuildForwardInner(TrustDomain& trustDoma
   // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
   rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA,
                     KeyUsage::keyCertSign, requiredEKUIfPresent,
                     requiredPolicy, nullptr, subCACount, results);
   if (rv != Success) {
     return rv;
   }
 
-  return subject.VerifyOwnSignatureWithKey(
-                   trustDomain, potentialIssuer.GetSubjectPublicKeyInfo());
+  SECStatus srv = trustDomain.VerifySignedData(
+                                subject.GetSignedData(),
+                                potentialIssuer.GetSubjectPublicKeyInfo());
+  if (srv != SECSuccess) {
+    return MapSECStatus(srv);
+  }
+
+  return Success;
 }
 
 // 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.
 static Result
 BuildForward(TrustDomain& trustDomain,
-             BackCert& subject,
+             const BackCert& subject,
              PRTime time,
              EndEntityOrCA endEntityOrCA,
              KeyUsage requiredKeyUsageIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
              /*optional*/ const SECItem* stapledOCSPResponse,
              unsigned int subCACount,
              /*out*/ ScopedCERTCertList& results)
@@ -260,23 +143,25 @@ BuildForward(TrustDomain& trustDomain,
   if (trustLevel == TrustLevel::TrustAnchor) {
     // End of the recursion.
 
     // Construct the results cert chain.
     results = CERT_NewCertList();
     if (!results) {
       return MapSECStatus(SECFailure);
     }
-    for (BackCert* cert = &subject; cert; cert = cert->childCert) {
-      CERTCertificate* dup = CERT_DupCertificate(cert->GetNSSCert());
-      if (CERT_AddCertToListHead(results.get(), dup) != SECSuccess) {
-        CERT_DestroyCertificate(dup);
+    for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
+      ScopedCERTCertificate
+        nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+                                        const_cast<SECItem*>(&cert->GetDER()),
+                                        nullptr, false, true));
+      if (CERT_AddCertToListHead(results.get(), nssCert.get()) != SECSuccess) {
         return MapSECStatus(SECFailure);
       }
-      // dup is now owned by results.
+      nssCert.release(); // nssCert is now owned by results.
     }
 
     // This must be done here, after the chain is built but before any
     // revocation checks have been done.
     SECStatus srv = trustDomain.IsChainValid(results.get());
     if (srv != SECSuccess) {
       return MapSECStatus(srv);
     }
@@ -321,17 +206,17 @@ BuildForward(TrustDomain& trustDomain,
         return Fail(FatalError, deferredEndEntityError);
       }
 
       CertID certID(subject.GetIssuer(), n->cert->derPublicKey,
                     subject.GetSerialNumber());
       SECStatus srv = trustDomain.CheckRevocation(
                                     endEntityOrCA, certID, time,
                                     stapledOCSPResponse,
-                                    subject.encodedAuthorityInfoAccess);
+                                    subject.GetAuthorityInfoAccess());
       if (srv != SECSuccess) {
         return MapSECStatus(SECFailure);
       }
 
       // We found a trusted issuer. At this point, we know the cert is valid
       // and results contains the complete cert chain.
       return Success;
     }
@@ -360,42 +245,34 @@ BuildForward(TrustDomain& trustDomain,
   if (errorToReturn == 0) {
     errorToReturn = SEC_ERROR_UNKNOWN_ISSUER;
   }
 
   return Fail(RecoverableError, errorToReturn);
 }
 
 SECStatus
-BuildCertChain(TrustDomain& trustDomain,
-               const CERTCertificate* nssCert,
-               PRTime time,
-               EndEntityOrCA endEntityOrCA,
+BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER,
+               PRTime time, EndEntityOrCA endEntityOrCA,
                KeyUsage requiredKeyUsageIfPresent,
                KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
                /*optional*/ const SECItem* stapledOCSPResponse,
                /*out*/ ScopedCERTCertList& results)
 {
-  if (!nssCert) {
-    PR_NOT_REACHED("null cert passed to BuildCertChain");
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return SECFailure;
-  }
-
   // XXX: Support the legacy use of the subject CN field for indicating the
   // domain name the certificate is valid for.
   BackCert::IncludeCN includeCN
     = endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
       requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth
     ? BackCert::IncludeCN::Yes
     : BackCert::IncludeCN::No;
 
-  BackCert cert(nullptr, includeCN);
-  Result rv = cert.Init(nssCert->derCert);
+  BackCert cert(certDER, nullptr, includeCN);
+  Result rv = cert.Init();
   if (rv != Success) {
     return SECFailure;
   }
 
   rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
                     requiredKeyUsageIfPresent, requiredEKUIfPresent,
                     requiredPolicy, stapledOCSPResponse, 0, results);
   if (rv != Success) {
new file mode 100644
--- /dev/null
+++ b/security/pkix/lib/pkixcert.cpp
@@ -0,0 +1,252 @@
+/* -*- 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 "pkix/bind.h"
+#include "pkixutil.h"
+
+namespace mozilla { namespace pkix {
+
+Result
+BackCert::Init()
+{
+  // Certificate  ::=  SEQUENCE  {
+  //         tbsCertificate       TBSCertificate,
+  //         signatureAlgorithm   AlgorithmIdentifier,
+  //         signatureValue       BIT STRING  }
+
+  der::Input tbsCertificate;
+
+  // The scope of |input| and |certificate| are limited to this block so we
+  // don't accidentally confuse them for tbsCertificate later.
+  {
+    der::Input input;
+    if (input.Init(der.data, der.len) != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+    der::Input certificate;
+    if (der::ExpectTagAndGetValue(input, der::SEQUENCE, certificate)
+          != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+    if (der::End(input) != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+    if (der::SignedData(certificate, tbsCertificate, signedData)
+          != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+    if (der::End(certificate) != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+  }
+
+  // TBSCertificate  ::=  SEQUENCE  {
+  //      version         [0]  EXPLICIT Version DEFAULT v1,
+  //      serialNumber         CertificateSerialNumber,
+  //      signature            AlgorithmIdentifier,
+  //      issuer               Name,
+  //      validity             Validity,
+  //      subject              Name,
+  //      subjectPublicKeyInfo SubjectPublicKeyInfo,
+  //      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+  //                           -- If present, version MUST be v2 or v3
+  //      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+  //                           -- If present, version MUST be v2 or v3
+  //      extensions      [3]  EXPLICIT Extensions OPTIONAL
+  //                           -- If present, version MUST be v3
+  //      }
+  if (der::OptionalVersion(tbsCertificate, version) != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  if (der::CertificateSerialNumber(tbsCertificate, serialNumber)
+        != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  // XXX: Ignored. What are we supposed to check? This seems totally redundant
+  // with Certificate.signatureAlgorithm. Is it important to check that they
+  // are consistent with each other? It doesn't seem to matter!
+  SECAlgorithmID signature;
+  if (der::AlgorithmIdentifier(tbsCertificate, signature) != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer)
+        != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  if (der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity)
+        != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
+  // that the name is syntactically valid, if they care. In Gecko we do this
+  // implicitly by parsing the certificate into a CERTCertificate object.
+  // Instead of relying on the caller to do this, we should do it ourselves.
+  if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject)
+        != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+  // TODO(bug XXXXXXX): We defer parsing/validating subjectPublicKeyInfo to
+  // the point where the public key is needed. For end-entity certificates, we
+  // assume that the caller will extract the public key and use it somehow; if
+  // they don't do that then we'll never know whether the key is invalid. On
+  // the other hand, if the caller never uses the key then in some ways it
+  // doesn't matter. Regardless, we should parse and validate
+  // subjectPublicKeyKeyInfo internally.
+  if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
+                              subjectPublicKeyInfo) != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+
+  static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
+
+  // RFC 5280 says: "These fields MUST only appear if the version is 2 or 3
+  // (Section 4.1.2.1). These fields MUST NOT appear if the version is 1."
+  if (version != der::Version::v1) {
+
+    // Ignore issuerUniqueID if present.
+    if (tbsCertificate.Peek(CSC | 1)) {
+      if (der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1) != der::Success) {
+        return MapSECStatus(SECFailure);
+      }
+    }
+
+    // Ignore subjectUniqueID if present.
+    if (tbsCertificate.Peek(CSC | 2)) {
+      if (der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2) != der::Success) {
+        return MapSECStatus(SECFailure);
+      }
+    }
+  }
+
+  // Extensions were added in v3, so only accept extensions in v3 certificates.
+  if (version == der::Version::v3) {
+    if (der::OptionalExtensions(tbsCertificate, CSC | 3,
+                                bind(&BackCert::RememberExtension, this, _1, _2,
+                                     _3)) != der::Success) {
+      return MapSECStatus(SECFailure);
+    }
+  }
+
+  if (der::End(tbsCertificate) != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+
+  return Success;
+}
+
+der::Result
+BackCert::RememberExtension(der::Input& extnID, const SECItem& extnValue,
+                            /*out*/ bool& understood)
+{
+  understood = false;
+
+  // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
+  static const uint8_t id_ce_keyUsage[] = {
+    0x55, 0x1d, 0x0f
+  };
+  // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
+  static const uint8_t id_ce_subjectAltName[] = {
+    0x55, 0x1d, 0x11
+  };
+  // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
+  static const uint8_t id_ce_basicConstraints[] = {
+    0x55, 0x1d, 0x13
+  };
+  // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
+  static const uint8_t id_ce_nameConstraints[] = {
+    0x55, 0x1d, 0x1e
+  };
+  // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
+  static const uint8_t id_ce_certificatePolicies[] = {
+    0x55, 0x1d, 0x20
+  };
+  // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
+  static const uint8_t id_ce_policyConstraints[] = {
+    0x55, 0x1d, 0x24
+  };
+  // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
+  static const uint8_t id_ce_extKeyUsage[] = {
+    0x55, 0x1d, 0x25
+  };
+  // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
+  static const uint8_t id_ce_inhibitAnyPolicy[] = {
+    0x55, 0x1d, 0x36
+  };
+  // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
+  static const uint8_t id_pe_authorityInfoAccess[] = {
+    0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
+  };
+
+  SECItem* out = nullptr;
+
+  // We already enforce the maximum possible constraints for policies so we
+  // can safely ignore even critical policy constraint extensions.
+  //
+  // XXX: Doing it this way won't allow us to detect duplicate
+  // policyConstraints extensions, but that's OK because (and only because) we
+  // ignore the extension.
+  SECItem dummyPolicyConstraints = { siBuffer, nullptr, 0 };
+
+  // RFC says "Conforming CAs MUST mark this extension as non-critical" for
+  // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
+  // them for anything, so we totally ignore them here.
+
+  if (extnID.MatchRest(id_ce_keyUsage)) {
+    out = &keyUsage;
+  } else if (extnID.MatchRest(id_ce_subjectAltName)) {
+    out = &subjectAltName;
+  } else if (extnID.MatchRest(id_ce_basicConstraints)) {
+    out = &basicConstraints;
+  } else if (extnID.MatchRest(id_ce_nameConstraints)) {
+    out = &nameConstraints;
+  } else if (extnID.MatchRest(id_ce_certificatePolicies)) {
+    out = &certificatePolicies;
+  } else if (extnID.MatchRest(id_ce_policyConstraints)) {
+    out = &dummyPolicyConstraints;
+  } else if (extnID.MatchRest(id_ce_extKeyUsage)) {
+    out = &extKeyUsage;
+  } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
+    out = &inhibitAnyPolicy;
+  } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
+    out = &authorityInfoAccess;
+  }
+
+  if (out) {
+    // Don't allow an empty value for any extension we understand. This way, we
+    // can test out->len to check for duplicates.
+    if (extnValue.len == 0) {
+      return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+    }
+    if (out->len != 0) {
+      // Duplicate extension
+      return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
+    }
+    *out = extnValue;
+    understood = true;
+  }
+
+  return der::Success;
+}
+
+} } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -28,46 +28,43 @@
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
 #include "pkixder.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 Result
-CheckTimes(const CERTValidity& validity, PRTime time)
+CheckValidity(const SECItem& encodedValidity, PRTime time)
 {
-  der::Input notBeforeInput;
-  if (notBeforeInput.Init(validity.notBefore.data, validity.notBefore.len)
+  der::Input validity;
+  if (validity.Init(encodedValidity.data, encodedValidity.len)
         != der::Success) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
   PRTime notBefore;
-  if (der::TimeChoice(validity.notBefore.type, notBeforeInput, notBefore)
-        != der::Success) {
+  if (der::TimeChoice(validity, notBefore) != der::Success) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
   if (time < notBefore) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
 
-  der::Input notAfterInput;
-  if (notAfterInput.Init(validity.notAfter.data, validity.notAfter.len)
-        != der::Success) {
-    return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
-  }
   PRTime notAfter;
-  if (der::TimeChoice(validity.notAfter.type, notAfterInput, notAfter)
-        != der::Success) {
+  if (der::TimeChoice(validity, notAfter) != der::Success) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
   if (time > notAfter) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
 
+  if (der::End(validity) != der::Success) {
+    return MapSECStatus(SECFailure);
+  }
+
   return Success;
 }
 
 // 4.2.1.3. Key Usage (id-ce-keyUsage)
 
 // As explained in the comment in CheckKeyUsage, bit 0 is the most significant
 // bit and bit 7 is the least significant bit.
 inline uint8_t KeyUsageToBitMask(KeyUsage keyUsage)
@@ -403,28 +400,28 @@ CheckBasicConstraints(EndEntityOrCA endE
 
   return Success;
 }
 
 // 4.2.1.10. Name Constraints
 Result
 CheckNameConstraints(const BackCert& cert)
 {
-  if (!cert.encodedNameConstraints) {
+  if (!cert.GetNameConstraints()) {
     return Success;
   }
 
   ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
     return MapSECStatus(SECFailure);
   }
 
   // Owned by arena
   const CERTNameConstraints* constraints =
-    CERT_DecodeNameConstraintsExtension(arena.get(), cert.encodedNameConstraints);
+    CERT_DecodeNameConstraintsExtension(arena.get(), cert.GetNameConstraints());
   if (!constraints) {
     return MapSECStatus(SECFailure);
   }
 
   for (const BackCert* child = cert.childCert; child;
        child = child->childCert) {
     ScopedCERTCertificate
       nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
@@ -614,17 +611,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
     }
   }
 
   return Success;
 }
 
 Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
-                                 BackCert& cert,
+                                 const BackCert& cert,
                                  PRTime time,
                                  EndEntityOrCA endEntityOrCA,
                                  KeyUsage requiredKeyUsageIfPresent,
                                  KeyPurposeId requiredEKUIfPresent,
                                  const CertPolicyId& requiredPolicy,
                                  unsigned int subCACount,
                 /*optional out*/ TrustLevel* trustLevelOut)
 {
@@ -649,25 +646,25 @@ CheckIssuerIndependentProperties(TrustDo
     *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).
 
   // 4.2.1.3. Key Usage
-  rv = CheckKeyUsage(endEntityOrCA, cert.encodedKeyUsage,
+  rv = CheckKeyUsage(endEntityOrCA, cert.GetKeyUsage(),
                      requiredKeyUsageIfPresent);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.4. Certificate Policies
-  rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies,
-                                cert.encodedInhibitAnyPolicy, trustLevel,
+  rv = CheckCertificatePolicies(endEntityOrCA, cert.GetCertificatePolicies(),
+                                cert.GetInhibitAnyPolicy(), trustLevel,
                                 requiredPolicy);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.5. Policy Mappings are not supported; see the documentation about
   //          policy enforcement in pkix.h.
 
@@ -675,44 +672,44 @@ CheckIssuerIndependentProperties(TrustDo
   //          checking and during name verification (CERT_VerifyCertName).
 
   // 4.2.1.7. Issuer Alternative Name is not something that needs checking.
 
   // 4.2.1.8. Subject Directory Attributes is not something that needs
   //          checking.
 
   // 4.2.1.9. Basic Constraints.
-  rv = CheckBasicConstraints(endEntityOrCA, cert.encodedBasicConstraints,
-                             cert.version, trustLevel, subCACount);
+  rv = CheckBasicConstraints(endEntityOrCA, cert.GetBasicConstraints(),
+                             cert.GetVersion(), trustLevel, subCACount);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.10. Name Constraints is dealt with in during path building.
 
   // 4.2.1.11. Policy Constraints are implicitly supported; see the
   //           documentation about policy enforcement in pkix.h.
 
   // 4.2.1.12. Extended Key Usage
-  rv = CheckExtendedKeyUsage(endEntityOrCA, cert.encodedExtendedKeyUsage,
+  rv = CheckExtendedKeyUsage(endEntityOrCA, cert.GetExtKeyUsage(),
                              requiredEKUIfPresent);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.13. CRL Distribution Points is not supported, though the
   //           TrustDomain's CheckRevocation method may parse it and process it
   //           on its own.
 
   // 4.2.1.14. Inhibit anyPolicy is implicitly supported; see the documentation
   //           about policy enforcement in pkix.h.
 
   // IMPORTANT: This check must come after the other checks in order for error
   // ranking to work correctly.
-  rv = CheckTimes(cert.GetNSSCert()->validity, time);
+  rv = CheckValidity(cert.GetValidity(), time);
   if (rv != Success) {
     return rv;
   }
 
   return Success;
 }
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -28,17 +28,17 @@
 #include "pkix/pkixtypes.h"
 #include "pkixutil.h"
 #include "certt.h"
 
 namespace mozilla { namespace pkix {
 
 Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
-          BackCert& cert,
+          const BackCert& cert,
           PRTime time,
           EndEntityOrCA endEntityOrCA,
           KeyUsage requiredKeyUsageIfPresent,
           KeyPurposeId requiredEKUIfPresent,
           const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*optional out*/ TrustLevel* trustLevel = nullptr);
 
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -169,36 +169,43 @@ inline int
 daysBeforeYear(int year)
 {
   return (365 * (year - 1))
        + ((year - 1) / 4)    // leap years are every 4 years,
        - ((year - 1) / 100)  // except years divisible by 100,
        + ((year - 1) / 400); // except years divisible by 400.
 }
 
+namespace internal {
+
 // We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
 // accept all time formats allowed in the ASN.1 spec. That is,
 // GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
 // must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
 // +HH:MM or -HH:MM or NOT accepted.
 Result
-TimeChoice(SECItemType type, Input& input, /*out*/ PRTime& time)
+TimeChoice(Input& tagged, uint8_t expectedTag, /*out*/ PRTime& time)
 {
   int days;
 
+  Input input;
+  if (ExpectTagAndGetValue(tagged, expectedTag, input) != Success) {
+    return Failure;
+  }
+
   int yearHi;
   int yearLo;
-  if (type == siGeneralizedTime) {
+  if (expectedTag == GENERALIZED_TIME) {
     if (ReadTwoDigits(input, 0, 99, yearHi) != Success) {
       return Failure;
     }
     if (ReadTwoDigits(input, 0, 99, yearLo) != Success) {
       return Failure;
     }
-  } else if (type == siUTCTime) {
+  } else if (expectedTag == UTCTime) {
     if (ReadTwoDigits(input, 0, 99, yearLo) != Success) {
       return Failure;
     }
     yearHi = yearLo >= 50 ? 19 : 20;
   } else {
     PR_NOT_REACHED("invalid tag given to TimeChoice");
     return Fail(SEC_ERROR_INVALID_TIME);
   }
@@ -300,9 +307,11 @@ TimeChoice(SECItemType type, Input& inpu
                          (static_cast<int64_t>(hours)     * 60 * 60) +
                          (static_cast<int64_t>(minutes)        * 60) +
                          seconds;
 
   time = totalSeconds * PR_USEC_PER_SEC;
   return Success;
 }
 
+} // namespace internal
+
 } } } // namespace mozilla::pkix::der
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -508,43 +508,45 @@ OptionalBoolean(Input& input, bool allow
 // This parser will only parse values between 0..127. If this range is
 // increased then callers will need to be changed.
 inline Result
 Enumerated(Input& input, uint8_t& value)
 {
   return internal::IntegralValue(input, ENUMERATED | 0, value);
 }
 
-// XXX: This function should have the signature:
-//
-//    Result TimeChoice(Input& input, /*out*/ PRTime& time);
-//
-// and parse the tag (and length and value) from the input like the other
-// functions here. However, currently we get TimeChoices already partially
-// decoded by NSS, so for now we'll have this signature, where the input
-// parameter contains the value of the time choice.
-//
-// type must be either siGeneralizedTime or siUTCTime.
+namespace internal {
+
+// internal::TimeChoice implements the shared functionality of GeneralizedTime
+// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
 //
 // Only times from 1970-01-01-00:00:00 onward are accepted, in order to
 // eliminate the chance for complications in converting times to traditional
 // time formats that start at 1970.
-Result TimeChoice(SECItemType type, Input& input, /*out*/ PRTime& time);
+Result TimeChoice(Input& input, uint8_t tag, /*out*/ PRTime& time);
+
+} // namespace internal
 
 // Only times from 1970-01-01-00:00:00 onward are accepted, in order to
 // eliminate the chance for complications in converting times to traditional
 // time formats that start at 1970.
 inline Result
-GeneralizedTime(Input& input, PRTime& time)
+GeneralizedTime(Input& input, /*out*/ PRTime& time)
 {
-  Input value;
-  if (ExpectTagAndGetValue(input, GENERALIZED_TIME, value) != Success) {
-    return Failure;
-  }
-  return TimeChoice(siGeneralizedTime, value, time);
+  return internal::TimeChoice(input, GENERALIZED_TIME, time);
+}
+
+// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
+// eliminate the chance for complications in converting times to traditional
+// time formats that start at 1970.
+inline Result
+TimeChoice(Input& input, /*out*/ PRTime& time)
+{
+  uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
+  return internal::TimeChoice(input, expectedTag, time);
 }
 
 // This parser will only parse values between 0..127. If this range is
 // increased then callers will need to be changed.
 inline Result
 Integer(Input& input, /*out*/ uint8_t& value)
 {
   if (internal::IntegralValue(input, INTEGER, value) != Success) {
--- a/security/pkix/lib/pkixkey.cpp
+++ b/security/pkix/lib/pkixkey.cpp
@@ -30,50 +30,50 @@
 #include "keyhi.h"
 #include "pkix/pkix.h"
 #include "prerror.h"
 #include "secerr.h"
 
 namespace mozilla { namespace pkix {
 
 SECStatus
-VerifySignedData(const CERTSignedData* sd, const SECItem& subjectPublicKeyInfo,
+VerifySignedData(const CERTSignedData& sd, const SECItem& subjectPublicKeyInfo,
                  void* pkcs11PinArg)
 {
-  if (!sd || !sd->data.data || !sd->signatureAlgorithm.algorithm.data ||
-      !sd->signature.data) {
+  if (!sd.data.data || !sd.signatureAlgorithm.algorithm.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())) {
+  if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
   }
 
   // convert sig->len from bit counts to byte count.
-  SECItem sig = sd->signature;
+  SECItem sig = sd.signature;
   DER_ConvertBitString(&sig);
 
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
     spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo));
   if (!spki) {
     return SECFailure;
   }
   ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
     pubKey(SECKEY_ExtractPublicKey(spki.get()));
   if (!pubKey) {
     return SECFailure;
   }
 
   SECOidTag hashAlg;
-  if (VFY_VerifyDataWithAlgorithmID(sd->data.data, static_cast<int>(sd->data.len),
-                                    pubKey.get(), &sig, &sd->signatureAlgorithm,
+  if (VFY_VerifyDataWithAlgorithmID(sd.data.data, static_cast<int>(sd.data.len),
+                                    pubKey.get(), &sig, &sd.signatureAlgorithm,
                                     &hashAlg, pkcs11PinArg) != SECSuccess) {
     return SECFailure;
   }
 
   // TODO: Ideally, we would do this check before we call
   // VFY_VerifyDataWithAlgorithmID. But, VFY_VerifyDataWithAlgorithmID gives us
   // the hash algorithm so it is more convenient to do things in this order.
   uint32_t policy;
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -151,22 +151,27 @@ CheckOCSPResponseSignerCert(TrustDomain&
   // 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);
   }
 
   // TODO(bug 926260): check name constraints
-  return potentialSigner.VerifyOwnSignatureWithKey(trustDomain,
-                                                   issuerSubjectPublicKeyInfo);
+  SECStatus srv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(),
+                                               issuerSubjectPublicKeyInfo);
+  if (srv != SECSuccess) {
+    return MapSECStatus(srv);
+  }
 
   // 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;
 }
 
 MOZILLA_PKIX_ENUM_CLASS ResponderIDType : uint8_t
 {
   byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2
 };
 
@@ -228,17 +233,17 @@ MatchResponderID(ResponderIDType respond
   }
 }
 
 static Result
 VerifyOCSPSignedData(TrustDomain& trustDomain,
                      const CERTSignedData& signedResponseData,
                      const SECItem& spki)
 {
-  SECStatus srv(trustDomain.VerifySignedData(&signedResponseData, 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);
     }
   }
   return MapSECStatus(srv);
 }
 
@@ -262,18 +267,18 @@ VerifySignature(Context& context, Respon
     return rv;
   }
   if (match) {
     return VerifyOCSPSignedData(context.trustDomain, signedResponseData,
                                 context.certID.issuerSubjectPublicKeyInfo);
   }
 
   for (size_t i = 0; i < numCerts; ++i) {
-    BackCert cert(nullptr, BackCert::IncludeCN::No);
-    rv = cert.Init(certs[i]);
+    BackCert cert(certs[i], nullptr, BackCert::IncludeCN::No);
+    rv = cert.Init();
     if (rv != Success) {
       return rv;
     }
     rv = MatchResponderID(responderIDType, responderID,
                           cert.GetSubject(), cert.GetSubjectPublicKeyInfo(),
                           match);
     if (rv == FatalError) {
       return rv;
@@ -309,17 +314,17 @@ SetErrorToMalformedResponseOnBadDERError
     PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0);
   }
 }
 
 SECStatus
 VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
                           PRTime time, uint16_t maxOCSPLifetimeInDays,
                           const SECItem& encodedResponse,
-                          bool& expired,
+                          /*out*/ bool& expired,
                           /*optional out*/ PRTime* thisUpdate,
                           /*optional out*/ PRTime* validThrough)
 {
   // Always initialize this to something reasonable.
   expired = false;
 
   der::Input input;
   if (input.Init(encodedResponse.data, encodedResponse.len) != der::Success) {
@@ -439,17 +444,17 @@ BasicResponse(der::Input& input, Context
 
   if (!input.AtEnd()) {
     // We ignore the lengths of the wrappers because we'll detect bad lengths
     // during parsing--too short and we'll run out of input for parsing a cert,
     // and too long and we'll have leftover data that won't parse as a cert.
 
     // [0] wrapper
     if (der::ExpectTagAndSkipLength(
-          input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0)
+          input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0)
         != der::Success) {
       return der::Failure;
     }
 
     // SEQUENCE wrapper
     if (der::ExpectTagAndSkipLength(input, der::SEQUENCE) != der::Success) {
       return der::Failure;
     }
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -90,66 +90,120 @@ MapSECStatus(SECStatus srv)
 class BackCert
 {
 public:
   // IncludeCN::No means that name constraint enforcement should not consider
   // the subject CN as a possible dNSName. IncludeCN::Yes means that name
   // constraint enforcement will consider the subject CN as a possible dNSName.
   MOZILLA_PKIX_ENUM_CLASS IncludeCN { No = 0, Yes = 1 };
 
-  // nssCert and childCert must be valid for the lifetime of BackCert
-  BackCert(BackCert* childCert, IncludeCN includeCN)
-    : encodedAuthorityInfoAccess(nullptr)
-    , encodedBasicConstraints(nullptr)
-    , encodedCertificatePolicies(nullptr)
-    , encodedExtendedKeyUsage(nullptr)
-    , encodedKeyUsage(nullptr)
-    , encodedNameConstraints(nullptr)
-    , encodedInhibitAnyPolicy(nullptr)
+  // certDER and childCert must be valid for the lifetime of BackCert.
+  BackCert(const SECItem& certDER, const BackCert* childCert,
+           IncludeCN includeCN)
+    : der(certDER)
     , childCert(childCert)
     , includeCN(includeCN)
   {
   }
 
-  Result Init(const SECItem& certDER);
+  Result Init();
 
-  const SECItem& GetDER() const { return nssCert->derCert; }
-  const SECItem& GetIssuer() const { return nssCert->derIssuer; }
-  const SECItem& GetSerialNumber() const { return nssCert->serialNumber; }
-  const SECItem& GetSubject() const { return nssCert->derSubject; }
+  const SECItem& GetDER() const { return der; }
+  const der::Version GetVersion() const { return version; }
+  const CERTSignedData& GetSignedData() const { return signedData; }
+  const SECItem& GetIssuer() const { return issuer; }
+  // XXX: "validity" is a horrible name for the structure that holds
+  // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
+  // RFC 5280 names for everything.
+  const SECItem& GetValidity() const { return validity; }
+  const SECItem& GetSerialNumber() const { return serialNumber; }
+  const SECItem& GetSubject() const { return subject; }
   const SECItem& GetSubjectPublicKeyInfo() const
   {
-    return nssCert->derPublicKey;
+    return subjectPublicKeyInfo;
+  }
+  const SECItem* GetAuthorityInfoAccess() const
+  {
+    return MaybeSECItem(authorityInfoAccess);
+  }
+  const SECItem* GetBasicConstraints() const
+  {
+    return MaybeSECItem(basicConstraints);
+  }
+  const SECItem* GetCertificatePolicies() const
+  {
+    return MaybeSECItem(certificatePolicies);
+  }
+  const SECItem* GetExtKeyUsage() const { return MaybeSECItem(extKeyUsage); }
+  const SECItem* GetKeyUsage() const { return MaybeSECItem(keyUsage); }
+  const SECItem* GetInhibitAnyPolicy() const
+  {
+    return MaybeSECItem(inhibitAnyPolicy);
+  }
+  const SECItem* GetNameConstraints() const
+  {
+    return MaybeSECItem(nameConstraints);
   }
 
-  Result VerifyOwnSignatureWithKey(TrustDomain& trustDomain,
-                                   const SECItem& subjectPublicKeyInfo) const;
+private:
+  const SECItem& der;
 
+public:
+  BackCert const* const childCert;
+  const IncludeCN includeCN;
+
+private:
   der::Version version;
 
-  const SECItem* encodedAuthorityInfoAccess;
-  const SECItem* encodedBasicConstraints;
-  const SECItem* encodedCertificatePolicies;
-  const SECItem* encodedExtendedKeyUsage;
-  const SECItem* encodedKeyUsage;
-  const SECItem* encodedNameConstraints;
-  const SECItem* encodedInhibitAnyPolicy;
+  // When parsing certificates in BackCert::Init, we don't accept empty
+  // extensions. Consequently, we don't have to store a distinction between
+  // empty extensions and extensions that weren't included. However, when
+  // *processing* extensions, we distinguish between whether an extension was
+  // included or not based on whetehr the GetXXX function for the extension
+  // returns nullptr.
+  static inline const SECItem* MaybeSECItem(const SECItem& item)
+  {
+    return item.len > 0 ? &item : nullptr;
+  }
 
-  BackCert* const childCert;
-  const IncludeCN includeCN;
+  // Helper classes to zero-initialize these fields on construction and to
+  // document that they contain non-owning pointers to the data they point
+  // to.
+  struct NonOwningSECItem : public SECItemStr {
+    NonOwningSECItem()
+    {
+      data = nullptr;
+      len = 0;
+    }
+  };
+  struct NonOwningCERTSignedData : public CERTSignedDataStr {
+    NonOwningCERTSignedData() { memset(this, 0, sizeof(*this)); }
+  };
 
-  // Only non-const so that we can pass this to TrustDomain::IsRevoked,
-  // which only takes a non-const pointer because VerifyEncodedOCSPResponse
-  // requires it, and that is only because the implementation of
-  // VerifyEncodedOCSPResponse does a CERT_DupCertificate. TODO: get rid
-  // of that CERT_DupCertificate so that we can make all these things const.
-  /*const*/ CERTCertificate* GetNSSCert() const { return nssCert.get(); }
+  NonOwningCERTSignedData signedData;
+  NonOwningSECItem issuer;
+  // XXX: "validity" is a horrible name for the structure that holds
+  // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
+  // RFC 5280 names for everything.
+  NonOwningSECItem validity;
+  NonOwningSECItem serialNumber;
+  NonOwningSECItem subject;
+  NonOwningSECItem subjectPublicKeyInfo;
 
-private:
-  ScopedCERTCertificate nssCert;
+  NonOwningSECItem authorityInfoAccess;
+  NonOwningSECItem basicConstraints;
+  NonOwningSECItem certificatePolicies;
+  NonOwningSECItem extKeyUsage;
+  NonOwningSECItem inhibitAnyPolicy;
+  NonOwningSECItem keyUsage;
+  NonOwningSECItem nameConstraints;
+  NonOwningSECItem subjectAltName;
+
+  der::Result RememberExtension(der::Input& extnID, const SECItem& extnValue,
+                                /*out*/ bool& understood);
 
   BackCert(const BackCert&) /* = delete */;
   void operator=(const BackCert&); /* = delete */;
 };
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixutil_h
--- a/security/pkix/moz.build
+++ b/security/pkix/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # 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/.
 
 SOURCES += [
     'lib/pkixbind.cpp',
     'lib/pkixbuild.cpp',
+    'lib/pkixcert.cpp',
     'lib/pkixcheck.cpp',
     'lib/pkixder.cpp',
     'lib/pkixkey.cpp',
     'lib/pkixocsp.cpp',
 ]
 
 LOCAL_INCLUDES += [
     'include',
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -7,17 +7,17 @@
 LIBRARY_NAME = 'mozillapkix_gtest'
 
 SOURCES += [
     'nssgtest.cpp',
     'pkix_cert_chain_length_tests.cpp',
     'pkix_cert_extension_tests.cpp',
     'pkix_ocsp_request_tests.cpp',
     'pkixcheck_CheckKeyUsage_tests.cpp',
-    'pkixcheck_CheckTimes_tests.cpp',
+    'pkixcheck_CheckValidity_tests.cpp',
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
     'pkixgtest.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../../include',
--- a/security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
+++ b/security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
@@ -130,17 +130,17 @@ private:
                                  PRTime time,
                                  /*out*/ ScopedCERTCertList& results)
   {
     results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                          encodedIssuerName, time, true);
     return SECSuccess;
   }
 
-  SECStatus VerifySignedData(const CERTSignedData* signedData,
+  SECStatus VerifySignedData(const CERTSignedData& signedData,
                              const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
   SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
                             /*optional*/ const SECItem*,
@@ -183,32 +183,33 @@ protected:
   static TestTrustDomain trustDomain;
 };
 
 /*static*/ TestTrustDomain pkix_cert_chain_length::trustDomain;
 
 TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
 {
   ScopedCERTCertList results;
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, trustDomain.GetLeafeCACert(),
+  ASSERT_SECSuccess(BuildCertChain(trustDomain,
+                                   trustDomain.GetLeafeCACert()->derCert,
                                    now, EndEntityOrCA::MustBeCA,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(),
                          trustDomain.GetLeafeCACert()->subjectName,
                          "CN=Direct End-Entity",
                          EndEntityOrCA::MustBeEndEntity,
                          trustDomain.leafCAKey.get(), privateKey, cert));
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now,
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert->derCert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
@@ -220,32 +221,32 @@ TEST_F(pkix_cert_chain_length, BeyondMax
   ScopedCERTCertificate caCert;
   ASSERT_TRUE(CreateCert(arena.get(),
                          trustDomain.GetLeafeCACert()->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.get(), now,
+                    BuildCertChain(trustDomain, caCert->derCert, now,
                                    EndEntityOrCA::MustBeCA,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 
   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.get(), now,
+                    BuildCertChain(trustDomain, cert->derCert, now,
                                    EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
--- a/security/pkix/test/gtest/pkix_cert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkix_cert_extension_tests.cpp
@@ -25,53 +25,53 @@
 #include "nssgtest.h"
 #include "pkix/pkix.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 // Creates a self-signed certificate with the given extension.
-static bool
+static const SECItem*
 CreateCert(PLArenaPool* arena, const char* subjectStr,
-           const SECItem* extension,
-           /*out*/ ScopedSECKEYPrivateKey& subjectKey,
-           /*out*/ ScopedCERTCertificate& subjectCert)
+           SECItem const* const* extensions, // null-terminated array
+           /*out*/ ScopedSECKEYPrivateKey& subjectKey)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
                                                         serialNumberValue));
   if (!serialNumber) {
-    return false;
+    return nullptr;
   }
   const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
   if (!issuerDER) {
-    return false;
+    return nullptr;
   }
   const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
   if (!subjectDER) {
-    return false;
+    return nullptr;
   }
 
-  const SECItem* extensions[2] = { extension, nullptr };
+  return CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
+                                  serialNumber, issuerDER,
+                                  PR_Now() - ONE_DAY,
+                                  PR_Now() + ONE_DAY,
+                                  subjectDER, extensions,
+                                  nullptr, SEC_OID_SHA256, subjectKey);
+}
 
-  SECItem* certDER(CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
-                                            serialNumber, issuerDER,
-                                            PR_Now() - ONE_DAY,
-                                            PR_Now() + ONE_DAY,
-                                            subjectDER, extensions,
-                                            nullptr, SEC_OID_SHA256,
-                                            subjectKey));
-  if (!certDER) {
-    return false;
-  }
-  subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
-                                        nullptr, false, true);
-  return subjectCert.get() != nullptr;
+// Creates a self-signed certificate with the given extension.
+static const SECItem*
+CreateCert(PLArenaPool* arena, const char* subjectStr,
+           const SECItem* extension,
+           /*out*/ ScopedSECKEYPrivateKey& subjectKey)
+{
+  const SECItem * extensions[] = { extension, nullptr };
+  return CreateCert(arena, subjectStr, extensions, subjectKey);
 }
 
 class TrustEverythingTrustDomain : public TrustDomain
 {
 private:
   SECStatus GetCertTrust(EndEntityOrCA,
                          const CertPolicyId&,
                          const SECItem& candidateCert,
@@ -83,17 +83,17 @@ private:
 
   SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                  PRTime time,
                                  /*out*/ ScopedCERTCertList& results)
   {
     return SECSuccess;
   }
 
-  SECStatus VerifySignedData(const CERTSignedData* signedData,
+  SECStatus VerifySignedData(const CERTSignedData& signedData,
                              const SECItem& subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
   SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
                             /*optional*/ const SECItem*,
@@ -138,23 +138,24 @@ TEST_F(pkix_cert_extensions, UnknownCrit
   };
   static const SECItem unknownCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(unknownCriticalExtensionBytes),
     sizeof(unknownCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown Critical Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key,
-                         cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN,
+                                 &unknownCriticalExtension, key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, cert.get(),
-                                   now, EndEntityOrCA::MustBeEndEntity,
+                    BuildCertChain(trustDomain, *cert, now,
+                                   EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
@@ -171,22 +172,23 @@ TEST_F(pkix_cert_extensions, UnknownNonC
   };
   static const SECItem unknownNonCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(unknownNonCriticalExtensionBytes),
     sizeof(unknownNonCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown NonCritical Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key,
-                         cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN,
+                                 &unknownNonCriticalExtension, key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
-                                   now, EndEntityOrCA::MustBeEndEntity,
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
+                                   EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that an incorrect OID for id-pe-authorityInformationAccess
@@ -204,23 +206,24 @@ TEST_F(pkix_cert_extensions, WrongOIDCri
   };
   static const SECItem wrongOIDCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(wrongOIDCriticalExtensionBytes),
     sizeof(wrongOIDCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical Wrong OID Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key,
-                         cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN, 
+                                 &wrongOIDCriticalExtension, key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, cert.get(),
-                                   now, EndEntityOrCA::MustBeEndEntity,
+                    BuildCertChain(trustDomain, *cert, now,
+                                   EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a id-pe-authorityInformationAccess critical extension
@@ -240,22 +243,23 @@ TEST_F(pkix_cert_extensions, CriticalAIA
   };
   static const SECItem criticalAIAExtension = {
     siBuffer,
     const_cast<unsigned char*>(criticalAIAExtensionBytes),
     sizeof(criticalAIAExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical AIA Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key,
-                         cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN, &criticalAIAExtension,
+                                 key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
-                                   now, EndEntityOrCA::MustBeEndEntity,
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
+                                   EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
@@ -272,23 +276,24 @@ TEST_F(pkix_cert_extensions, UnknownCrit
   };
   static const SECItem unknownCriticalCEExtension = {
     siBuffer,
     const_cast<unsigned char*>(unknownCriticalCEExtensionBytes),
     sizeof(unknownCriticalCEExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown Critical id-ce Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key,
-                         cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN,
+                                 &unknownCriticalCEExtension, key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
-                    BuildCertChain(trustDomain, cert.get(),
-                                   now, EndEntityOrCA::MustBeEndEntity,
+                    BuildCertChain(trustDomain, *cert, now,
+                                   EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a certificate with a known critical id-ce extension (in this case,
@@ -305,19 +310,55 @@ TEST_F(pkix_cert_extensions, KnownCritic
   };
   static const SECItem criticalCEExtension = {
     siBuffer,
     const_cast<unsigned char*>(criticalCEExtensionBytes),
     sizeof(criticalCEExtensionBytes)
   };
   const char* certCN = "CN=Cert With Known Critical id-ce Extension";
   ScopedSECKEYPrivateKey key;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert));
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN, &criticalCEExtension,
+                                 key));
+  ASSERT_TRUE(cert);
   ScopedCERTCertList results;
-  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert,
                                    now, EndEntityOrCA::MustBeEndEntity,
                                    KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
+
+// Two subjectAltNames must result in an error.
+TEST_F(pkix_cert_extensions, DuplicateSubjectAltName)
+{
+  static const uint8_t DER_BYTES[] = {
+    0x30, 22, // SEQUENCE (length = 22)
+      0x06, 3, // OID (length = 3)
+        0x55, 0x1d, 0x11, // 2.5.29.17
+      0x04, 15, // OCTET STRING (length = 15)
+        0x30, 13, // GeneralNames (SEQUENCE) (length = 13)
+          0x82, 11, // [2] (dNSName) (length = 11)
+            'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'
+  };
+  static const SECItem DER = {
+    siBuffer,
+    const_cast<unsigned char*>(DER_BYTES),
+    sizeof(DER_BYTES)
+  };
+  static SECItem const* const extensions[] = { &DER, &DER, nullptr };
+  static const char* certCN = "CN=Cert With Duplicate subjectAltName";
+  ScopedSECKEYPrivateKey key;
+  // cert is owned by the arena
+  const SECItem* cert(CreateCert(arena.get(), certCN, extensions, key));
+  ASSERT_TRUE(cert);
+  ScopedCERTCertList results;
+  ASSERT_SECFailure(SEC_ERROR_EXTENSION_VALUE_INVALID,
+                    BuildCertChain(trustDomain, *cert,
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   KeyUsage::noParticularKeyUsageRequired,
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
deleted file mode 100644
--- a/security/pkix/test/gtest/pkixcheck_CheckTimes_tests.cpp
+++ /dev/null
@@ -1,174 +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"
-#include "pkixtestutil.h"
-
-using namespace mozilla::pkix;
-using namespace mozilla::pkix::test;
-
-namespace mozilla { namespace pkix {
-
-extern Result CheckTimes(const CERTValidity& validity, PRTime time);
-
-} } // namespace mozilla::pkix
-
-static const SECItem empty_null = { siBuffer, nullptr, 0 };
-
-static const PRTime PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
-
-static const uint8_t OLDER_GENERALIZEDTIME_DATA[] = {
-  '1', '9', '9', '9', '0', '1', '0', '1', // 1999-01-01
-  '0', '0', '0', '0', '0', '0', 'Z'       // 00:00:00Z
-};
-static const SECItem OLDER_GENERALIZEDTIME = {
-  siGeneralizedTime,
-  const_cast<uint8_t*>(OLDER_GENERALIZEDTIME_DATA),
-  sizeof(OLDER_GENERALIZEDTIME_DATA)
-};
-
-static const uint8_t OLDER_UTCTIME_DATA[] = {
-  '9', '9', '0', '1', '0', '1',           // (19)99-01-01
-  '0', '0', '0', '0', '0', '0', 'Z'       // 00:00:00Z
-};
-static const SECItem OLDER_UTCTIME = {
-  siUTCTime,
-  const_cast<uint8_t*>(OLDER_UTCTIME_DATA),
-  sizeof(OLDER_UTCTIME_DATA)
-};
-
-static const PRTime NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
-
-static const uint8_t NEWER_GENERALIZEDTIME_DATA[] = {
-  '2', '0', '2', '1', '0', '1', '0', '1', // 2021-01-01
-  '0', '0', '0', '0', '0', '0', 'Z'       // 00:00:00Z
-};
-static const SECItem NEWER_GENERALIZEDTIME = {
-  siGeneralizedTime,
-  const_cast<uint8_t*>(NEWER_GENERALIZEDTIME_DATA),
-  sizeof(NEWER_GENERALIZEDTIME_DATA)
-};
-
-static const uint8_t NEWER_UTCTIME_DATA[] = {
-  '2', '1', '0', '1', '0', '1',           // 2021-01-01
-  '0', '0', '0', '0', '0', '0', 'Z'       // 00:00:00Z
-};
-static const SECItem NEWER_UTCTIME = {
-  siUTCTime,
-  const_cast<uint8_t*>(NEWER_UTCTIME_DATA),
-  sizeof(NEWER_UTCTIME_DATA)
-};
-
-static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
-
-
-
-class pkixcheck_CheckTimes : public ::testing::Test
-{
-public:
-  virtual void SetUp()
-  {
-    PR_SetError(0, 0);
-  }
-};
-
-TEST_F(pkixcheck_CheckTimes, BothEmptyNull)
-{
-  static const CERTValidity validity = { nullptr, empty_null, empty_null };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, NotBeforeEmptyNull)
-{
-  static const CERTValidity validity = { nullptr, empty_null, NEWER_UTCTIME };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, NotAfterEmptyNull)
-{
-  static const CERTValidity validity = { nullptr, OLDER_UTCTIME, empty_null };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, Valid_UTCTIME_UTCTIME)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_UTCTIME, NEWER_UTCTIME
-  };
-  ASSERT_Success(CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_GENERALIZEDTIME, NEWER_GENERALIZEDTIME
-  };
-  ASSERT_Success(CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, Valid_GENERALIZEDTIME_UTCTIME)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_GENERALIZEDTIME, NEWER_UTCTIME
-  };
-  ASSERT_Success(CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, Valid_UTCTIME_GENERALIZEDTIME)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_UTCTIME, NEWER_GENERALIZEDTIME
-  };
-  ASSERT_Success(CheckTimes(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckTimes, InvalidBeforeNotBefore)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_UTCTIME, NEWER_UTCTIME
-  };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, PAST_TIME));
-}
-
-TEST_F(pkixcheck_CheckTimes, InvalidAfterNotAfter)
-{
-  static const CERTValidity validity = {
-    nullptr, OLDER_UTCTIME, NEWER_UTCTIME
-  };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, FUTURE_TIME));
-}
-
-TEST_F(pkixcheck_CheckTimes, InvalidNotAfterBeforeNotBefore)
-{
-  static const CERTValidity validity = {
-    nullptr, NEWER_UTCTIME, OLDER_UTCTIME
-  };
-  ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
-                          CheckTimes(validity, NOW));
-}
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
@@ -0,0 +1,203 @@
+/* -*- 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"
+#include "pkixtestutil.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+namespace mozilla { namespace pkix {
+
+Result CheckValidity(const SECItem& encodedValidity, PRTime time);
+
+} } // namespace mozilla::pkix
+
+static const SECItem empty_null = { siBuffer, nullptr, 0 };
+
+static const PRTime PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
+
+#define OLDER_GENERALIZEDTIME \
+  0x18, 15,                               /* tag, length */ \
+  '1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \
+  '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
+
+#define OLDER_UTCTIME \
+  0x17, 13,                               /* tag, length */ \
+  '9', '9', '0', '1', '0', '1',           /* (19)99-01-01 */ \
+  '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
+
+static const PRTime NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
+
+#define NEWER_GENERALIZEDTIME \
+  0x18, 15,                               /* tag, length */ \
+  '2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
+  '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
+
+#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);
+  }
+};
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+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));
+}
+
+TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
+{
+  ASSERT_RecoverableError(SEC_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));
+}
+
+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));
+}
--- a/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -327,99 +327,98 @@ TEST_F(pkixder_universal_types_tests, En
 // So while we can could accept other ASN1 (ITU-T X.680) encodings for
 // GeneralizedTime we should not accept them, and breaking reading of these
 // other encodings is actually encouraged.
 
 // e.g. TWO_CHARS(53) => '5', '3'
 #define TWO_CHARS(t) static_cast<uint8_t>('0' + ((t) / 10u)), \
                      static_cast<uint8_t>('0' + ((t) % 10u))
 
-// Given a DER-encoded GeneralizedTime where we want to extract the value, we
-// need to skip two bytes: the tag and the length.
-static const uint16_t GT_VALUE_OFFSET = 2;
+// Calls TimeChoice on the UTCTime variant of the given generalized time.
+template <uint16_t LENGTH>
+Result
+TimeChoiceForEquivalentUTCTime(const uint8_t (&generalizedTimeDER)[LENGTH],
+                               /*out*/ PRTime& value)
+{
+  static_assert(LENGTH >= 4,
+                "TimeChoiceForEquivalentUTCTime input too small");
+  uint8_t utcTimeDER[LENGTH - 2];
+  utcTimeDER[0] = 0x17; // tag UTCTime
+  utcTimeDER[1] = LENGTH - 1/*tag*/ - 1/*value*/ - 2/*century*/;
+  // Copy the value except for the first two digits of the year
+  for (size_t i = 2; i < LENGTH - 2; ++i) {
+    utcTimeDER[i] = generalizedTimeDER[i + 2];
+  }
 
-// Given a DER-encoded GeneralizedTime where we want to extract the value as
-// though it were a UTC time, we need to skip four bytes: the tag, the length
-// and the first two digits of the year.
-static const uint16_t UTC_VALUE_OFFSET = 4;
+  Input input;
+  if (input.Init(utcTimeDER, sizeof utcTimeDER) != Success) {
+    return Failure;
+  }
+  return TimeChoice(input, value);
+}
 
 template <uint16_t LENGTH>
 void
 ExpectGoodTime(PRTime expectedValue,
                const uint8_t (&generalizedTimeDER)[LENGTH])
 {
-  static_assert(LENGTH >= UTC_VALUE_OFFSET,
-                "ExpectGoodTime requires input at least UTC_VALUE_OFFSET bytes");
-
   // GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value = 0;
     ASSERT_EQ(Success, GeneralizedTime(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER + GT_VALUE_OFFSET,
-                                  LENGTH - GT_VALUE_OFFSET));
+    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value = 0;
-    ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
+    ASSERT_EQ(Success, TimeChoice(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: UTCTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER + UTC_VALUE_OFFSET,
-                                  LENGTH - UTC_VALUE_OFFSET));
     PRTime value = 0;
-    ASSERT_EQ(Success, TimeChoice(siUTCTime, input, value));
+    ASSERT_EQ(Success,
+              TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
     EXPECT_EQ(expectedValue, value);
   }
 }
 
 template <uint16_t LENGTH>
 void
 ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
 {
-  static_assert(LENGTH >= UTC_VALUE_OFFSET,
-                "ExpectBadTime requires input at least UTC_VALUE_OFFSET bytes");
-
   // GeneralizedTime
   {
     Input input;
     ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value;
     ASSERT_EQ(Failure, GeneralizedTime(input, value));
     EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
-    ASSERT_EQ(Success,
-              input.Init(generalizedTimeDER + GT_VALUE_OFFSET,
-                         LENGTH - GT_VALUE_OFFSET));
+    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
     PRTime value;
-    ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
+    ASSERT_EQ(Failure, TimeChoice(input, value));
     EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
   }
 
   // TimeChoice: UTCTime
   {
-    Input input;
-    ASSERT_EQ(Success,
-              input.Init(generalizedTimeDER + UTC_VALUE_OFFSET,
-                         LENGTH - UTC_VALUE_OFFSET));
     PRTime value;
-    ASSERT_EQ(Failure, TimeChoice(siUTCTime, input, value));
-    EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
+    ASSERT_EQ(Failure,
+              TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
   }
 }
 
 // Control value: a valid time
 TEST_F(pkixder_universal_types_tests, ValidControl)
 {
   const uint8_t GT_DER[] = {
     0x18,                           // Generalized Time
@@ -452,28 +451,33 @@ TEST_F(pkixder_universal_types_tests, Ti
   // GeneralizedTime
   Input gt;
   ASSERT_EQ(Success,
             gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
                     sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
   ASSERT_EQ(Failure, GeneralizedTime(gt, value));
   ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
 
-  static const uint8_t dummy[1] = { 'X' };
-
   // TimeChoice: GeneralizedTime
   Input tc_gt;
-  ASSERT_EQ(Success, tc_gt.Init(dummy, 0));
-  ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, tc_gt, value));
+  ASSERT_EQ(Success,
+            tc_gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
+                       sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
+  ASSERT_EQ(Failure, TimeChoice(tc_gt, value));
   ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
 
   // TimeChoice: UTCTime
+  const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = {
+    0x17, // UTCTime
+    0x00  // Length = 0
+  };
   Input tc_utc;
-  ASSERT_EQ(Success, tc_utc.Init(dummy, 0));
-  ASSERT_EQ(Failure, TimeChoice(siUTCTime, tc_utc, value));
+  ASSERT_EQ(Success, tc_utc.Init(DER_UTCTIME_INVALID_ZERO_LENGTH,
+                                 sizeof DER_UTCTIME_INVALID_ZERO_LENGTH));
+  ASSERT_EQ(Failure, TimeChoice(tc_utc, value));
   ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
 }
 
 // A non zulu time should fail
 TEST_F(pkixder_universal_types_tests, TimeInvalidLocal)
 {
   const uint8_t DER_GENERALIZED_TIME_INVALID_LOCAL[] = {
     0x18,                           // Generalized Time
@@ -489,17 +493,17 @@ TEST_F(pkixder_universal_types_tests, Ti
   const uint8_t DER_GENERALIZED_TIME_INVALID_TRUNCATED[] = {
     0x18,                           // Generalized Time
     12,                             // Length = 12
     '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5'
   };
   ExpectBadTime(DER_GENERALIZED_TIME_INVALID_TRUNCATED);
 }
 
-TEST_F(pkixder_universal_types_tests, GeneralizedTimeNoSeconds)
+TEST_F(pkixder_universal_types_tests, TimeNoSeconds)
 {
   const uint8_t DER_GENERALIZED_TIME_NO_SECONDS[] = {
     0x18,                           // Generalized Time
     13,                             // Length = 13
     '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', 'Z'
   };
   ExpectBadTime(DER_GENERALIZED_TIME_NO_SECONDS);
 }
@@ -554,30 +558,27 @@ TEST_F(pkixder_universal_types_tests, Ge
       PRTime value = 0;
       ASSERT_EQ(Success, GeneralizedTime(input, value));
       EXPECT_EQ(expectedValue, value);
     }
 
     // TimeChoice: GeneralizedTime
     {
       Input input;
-      ASSERT_EQ(Success, input.Init(DER + GT_VALUE_OFFSET,
-                                    sizeof(DER) - GT_VALUE_OFFSET));
+      ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
       PRTime value = 0;
-      ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
+      ASSERT_EQ(Success, TimeChoice(input, value));
       EXPECT_EQ(expectedValue, value);
     }
 
     // TimeChoice: UTCTime, which is limited to years less than 2049.
     if (i <= 2049) {
       Input input;
-      ASSERT_EQ(Success, input.Init(DER + UTC_VALUE_OFFSET,
-                                    sizeof(DER) - UTC_VALUE_OFFSET));
       PRTime value = 0;
-      ASSERT_EQ(Success, TimeChoice(siUTCTime, input, value));
+      ASSERT_EQ(Success, TimeChoiceForEquivalentUTCTime(DER, value));
       EXPECT_EQ(expectedValue, value);
     }
   }
 }
 
 // In order to ensure we we don't run into any trouble with conversions to and
 // from time_t we only accept times from 1970 onwards.
 TEST_F(pkixder_universal_types_tests, TimeYearInvalid1969)
@@ -713,20 +714,19 @@ TEST_F(pkixder_universal_types_tests, Ti
     PRTime value = 0;
     ASSERT_EQ(Success, GeneralizedTime(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
-    ASSERT_EQ(Success, input.Init(DER + GT_VALUE_OFFSET,
-                                  sizeof(DER) - GT_VALUE_OFFSET));
+    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
     PRTime value = 0;
-    ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
+    ASSERT_EQ(Success, TimeChoice(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2014)
 {
   static const uint8_t DER[] = {
     0x18,                           // Generalized Time
@@ -755,21 +755,19 @@ TEST_F(pkixder_universal_types_tests, Ti
     PRTime value;
     ASSERT_EQ(Failure, GeneralizedTime(input, value));
     EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input;
-    ASSERT_EQ(Success,
-              input.Init(DER + GT_VALUE_OFFSET,
-                         sizeof(DER) - GT_VALUE_OFFSET));
+    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
     PRTime value;
-    ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
+    ASSERT_EQ(Failure, TimeChoice(input, value));
     EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeHoursValidRange)
 {
   for (uint8_t i = 0; i <= 23; ++i) {
     const uint8_t DER[] = {
@@ -903,17 +901,17 @@ TEST_F(pkixder_universal_types_tests, Ti
 
   // 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_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
+    ASSERT_EQ(Failure, TimeChoice(input, value));
     EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
   }
 
   // This test is not applicable to TimeChoice: UTCTime
 }
 
 TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar)
 {
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -491,17 +491,17 @@ SignedData(PLArenaPool* arena, const SEC
       certsOutput.Add(*certs);
       ++certs;
     }
     SECItem* certsSequence = certsOutput.Squash(arena, der::SEQUENCE);
     if (!certsSequence) {
       return nullptr;
     }
     certsNested = EncodeNested(arena,
-                               der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
+                               der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
                                certsSequence);
     if (!certsNested) {
       return nullptr;
     }
   }
 
   Output output;
   if (output.Add(tbsData) != der::Success) {
@@ -740,17 +740,17 @@ TBSCertificate(PLArenaPool* arena, long 
   Output output;
 
   if (versionValue != static_cast<long>(der::Version::v1)) {
     SECItem* versionInteger(Integer(arena, versionValue));
     if (!versionInteger) {
       return nullptr;
     }
     SECItem* version(EncodeNested(arena,
-                                  der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
+                                  der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
                                   versionInteger));
     if (!version) {
       return nullptr;
     }
     if (output.Add(version) != der::Success) {
       return nullptr;
     }
   }
@@ -824,17 +824,17 @@ TBSCertificate(PLArenaPool* arena, long 
       }
       ++extensions;
     }
     SECItem* allExtensions(extensionsOutput.Squash(arena, der::SEQUENCE));
     if (!allExtensions) {
       return nullptr;
     }
     SECItem* extensionsWrapped(
-      EncodeNested(arena, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 3,
+      EncodeNested(arena, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3,
                    allExtensions));
     if (!extensions) {
       return nullptr;
     }
     if (output.Add(extensionsWrapped) != der::Success) {
       return nullptr;
     }
   }
@@ -1349,17 +1349,17 @@ CertStatus(OCSPResponseContext& context)
     {
       SECItem* revocationTime = PRTimeToGeneralizedTime(context.arena,
                                                         context.revocationTime);
       if (!revocationTime) {
         return nullptr;
       }
       // TODO(bug 980536): add support for revocationReason
       return EncodeNested(context.arena,
-                          der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1,
+                          der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
                           revocationTime);
     }
     default:
       PR_NOT_REACHED("CertStatus: bad context.certStatus");
       PR_Abort();
   }
   return nullptr;
 }