Bug 1077926: Make test certificate generation faster by reusing key, r=keeler
authorBrian Smith <brian@briansmith.org>
Tue, 07 Oct 2014 18:30:47 -0700
changeset 209738 b4d10d3e91311cffb68e935250404269deee2085
parent 209737 fdd14734b0364cae8a74eb086354f52e6c46c5ea
child 209739 b885a82dc02a088f2f478c20f568dbb22f72130e
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerskeeler
bugs1077926
milestone35.0a1
Bug 1077926: Make test certificate generation faster by reusing key, r=keeler
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
security/pkix/test/lib/pkixtestnss.cpp
security/pkix/test/lib/pkixtestutil.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -35,18 +35,16 @@ using namespace mozilla::pkix::test;
 typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
           ScopedCERTCertificate;
 typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
 
 static ByteString
 CreateCert(const char* issuerCN,
            const char* subjectCN,
            EndEntityOrCA endEntityOrCA,
-           /*optional*/ TestKeyPair* issuerKey,
-           /*out*/ ScopedTestKeyPair& subjectKey,
            /*out*/ ScopedCERTCertificate* subjectCert = nullptr)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
 
   ByteString issuerDER(CNToDERName(issuerCN));
@@ -55,23 +53,22 @@ CreateCert(const char* issuerCN,
   ByteString extensions[2];
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     extensions[0] =
       CreateEncodedBasicConstraints(true, nullptr,
                                     ExtensionCriticality::Critical);
     EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
   }
 
+  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
   ByteString certDER(CreateEncodedCertificate(
-                       v3, sha256WithRSAEncryption,
-                       serialNumber, issuerDER,
-                       oneDayBeforeNow, oneDayAfterNow,
-                       subjectDER, extensions, issuerKey,
-                       sha256WithRSAEncryption,
-                       subjectKey));
+                       v3, sha256WithRSAEncryption, serialNumber, issuerDER,
+                       oneDayBeforeNow, oneDayAfterNow, subjectDER,
+                       *reusedKey, extensions, *reusedKey,
+                       sha256WithRSAEncryption));
   EXPECT_FALSE(ENCODING_FAILED(certDER));
   if (subjectCert) {
     SECItem certDERItem = {
       siBuffer,
       const_cast<uint8_t*>(certDER.data()),
       static_cast<unsigned int>(certDER.length())
     };
     *subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
@@ -95,17 +92,17 @@ public:
 
     static_assert(MOZILLA_PKIX_ARRAY_LENGTH(names) ==
                     MOZILLA_PKIX_ARRAY_LENGTH(certChainTail),
                   "mismatch in sizes of names and certChainTail arrays");
 
     for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) {
       const char* issuerName = i == 0 ? names[0] : names[i-1];
       (void) CreateCert(issuerName, names[i], EndEntityOrCA::MustBeCA,
-                        leafCAKey.get(), leafCAKey, &certChainTail[i]);
+                        &certChainTail[i]);
     }
 
     return true;
   }
 
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
                               Input candidateCert,
@@ -187,17 +184,16 @@ private:
     return TestCheckPublicKey(subjectPublicKeyInfo);
   }
 
   // We hold references to CERTCertificates in the cert chain tail so that we
   // CERT_CreateSubjectCertList can find them.
   ScopedCERTCertificate certChainTail[7];
 
 public:
