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 192775 911d02f2c02a13fbdf80083f6d00886e35523f20
parent 192774 5d696c6fe0a7d44afa8955d08fc0e7dc1d80cb3c
child 192776 d76669861f43548b1f5bd07fcd4341e31e24d906
push id7651
push usercbook@mozilla.com
push dateTue, 08 Jul 2014 13:28:32 +0000
treeherderfx-team@05cfda67b9db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1029247
milestone33.0a1
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;
 }