-  ScopedTestKeyPair leafCAKey;
   CERTCertificate* GetLeafCACert() const
   {
     return certChainTail[MOZILLA_PKIX_ARRAY_LENGTH(certChainTail) - 1].get();
   }
 };
 
 class pkixbuild : public ::testing::Test
 {
@@ -233,64 +229,58 @@ TEST_F(pkixbuild, MaxAcceptableCertChain
                              EndEntityOrCA::MustBeCA,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
                              nullptr/*stapledOCSPResponse*/));
   }
 
   {
-    ScopedTestKeyPair unusedKeyPair;
     ScopedCERTCertificate cert;
     ByteString certDER(CreateCert("CA7", "Direct End-Entity",
-                                  EndEntityOrCA::MustBeEndEntity,
-                                  trustDomain.leafCAKey.get(), unusedKeyPair));
+                                  EndEntityOrCA::MustBeEndEntity));
     ASSERT_FALSE(ENCODING_FAILED(certDER));
     Input certDERInput;
     ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
     ASSERT_EQ(Success,
               BuildCertChain(trustDomain, certDERInput, Now(),
                              EndEntityOrCA::MustBeEndEntity,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
                              nullptr/*stapledOCSPResponse*/));
   }
 }
 
 TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
 {
   static char const* const caCertName = "CA Too Far";
-  ScopedTestKeyPair caKeyPair;
 
   // We need a CERTCertificate for caCert so that the trustdomain's FindIssuer
   // method can find it through the NSS cert DB.
   ScopedCERTCertificate caCert;
 
   {
     ByteString certDER(CreateCert("CA7", caCertName, EndEntityOrCA::MustBeCA,
-                                  trustDomain.leafCAKey.get(), caKeyPair,
                                   &caCert));
     ASSERT_FALSE(ENCODING_FAILED(certDER));
     Input certDERInput;
     ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
     ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
               BuildCertChain(trustDomain, certDERInput, Now(),
                              EndEntityOrCA::MustBeCA,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
                              nullptr/*stapledOCSPResponse*/));
   }
 
   {
-    ScopedTestKeyPair unusedKeyPair;
     ByteString certDER(CreateCert(caCertName, "End-Entity Too Far",
-                                  EndEntityOrCA::MustBeEndEntity,
-                                  caKeyPair.get(), unusedKeyPair));
+                                  EndEntityOrCA::MustBeEndEntity));
     ASSERT_FALSE(ENCODING_FAILED(certDER));
     Input certDERInput;
     ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
     ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
               BuildCertChain(trustDomain, certDERInput, Now(),
                              EndEntityOrCA::MustBeEndEntity,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
@@ -378,35 +368,33 @@ public:
 
 private:
   ByteString rootDER;
 };
 
 TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
 {
   const char* rootCN = "Root CA";
-  ScopedTestKeyPair rootKey;
   ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
-                                nullptr, rootKey, nullptr));
+                                nullptr));
   EXPECT_FALSE(ENCODING_FAILED(rootDER));
   ExpiredCertTrustDomain expiredCertTrustDomain(rootDER);
 
   ByteString serialNumber(CreateEncodedSerialNumber(100));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
   ByteString issuerDER(CNToDERName(rootCN));
   ByteString subjectDER(CNToDERName("Expired End-Entity Cert"));
-  ScopedTestKeyPair unusedSubjectKey;
+  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
   ByteString certDER(CreateEncodedCertificate(
                        v3, sha256WithRSAEncryption,
                        serialNumber, issuerDER,
                        oneDayBeforeNow - Time::ONE_DAY_IN_SECONDS,
                        oneDayBeforeNow,
-                       subjectDER, nullptr, rootKey.get(),
-                       sha256WithRSAEncryption,
-                       unusedSubjectKey));
+                       subjectDER, *reusedKey, nullptr, *reusedKey,
+                       sha256WithRSAEncryption));
   EXPECT_FALSE(ENCODING_FAILED(certDER));
 
   Input cert;
   ASSERT_EQ(Success, cert.Init(certDER.data(), certDER.length()));
   ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
             BuildCertChain(expiredCertTrustDomain, cert, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -26,45 +26,42 @@
 #include "pkixgtest.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 // Creates a self-signed certificate with the given extension.
 static ByteString
-CreateCert(const char* subjectCN,
-           const ByteString* extensions, // empty-string-terminated array
-           /*out*/ ScopedTestKeyPair& subjectKey)
+CreateCertWithExtensions(const char* subjectCN,
+                         const ByteString* extensions)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
   ByteString issuerDER(CNToDERName(subjectCN));
   EXPECT_FALSE(ENCODING_FAILED(issuerDER));
   ByteString subjectDER(CNToDERName(subjectCN));
   EXPECT_FALSE(ENCODING_FAILED(subjectDER));
+  ScopedTestKeyPair subjectKey(CloneReusedKeyPair());
   return CreateEncodedCertificate(v3, sha256WithRSAEncryption,
                                   serialNumber, issuerDER,
                                   oneDayBeforeNow, oneDayAfterNow,
-                                  subjectDER, extensions,
-                                  nullptr,
-                                  sha256WithRSAEncryption,
-                                  subjectKey);
+                                  subjectDER, *subjectKey, extensions,
+                                  *subjectKey,
+                                  sha256WithRSAEncryption);
 }
 
 // Creates a self-signed certificate with the given extension.
 static ByteString
-CreateCert(const char* subjectStr,
-           const ByteString& extension,
-           /*out*/ ScopedTestKeyPair& subjectKey)
+CreateCertWithOneExtension(const char* subjectStr, const ByteString& extension)
 {
   const ByteString extensions[] = { extension, ByteString() };
-  return CreateCert(subjectStr, extensions, subjectKey);
+  return CreateCertWithExtensions(subjectStr, extensions);
 }
 
 class TrustEverythingTrustDomain : public TrustDomain
 {
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
                               Input candidateCert,
                               /*out*/ TrustLevel& trustLevel)
@@ -131,18 +128,17 @@ TEST_F(pkixcert_extension, UnknownCritic
         0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
       0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
       0x04, 0x00 // OCTET STRING (length = 0)
   };
   static const ByteString
     unknownCriticalExtension(unknownCriticalExtensionBytes,
                              sizeof(unknownCriticalExtensionBytes));
   const char* certCN = "Cert With Unknown Critical Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, unknownCriticalExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN, unknownCriticalExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -161,18 +157,18 @@ TEST_F(pkixcert_extension, UnknownNonCri
         0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a,
         0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
       0x04, 0x00 // OCTET STRING (length = 0)
   };
   static const ByteString
     unknownNonCriticalExtension(unknownNonCriticalExtensionBytes,
                                 sizeof(unknownNonCriticalExtensionBytes));
   const char* certCN = "Cert With Unknown NonCritical Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, unknownNonCriticalExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN,
+                                             unknownNonCriticalExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Success,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -192,18 +188,18 @@ TEST_F(pkixcert_extension, WrongOIDCriti
         0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
       0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
       0x04, 0x00 // OCTET STRING (length = 0)
   };
   static const ByteString
     wrongOIDCriticalExtension(wrongOIDCriticalExtensionBytes,
                               sizeof(wrongOIDCriticalExtensionBytes));
   const char* certCN = "Cert With Critical Wrong OID Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, wrongOIDCriticalExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN,
+                                             wrongOIDCriticalExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -225,18 +221,17 @@ TEST_F(pkixcert_extension, CriticalAIAEx
       0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
       0x04, 0x02, // OCTET STRING (length = 2)
         0x30, 0x00, // SEQUENCE (length = 0)
   };
   static const ByteString
     criticalAIAExtension(criticalAIAExtensionBytes,
                          sizeof(criticalAIAExtensionBytes));
   const char* certCN = "Cert With Critical AIA Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, criticalAIAExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN, criticalAIAExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Success,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -255,18 +250,18 @@ TEST_F(pkixcert_extension, UnknownCritic
         0x55, 0x1d, 0x37, // 2.5.29.55
       0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
       0x04, 0x00 // OCTET STRING (length = 0)
   };
   static const ByteString
     unknownCriticalCEExtension(unknownCriticalCEExtensionBytes,
                                sizeof(unknownCriticalCEExtensionBytes));
   const char* certCN = "Cert With Unknown Critical id-ce Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, unknownCriticalCEExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN,
+                                             unknownCriticalCEExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -285,18 +280,17 @@ TEST_F(pkixcert_extension, KnownCritical
       0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
       0x04, 0x03, // OCTET STRING (length = 3)
         0x02, 0x01, 0x00, // INTEGER (length = 1, value = 0)
   };
   static const ByteString
     criticalCEExtension(criticalCEExtensionBytes,
                         sizeof(criticalCEExtensionBytes));
   const char* certCN = "Cert With Known Critical id-ce Extension";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, criticalCEExtension, key));
+  ByteString cert(CreateCertWithOneExtension(certCN, criticalCEExtension));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Success,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
@@ -314,18 +308,17 @@ TEST_F(pkixcert_extension, DuplicateSubj
       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 ByteString DER(DER_BYTES, sizeof(DER_BYTES));
   static const ByteString extensions[] = { DER, DER, ByteString() };
   static const char* certCN = "Cert With Duplicate subjectAltName";
-  ScopedTestKeyPair key;
-  ByteString cert(CreateCert(certCN, extensions, key));
+  ByteString cert(CreateCertWithExtensions(certCN, extensions));
   ASSERT_FALSE(ENCODING_FAILED(cert));
   Input certInput;
   ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
   ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID,
             BuildCertChain(trustDomain, certInput, Now(),
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
--- a/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
@@ -11,18 +11,16 @@
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 static ByteString
 CreateCert(const char* issuerCN,
            const char* subjectCN,
            EndEntityOrCA endEntityOrCA,
            const ByteString& signatureAlgorithm,
-           /*optional*/ TestKeyPair* issuerKey,
-           /*out*/ ScopedTestKeyPair& subjectKey,
            /*out*/ ByteString& subjectDER)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
 
   ByteString issuerDER(CNToDERName(issuerCN));
@@ -33,22 +31,23 @@ CreateCert(const char* issuerCN,
   ByteString extensions[2];
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     extensions[0] =
       CreateEncodedBasicConstraints(true, nullptr,
                                     ExtensionCriticality::Critical);
     EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
   }
 
+  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
   ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm,
-                                              serialNumber,
-                                              issuerDER, oneDayBeforeNow,
-                                              oneDayAfterNow, subjectDER,
-                                              extensions, issuerKey,
-                                              signatureAlgorithm, subjectKey));
+                                              serialNumber, issuerDER,
+                                              oneDayBeforeNow, oneDayAfterNow,
+                                              subjectDER, *reusedKey,
+                                              extensions, *reusedKey,
+                                              signatureAlgorithm));
   EXPECT_FALSE(ENCODING_FAILED(certDER));
   return certDER;
 }
 
 class AlgorithmTestsTrustDomain : public TrustDomain
 {
 public:
   AlgorithmTestsTrustDomain(const ByteString& rootDER,
@@ -201,54 +200,48 @@ class pkixcert_IsValidChainForAlgorithm
   , public ::testing::WithParamInterface<ChainValidity>
 {
 };
 
 TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm)
 {
   const ChainValidity& chainValidity(GetParam());
   const char* rootCN = "CN=Root";
-  ScopedTestKeyPair rootKey;
   ByteString rootSubjectDER;
   ByteString rootEncoded(
     CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
-               chainValidity.rootSignatureAlgorithm,
-               nullptr, rootKey, rootSubjectDER));
+               chainValidity.rootSignatureAlgorithm, rootSubjectDER));
   EXPECT_FALSE(ENCODING_FAILED(rootEncoded));
   EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER));
 
   const char* issuerCN = rootCN;
-  TestKeyPair* issuerKey = rootKey.get();
 
   const char* intermediateCN = "CN=Intermediate";
-  ScopedTestKeyPair intermediateKey;
   ByteString intermediateSubjectDER;
   ByteString intermediateEncoded;
   if (chainValidity.optionalIntermediateSignatureAlgorithm != NO_INTERMEDIATE) {
     intermediateEncoded =
       CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA,
                  chainValidity.optionalIntermediateSignatureAlgorithm,
-                 rootKey.get(), intermediateKey, intermediateSubjectDER);
+                 intermediateSubjectDER);
     EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded));
     EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER));
     issuerCN = intermediateCN;
-    issuerKey = intermediateKey.get();
   }
 
   AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER,
                                         intermediateEncoded,
                                         intermediateSubjectDER);
 
   const char* endEntityCN = "CN=End Entity";
-  ScopedTestKeyPair endEntityKey;
   ByteString endEntitySubjectDER;
   ByteString endEntityEncoded(
     CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity,
                chainValidity.endEntitySignatureAlgorithm,
-               issuerKey, endEntityKey, endEntitySubjectDER));
+               endEntitySubjectDER));
   EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded));
   EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER));
 
   Input endEntity;
   ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(),
                                     endEntityEncoded.length()));
   Result expectedResult = chainValidity.isValid
                         ? Success
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -418,23 +418,23 @@ protected:
 
     const ByteString extensions[] = {
       signerEKUDER
         ? CreateEncodedEKUExtension(*signerEKUDER,
                                     ExtensionCriticality::NotCritical)
         : ByteString(),
       ByteString()
     };
-    ScopedTestKeyPair signerKeyPair;
+    ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
     ByteString signerDER(CreateEncodedCertificate(
-                           ++rootIssuedCount, rootName,
-                           oneDayBeforeNow, oneDayAfterNow, certSubjectName,
-                           certSignatureAlgorithm,
+                           ++rootIssuedCount, certSignatureAlgorithm,
+                           rootName, oneDayBeforeNow, oneDayAfterNow,
+                           certSubjectName, *signerKeyPair,
                            signerEKUDER ? extensions : nullptr,
-                           rootKeyPair.get(), signerKeyPair));
+                           *rootKeyPair));
     EXPECT_FALSE(ENCODING_FAILED(signerDER));
     if (signerDEROut) {
       *signerDEROut = signerDER;
     }
 
     ByteString signerNameDER;
     if (signerName) {
       signerNameDER = CNToDERName(signerName);
@@ -446,45 +446,42 @@ protected:
                                                oneDayBeforeNow,
                                                oneDayBeforeNow,
                                                &oneDayAfterNow,
                                                sha256WithRSAEncryption,
                                                certs);
   }
 
   static ByteString CreateEncodedCertificate(uint32_t serialNumber,
+                                             const ByteString& signatureAlg,
                                              const char* issuer,
                                              time_t notBefore,
                                              time_t notAfter,
                                              const char* subject,
-                                             const ByteString& signatureAlg,
+                                             const TestKeyPair& subjectKeyPair,
                                 /*optional*/ const ByteString* extensions,
-                                /*optional*/ TestKeyPair* signerKeyPair,
-                                     /*out*/ ScopedTestKeyPair& keyPair)
+                                             const TestKeyPair& signerKeyPair)
   {
     ByteString serialNumberDER(CreateEncodedSerialNumber(serialNumber));
     if (ENCODING_FAILED(serialNumberDER)) {
       return ByteString();
     }
     ByteString issuerDER(CNToDERName(issuer));
     if (ENCODING_FAILED(issuerDER)) {
       return ByteString();
     }
     ByteString subjectDER(CNToDERName(subject));
     if (ENCODING_FAILED(subjectDER)) {
       return ByteString();
     }
     return ::mozilla::pkix::test::CreateEncodedCertificate(
-                                    v3,
-                                    signatureAlg,
-                                    serialNumberDER, issuerDER, notBefore,
-                                    notAfter, subjectDER, extensions,
-                                    signerKeyPair,
-                                    signatureAlg,
-                                    keyPair);
+                                    v3, signatureAlg, serialNumberDER,
+                                    issuerDER, notBefore, notAfter,
+                                    subjectDER, subjectKeyPair, extensions,
+                                    signerKeyPair, signatureAlg);
   }
 
   static const Input OCSPSigningEKUDER;
 };
 
 /*static*/ const Input pkixocsp_VerifyEncodedResponse_DelegatedResponder::
   OCSPSigningEKUDER(tlv_id_kp_OCSPSigning);
 
@@ -572,23 +569,23 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const char* signerName = "good_indirect_expired";
 
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
 
-  ScopedTestKeyPair signerKeyPair;
+  ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
   ByteString signerDER(CreateEncodedCertificate(
-                          ++rootIssuedCount, rootName,
+                          ++rootIssuedCount, sha256WithRSAEncryption, rootName,
                           now - (10 * Time::ONE_DAY_IN_SECONDS),
                           now - (2 * Time::ONE_DAY_IN_SECONDS),
-                          signerName, sha256WithRSAEncryption, extensions,
-                          rootKeyPair.get(), signerKeyPair));
+                          signerName, *signerKeyPair, extensions,
+                          *rootKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
                          oneDayBeforeNow, &oneDayAfterNow,
@@ -608,23 +605,24 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const char* signerName = "good_indirect_future";
 
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
 
-  ScopedTestKeyPair signerKeyPair;
+  ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
   ByteString signerDER(CreateEncodedCertificate(
-                         ++rootIssuedCount, rootName,
+                         ++rootIssuedCount, sha256WithRSAEncryption,
+                         rootName,
                          now + (2 * Time::ONE_DAY_IN_SECONDS),
                          now + (10 * Time::ONE_DAY_IN_SECONDS),
-                         signerName, sha256WithRSAEncryption, extensions,
-                         rootKeyPair.get(), signerKeyPair));
+                         signerName, *signerKeyPair, extensions,
+                         *rootKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
                          oneDayBeforeNow, &oneDayAfterNow,
@@ -714,21 +712,21 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   ASSERT_TRUE(unknownKeyPair);
 
   // Delegated responder cert signed by unknown issuer
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
-  ScopedTestKeyPair signerKeyPair;
+  ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
   ByteString signerDER(CreateEncodedCertificate(
-                         1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, sha256WithRSAEncryption, extensions,
-                         unknownKeyPair.get(), signerKeyPair));
+                         1, sha256WithRSAEncryption, subCAName,
+                         oneDayBeforeNow, oneDayAfterNow, signerName,
+                         *signerKeyPair, extensions, *unknownKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by that delegated responder
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
@@ -754,35 +752,34 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const char* subCAName = "good_indirect_subca_1_first sub-CA";
   static const char* signerName = "good_indirect_subca_1_first OCSP signer";
 
   // sub-CA of root (root is the direct issuer of endEntity)
   const ByteString subCAExtensions[] = {
     CreateEncodedBasicConstraints(true, 0, ExtensionCriticality::NotCritical),
     ByteString()
   };
-  ScopedTestKeyPair subCAKeyPair;
+  ScopedTestKeyPair subCAKeyPair(GenerateKeyPair());
   ByteString subCADER(CreateEncodedCertificate(
-                        ++rootIssuedCount, rootName,
-                        oneDayBeforeNow, oneDayAfterNow,
-                        subCAName, sha256WithRSAEncryption,
-                        subCAExtensions, rootKeyPair.get(), subCAKeyPair));
+                        ++rootIssuedCount, sha256WithRSAEncryption, rootName,
+                        oneDayBeforeNow, oneDayAfterNow, subCAName,
+                        *subCAKeyPair, subCAExtensions, *rootKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(subCADER));
 
   // Delegated responder cert signed by that sub-CA
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString(),
   };
-  ScopedTestKeyPair signerKeyPair;
+  ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
   ByteString signerDER(CreateEncodedCertificate(
-                         1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, sha256WithRSAEncryption, extensions,
-                         subCAKeyPair.get(), signerKeyPair));
+                         1, sha256WithRSAEncryption, subCAName,
+                         oneDayBeforeNow, oneDayAfterNow, signerName,
+                         *signerKeyPair, extensions, *subCAKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   ByteString certs[] = { subCADER, signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
@@ -809,37 +806,36 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const char* subCAName = "good_indirect_subca_1_second sub-CA";
   static const char* signerName = "good_indirect_subca_1_second OCSP signer";
 
   // sub-CA of root (root is the direct issuer of endEntity)
   const ByteString subCAExtensions[] = {
     CreateEncodedBasicConstraints(true, 0, ExtensionCriticality::NotCritical),
     ByteString()
   };
-  ScopedTestKeyPair subCAKeyPair;
-  ByteString subCADER(CreateEncodedCertificate(++rootIssuedCount, rootName,
+  ScopedTestKeyPair subCAKeyPair(GenerateKeyPair());
+  ByteString subCADER(CreateEncodedCertificate(++rootIssuedCount,
+                                               sha256WithRSAEncryption,
+                                               rootName,
                                                oneDayBeforeNow, oneDayAfterNow,
-                                               subCAName,
-                                               sha256WithRSAEncryption,
-                                               subCAExtensions,
-                                               rootKeyPair.get(),
-                                               subCAKeyPair));
+                                               subCAName, *subCAKeyPair,
+                                               subCAExtensions, *rootKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(subCADER));
 
   // Delegated responder cert signed by that sub-CA
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
-  ScopedTestKeyPair signerKeyPair;
+  ScopedTestKeyPair signerKeyPair(GenerateKeyPair());
   ByteString signerDER(CreateEncodedCertificate(
-                         1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, sha256WithRSAEncryption, extensions,
-                         subCAKeyPair.get(), signerKeyPair));
+                         1, sha256WithRSAEncryption, subCAName,
+                         oneDayBeforeNow, oneDayAfterNow, signerName,
+                         *signerKeyPair, extensions, *subCAKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   ByteString certs[] = { signerDER, subCADER, ByteString() };
   ByteString responseString(
                  CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
--- a/security/pkix/test/lib/pkixtestnss.cpp
+++ b/security/pkix/test/lib/pkixtestnss.cpp
@@ -27,16 +27,17 @@
 #include <limits>
 
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "nss.h"
 #include "pk11pub.h"
 #include "pkix/pkixnss.h"
 #include "pkixder.h"
+#include "prinit.h"
 #include "secerr.h"
 #include "secitem.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
 namespace {
 
 typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
@@ -47,23 +48,34 @@ typedef ScopedPtr<SECKEYPrivateKey, SECK
 inline void
 SECITEM_FreeItem_true(SECItem* item)
 {
   SECITEM_FreeItem(item, true);
 }
 
 typedef mozilla::pkix::ScopedPtr<SECItem, SECITEM_FreeItem_true> ScopedSECItem;
 
-Result
+TestKeyPair* GenerateKeyPairInner();
+
+void
 InitNSSIfNeeded()
 {
   if (NSS_NoDB_Init(nullptr) != SECSuccess) {
-    return MapPRErrorCodeToResult(PR_GetError());
+    abort();
   }
-  return Success;
+}
+
+static ScopedTestKeyPair reusedKeyPair;
+
+PRStatus
+InitReusedKeyPair()
+{
+  InitNSSIfNeeded();
+  reusedKeyPair = GenerateKeyPairInner();
+  return reusedKeyPair ? PR_SUCCESS : PR_FAILURE;
 }
 
 class NSSTestKeyPair : public TestKeyPair
 {
 public:
   // NSSTestKeyPair takes ownership of privateKey.
   NSSTestKeyPair(const ByteString& spki,
                  const ByteString& spk,
@@ -137,26 +149,24 @@ private:
 // Ownership of privateKey is transfered.
 TestKeyPair* CreateTestKeyPair(const ByteString& spki,
                                const ByteString& spk,
                                SECKEYPrivateKey* privateKey)
 {
   return new (std::nothrow) NSSTestKeyPair(spki, spk, privateKey);
 }
 
+namespace {
+
 TestKeyPair*
-GenerateKeyPair()
+GenerateKeyPairInner()
 {
-  if (InitNSSIfNeeded() != Success) {
-    return nullptr;
-  }
-
   ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalSlot());
   if (!slot) {
-    return nullptr;
+    abort();
   }
 
   // Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient
   // entropy to generate a random key. Attempting to add some entropy and
   // retrying appears to solve this issue.
   for (uint32_t retries = 0; retries < 10; retries++) {
     PK11RSAGenParams params;
     params.keySizeInBits = 2048;
@@ -166,22 +176,22 @@ GenerateKeyPair()
       privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
                                       &params, &publicKeyTemp, false, true,
                                       nullptr));
     ScopedSECKEYPublicKey publicKey(publicKeyTemp);
     if (privateKey) {
       ScopedSECItem
         spkiDER(SECKEY_EncodeDERSubjectPublicKeyInfo(publicKey.get()));
       if (!spkiDER) {
-        return nullptr;
+        break;
       }
       ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
         spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
       if (!spki) {
-        return nullptr;
+        break;
       }
       SECItem spkDER = spki->subjectPublicKey;
       DER_ConvertBitString(&spkDER); // bits to bytes
       return CreateTestKeyPair(ByteString(spkiDER->data, spkiDER->len),
                                ByteString(spkDER.data, spkDER.len),
                                privateKey.release());
     }
 
@@ -196,59 +206,75 @@ GenerateKeyPair()
     // https://xkcd.com/221/
     static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 };
     if (PK11_RandomUpdate((void*) &RANDOM_NUMBER,
                           sizeof(RANDOM_NUMBER)) != SECSuccess) {
       break;
     }
   }
 
+  abort();
+#if defined(_MSC_VER) && (_MSC_VER < 1700)
+  // Older versions of MSVC don't know that abort() never returns, so silence
+  // its warning by adding a redundant and never-reached return. But, only do
+  // it for that ancient compiler, because some other compilers will rightly
+  // warn that the return statement is unreachable.
   return nullptr;
+#endif
+}
+
+} // unnamed namespace
+
+TestKeyPair*
+GenerateKeyPair()
+{
+  InitNSSIfNeeded();
+  return GenerateKeyPairInner();
+}
+
+TestKeyPair*
+CloneReusedKeyPair()
+{
+  static PRCallOnceType initCallOnce;
+  if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) {
+    abort();
+  }
+  assert(reusedKeyPair);
+  return reusedKeyPair->Clone();
 }
 
 ByteString
 SHA1(const ByteString& toHash)
 {
-  if (InitNSSIfNeeded() != Success) {
-    return ByteString();
-  }
+  InitNSSIfNeeded();
 
   uint8_t digestBuf[SHA1_LENGTH];
   SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, toHash.data(),
                                static_cast<int32_t>(toHash.length()));
   if (srv != SECSuccess) {
     return ByteString();
   }
   return ByteString(digestBuf, sizeof(digestBuf));
 }
 
 Result
 TestCheckPublicKey(Input subjectPublicKeyInfo)
 {
-  Result rv = InitNSSIfNeeded();
-  if (rv != Success) {
-    return rv;
-  }
+  InitNSSIfNeeded();
   return CheckPublicKey(subjectPublicKeyInfo);
 }
 
 Result
 TestVerifySignedData(const SignedDataWithSignature& signedData,
                      Input subjectPublicKeyInfo)
 {
-  Result rv = InitNSSIfNeeded();
-  if (rv != Success) {
-    return rv;
-  }
+  InitNSSIfNeeded();
   return VerifySignedData(signedData, subjectPublicKeyInfo, nullptr);
 }
 
 Result
 TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
-  Result rv = InitNSSIfNeeded();
-  if (rv != Success) {
-    return rv;
-  }
+  InitNSSIfNeeded();
   return DigestBuf(item, digestBuf, digestBufLen);
 }
 
 } } } // namespace mozilla::pkix::test
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -344,26 +344,23 @@ YMDHMS(int16_t year, int16_t month, int1
   totalSeconds += hour * 60 * 60;
   totalSeconds += minutes * 60;
   totalSeconds += seconds;
   return TimeFromElapsedSecondsAD(totalSeconds);
 }
 
 static ByteString
 SignedData(const ByteString& tbsData,
-           /*optional*/ TestKeyPair* keyPair,
+           const TestKeyPair& keyPair,
            const ByteString& signatureAlgorithm,
            bool corrupt, /*optional*/ const ByteString* certs)
 {
   ByteString signature;
-  if (keyPair) {
-    if (keyPair->SignData(tbsData, signatureAlgorithm, signature)
-          != Success) {
-       return ByteString();
-     }
+  if (keyPair.SignData(tbsData, signatureAlgorithm, signature) != Success) {
+    return ByteString();
   }
 
   // TODO: add ability to have signatures of bit length not divisible by 8,
   // resulting in unused bits in the bitstring encoding
   ByteString signatureNested(BitString(signature, corrupt));
   if (ENCODING_FAILED(signatureNested)) {
     return ByteString();
   }
@@ -459,50 +456,38 @@ static ByteString TBSCertificate(long ve
 //         signatureAlgorithm   AlgorithmIdentifier,
 //         signatureValue       BIT STRING  }
 ByteString
 CreateEncodedCertificate(long version, const ByteString& signature,
                          const ByteString& serialNumber,
                          const ByteString& issuerNameDER,
                          time_t notBefore, time_t notAfter,
                          const ByteString& subjectNameDER,
+                         const TestKeyPair& subjectKeyPair,
                          /*optional*/ const ByteString* extensions,
-                         /*optional*/ TestKeyPair* issuerKeyPair,
-                         const ByteString& signatureAlgorithm,
-                         /*out*/ ScopedTestKeyPair& keyPairResult)
+                         const TestKeyPair& issuerKeyPair,
+                         const ByteString& signatureAlgorithm)
 {
-  // It may be the case that privateKeyResult references the same TestKeyPair
-  // as issuerKeyPair. Thus, we can't set keyPairResult until after we're done
-  // with issuerKeyPair.
-  ScopedTestKeyPair subjectKeyPair(GenerateKeyPair());
-  if (!subjectKeyPair) {
-    return ByteString();
-  }
-
   ByteString tbsCertificate(TBSCertificate(version, serialNumber,
                                            signature, issuerNameDER, notBefore,
                                            notAfter, subjectNameDER,
-                                           subjectKeyPair->subjectPublicKeyInfo,
+                                           subjectKeyPair.subjectPublicKeyInfo,
                                            extensions));
   if (ENCODING_FAILED(tbsCertificate)) {
     return ByteString();
   }
 
-  ByteString result(SignedData(tbsCertificate,
-                               issuerKeyPair ? issuerKeyPair
-                                             : subjectKeyPair.get(),
+  ByteString result(SignedData(tbsCertificate, issuerKeyPair,
                                signatureAlgorithm, false, nullptr));
   if (ENCODING_FAILED(result)) {
     return ByteString();
   }
 
   MaybeLogOutput(result, "cert");
 
-  keyPairResult = subjectKeyPair.release();
-
   return result;
 }
 
 // TBSCertificate  ::=  SEQUENCE  {
 //      version         [0]  Version DEFAULT v1,
 //      serialNumber         CertificateSerialNumber,
 //      signature            AlgorithmIdentifier,
 //      issuer               Name,
@@ -758,20 +743,19 @@ ResponseBytes(OCSPResponseContext& conte
 ByteString
 BasicOCSPResponse(OCSPResponseContext& context)
 {
   ByteString tbsResponseData(ResponseData(context));
   if (ENCODING_FAILED(tbsResponseData)) {
     return ByteString();
   }
 
-  // TODO(bug 980538): certs
-  return SignedData(tbsResponseData, context.signerKeyPair.get(),
-                    context.signatureAlgorithm,
-                    context.badSignature, context.certs);
+  return SignedData(tbsResponseData, *context.signerKeyPair,
+                    context.signatureAlgorithm, context.badSignature,
+                    context.certs);
 }
 
 // Extension ::= SEQUENCE {
 //   id               OBJECT IDENTIFIER,
 //   critical         BOOLEAN DEFAULT FALSE
 //   value            OCTET STRING
 // }
 static ByteString
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -129,16 +129,17 @@ protected:
     , subjectPublicKey(spk)
   {
   }
 
   TestKeyPair(const TestKeyPair&) /*= delete*/;
   void operator=(const TestKeyPair&) /*= delete*/;
 };
 
+TestKeyPair* CloneReusedKeyPair();
 TestKeyPair* GenerateKeyPair();
 inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; }
 typedef ScopedPtr<TestKeyPair, DeleteTestKeyPair> ScopedTestKeyPair;
 
 ByteString SHA1(const ByteString& toHash);
 
 Result TestCheckPublicKey(Input subjectPublicKeyInfo);
 Result TestVerifySignedData(const SignedDataWithSignature& signedData,
@@ -167,29 +168,25 @@ enum Version { v1 = 0, v2 = 1, v3 = 2 };
 // be the same as signatureAlgorithm, which is the algorithm actually used
 // to sign the certificate.
 // serialNumber is assumed to be the DER encoding of an INTEGER.
 //
 // If extensions is null, then no extensions will be encoded. Otherwise,
 // extensions must point to an array of ByteStrings, terminated with an empty
 // ByteString. (If the first item of the array is empty then an empty
 // Extensions sequence will be encoded.)
-//
-// If issuerPrivateKey is null, then the certificate will be self-signed.
-// Parameter order is based on the order of the attributes of the certificate
-// in RFC 5280.
 ByteString CreateEncodedCertificate(long version, const ByteString& signature,
                                     const ByteString& serialNumber,
                                     const ByteString& issuerNameDER,
                                     time_t notBefore, time_t notAfter,
                                     const ByteString& subjectNameDER,
+                                    const TestKeyPair& subjectKeyPair,
                                     /*optional*/ const ByteString* extensions,
-                                    /*optional*/ TestKeyPair* issuerKeyPair,
-                                    const ByteString& signatureAlgorithm,
-                                    /*out*/ ScopedTestKeyPair& keyPairResult);
+                                    const TestKeyPair& issuerKeyPair,
+                                    const ByteString& signatureAlgorithm);
 
 ByteString CreateEncodedSerialNumber(long value);
 
 MOZILLA_PKIX_ENUM_CLASS ExtensionCriticality { NotCritical = 0, Critical = 1 };
 
 ByteString CreateEncodedBasicConstraints(bool isCA,
                                          /*optional*/ long* pathLenConstraint,
                                          ExtensionCriticality criticality);