Bug 1059928: Remove SECOidTag from mozilla::pkix testsuite interface, r=keeler
authorBrian Smith <brian@briansmith.org>
Fri, 29 Aug 2014 16:06:38 -0700
changeset 14670 1c6057955f8a787f571df5c3523787d2b036f348
parent 14669 7aed8e73b3c2db741b99597c1be938be8f89047f
child 14671 956856eaf246f26c26d6c874d7b510a17990e2d7
push id3202
push userfranziskuskiefer@gmail.com
push dateMon, 01 Oct 2018 08:30:12 +0000
reviewerskeeler
bugs1059928
Bug 1059928: Remove SECOidTag from mozilla::pkix testsuite interface, r=keeler
lib/mozpkix/test/gtest/pkixbuild_tests.cpp
lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
lib/mozpkix/test/lib/pkixtestutil.cpp
lib/mozpkix/test/lib/pkixtestutil.h
--- a/lib/mozpkix/test/gtest/pkixbuild_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixbuild_tests.cpp
@@ -60,19 +60,20 @@ CreateCert(PLArenaPool* arena, const cha
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     extensions[0] =
       CreateEncodedBasicConstraints(arena, true, nullptr,
                                     ExtensionCriticality::Critical);
     EXPECT_TRUE(extensions[0]);
   }
 
   SECItem* certDER(CreateEncodedCertificate(
-                     arena, v3, SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
+                     arena, v3, sha256WithRSAEncryption,
                      serialNumber, issuerDER, oneDayBeforeNow, oneDayAfterNow,
-                     subjectDER, extensions, issuerKey, SEC_OID_SHA256,
+                     subjectDER, extensions, issuerKey,
+                     SignatureAlgorithm::rsa_pkcs1_with_sha256,
                      subjectKey));
   EXPECT_TRUE(certDER);
   if (subjectCert) {
     *subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
                                            nullptr, false, true);
     EXPECT_TRUE(*subjectCert);
   }
   Input result;
--- a/lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
@@ -42,22 +42,23 @@ CreateCert(PLArenaPool* arena, const cha
   const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
                                                         serialNumberValue));
   EXPECT_TRUE(serialNumber);
   const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
   EXPECT_TRUE(issuerDER);
   const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
   EXPECT_TRUE(subjectDER);
   SECItem* cert = CreateEncodedCertificate(
-                                  arena, v3,
-                                  SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
+                                  arena, v3, sha256WithRSAEncryption,
                                   serialNumber, issuerDER,
                                   oneDayBeforeNow, oneDayAfterNow,
                                   subjectDER, extensions,
-                                  nullptr, SEC_OID_SHA256, subjectKey);
+                                  nullptr,
+                                  SignatureAlgorithm::rsa_pkcs1_with_sha256,
+                                  subjectKey);
   EXPECT_TRUE(cert);
   Input result;
   EXPECT_EQ(Success, result.Init(cert->data, cert->len));
   return result;
 }
 
 // Creates a self-signed certificate with the given extension.
 static Input
--- a/lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -273,17 +273,16 @@ public:
       EXPECT_TRUE(context.signerNameDER);
     }
     context.signerPrivateKey = SECKEY_CopyPrivateKey(signerPrivateKey.get());
     EXPECT_TRUE(context.signerPrivateKey);
     context.responseStatus = OCSPResponseContext::successful;
     context.producedAt = producedAt;
     context.certs = certs;
 
-    context.certIDHashAlg = SEC_OID_SHA1;
     context.certStatus = certStatus;
     context.thisUpdate = thisUpdate;
     context.nextUpdate = nextUpdate ? *nextUpdate : 0;
     context.includeNextUpdate = nextUpdate != nullptr;
 
     SECItem* response = CreateEncodedOCSPResponse(context);
     EXPECT_TRUE(response);
     Input result;
@@ -384,33 +383,33 @@ protected:
   // have any EKU extension. Otherwise, the certificate will have the given
   // EKU.
   //
   // signerDEROut is owned by the arena
   Input CreateEncodedIndirectOCSPSuccessfulResponse(
               const char* certSubjectName,
               OCSPResponseContext::CertStatus certStatus,
               const char* signerName,
-              SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER,
+              /*optional*/ const Input* signerEKUDER = &OCSPSigningEKUDER,
               /*optional, out*/ Input* signerDEROut = nullptr)
   {
     assert(certSubjectName);
 
     const SECItem* extensions[] = {
-      signerEKU != SEC_OID_UNKNOWN
-        ? CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+      signerEKUDER
+        ? CreateEncodedEKUExtension(arena.get(), *signerEKUDER,
                                     ExtensionCriticality::NotCritical)
         : nullptr,
       nullptr
     };
     ScopedSECKEYPrivateKey signerPrivateKey;
     SECItem* signerDER(CreateEncodedCertificate(
                           arena.get(), ++rootIssuedCount, rootName,
                           oneDayBeforeNow, oneDayAfterNow, certSubjectName,
-                          signerEKU != SEC_OID_UNKNOWN ? extensions : nullptr,
+                          signerEKUDER ? extensions : nullptr,
                           rootPrivateKey.get(), signerPrivateKey));
     EXPECT_TRUE(signerDER);
     if (signerDEROut) {
       EXPECT_EQ(Success,
                 signerDEROut->Init(signerDER->data, signerDER->len));
     }
 
     const SECItem* signerNameDER = nullptr;
@@ -446,23 +445,30 @@ protected:
       return nullptr;
     }
     const SECItem* subjectDER(ASCIIToDERName(arena, subject));
     if (!subjectDER) {
       return nullptr;
     }
     return ::mozilla::pkix::test::CreateEncodedCertificate(
                                     arena, v3,
-                                    SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
+                                    sha256WithRSAEncryption,
                                     serialNumberDER, issuerDER, notBefore,
                                     notAfter, subjectDER, extensions,
-                                    signerKey, SEC_OID_SHA256, privateKey);
+                                    signerKey,
+                                    SignatureAlgorithm::rsa_pkcs1_with_sha256,
+                                    privateKey);
   }
+
+  static const Input OCSPSigningEKUDER;
 };
 
+/*static*/ const Input pkixocsp_VerifyEncodedResponse_DelegatedResponder::
+  OCSPSigningEKUDER(tlv_id_kp_OCSPSigning);
+
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey)
 {
   Input response(CreateEncodedIndirectOCSPSuccessfulResponse(
                          "CN=good_indirect_byKey", OCSPResponseContext::good,
                          byKey));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
@@ -519,21 +525,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_expired)
 {
-  static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   static const char* signerName = "CN=good_indirect_expired";
 
   const SECItem* extensions[] = {
-    CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+    CreateEncodedEKUExtension(arena.get(), OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
 
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                               rootName,
                                               now - (10 * Time::ONE_DAY_IN_SECONDS),
@@ -552,21 +557,20 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_future)
 {
-  static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   static const char* signerName = "CN=good_indirect_future";
 
   const SECItem* extensions[] = {
-    CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+    CreateEncodedEKUExtension(arena.get(), OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
 
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                               rootName,
                                               now + (2 * Time::ONE_DAY_IN_SECONDS),
@@ -588,83 +592,81 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku)
 {
   Input response(CreateEncodedIndirectOCSPSuccessfulResponse(
                          "CN=good_indirect_wrong_eku",
-                         OCSPResponseContext::good, byKey, SEC_OID_UNKNOWN));
+                         OCSPResponseContext::good, byKey, nullptr));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
+static const Input serverAuthEKUDER(tlv_id_kp_serverAuth);
+
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_wrong_eku)
 {
   Input response(CreateEncodedIndirectOCSPSuccessfulResponse(
                         "CN=good_indirect_wrong_eku",
                         OCSPResponseContext::good, byKey,
-                        SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
+                        &serverAuthEKUDER));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 // Test that signature of OCSP response signer cert is verified
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku)
 {
   Input response(CreateEncodedIndirectOCSPSuccessfulResponse(
                          "CN=good_indirect_tampered_eku",
                          OCSPResponseContext::good, byKey,
-                         SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
-
-#define EKU_PREFIX \
-  0x06, 8, /* OBJECT IDENTIFIER, 8 bytes */ \
-  0x2B, 6, 1, 5, 5, 7, /* id-pkix */ \
-  0x03 /* id-kp */
-  static const uint8_t EKU_SERVER_AUTH[] = { EKU_PREFIX, 0x01 }; // serverAuth
-  static const uint8_t EKU_OCSP_SIGNER[] = { EKU_PREFIX, 0x09 }; // OCSPSigning
-#undef EKU_PREFIX
-  SECItem responseSECItem = UnsafeMapInputToSECItem(response);
+                         &serverAuthEKUDER));
+  ByteString tamperedResponse(response.UnsafeGetData(),
+                              response.GetLength());
   ASSERT_EQ(Success,
-            TamperOnce(responseSECItem,
-                       EKU_SERVER_AUTH, sizeof(EKU_SERVER_AUTH),
-                       EKU_OCSP_SIGNER, sizeof(EKU_OCSP_SIGNER)));
-
+            TamperOnce(tamperedResponse,
+                       ByteString(tlv_id_kp_serverAuth,
+                                  sizeof(tlv_id_kp_serverAuth)),
+                       ByteString(tlv_id_kp_OCSPSigning,
+                                  sizeof(tlv_id_kp_OCSPSigning))));
+  Input tamperedResponseInput;
+  ASSERT_EQ(Success, tamperedResponseInput.Init(tamperedResponse.data(),
+                                                tamperedResponse.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      response, expired));
+                                      tamperedResponseInput, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_unknown_issuer)
 {
   static const char* subCAName = "CN=good_indirect_unknown_issuer sub-CA";
   static const char* signerName = "CN=good_indirect_unknown_issuer OCSP signer";
 
   // unknown issuer
   ScopedSECKEYPublicKey unknownPublicKey;
   ScopedSECKEYPrivateKey unknownPrivateKey;
   ASSERT_EQ(Success, GenerateKeyPair(unknownPublicKey, unknownPrivateKey));
 
   // Delegated responder cert signed by unknown issuer
-  static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
-    CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+    CreateEncodedEKUExtension(arena.get(), OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1,
                         subCAName, oneDayBeforeNow, oneDayAfterNow,
                         signerName, extensions, unknownPrivateKey.get(),
                         signerPrivateKey));
@@ -704,19 +706,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                              rootName,
                                              oneDayBeforeNow, oneDayAfterNow,
                                              subCAName, subCAExtensions,
                                              rootPrivateKey.get(),
                                              subCAPrivateKey));
   ASSERT_TRUE(subCADER);
 
   // Delegated responder cert signed by that sub-CA
-  static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
-    CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+    CreateEncodedEKUExtension(arena.get(), OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1, subCAName,
                                               oneDayBeforeNow, oneDayAfterNow,
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
@@ -758,19 +759,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                              rootName,
                                              oneDayBeforeNow, oneDayAfterNow,
                                              subCAName, subCAExtensions,
                                              rootPrivateKey.get(),
                                              subCAPrivateKey));
   ASSERT_TRUE(subCADER);
 
   // Delegated responder cert signed by that sub-CA
-  static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
-    CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
+    CreateEncodedEKUExtension(arena.get(), OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1, subCAName,
                                               oneDayBeforeNow, oneDayAfterNow,
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
@@ -798,17 +798,17 @@ public:
   void SetUp()
   {
     pkixocsp_VerifyEncodedResponse_DelegatedResponder::SetUp();
 
     Input
       createdResponse(
         CreateEncodedIndirectOCSPSuccessfulResponse(
           "CN=OCSPGetCertTrustTest Signer", OCSPResponseContext::good,
-          byKey, SEC_OID_OCSP_RESPONDER, &signerCertDER));
+          byKey, &OCSPSigningEKUDER, &signerCertDER));
     if (response.Init(createdResponse) != Success) {
       abort();
     }
 
     if (response.GetLength() == 0 || signerCertDER.GetLength() == 0) {
       abort();
     }
   }
--- a/lib/mozpkix/test/lib/pkixtestutil.cpp
+++ b/lib/mozpkix/test/lib/pkixtestutil.cpp
@@ -33,23 +33,28 @@
 #include "cryptohi.h"
 #include "hasht.h"
 #include "pk11pub.h"
 #include "pkix/pkixnss.h"
 #include "pkixder.h"
 #include "pkixutil.h"
 #include "prinit.h"
 #include "prprf.h"
-#include "secder.h"
 #include "secerr.h"
 
 using namespace std;
 
 namespace mozilla { namespace pkix { namespace test {
 
+// python DottedOIDToCode.py --alg sha256WithRSAEncryption 1.2.840.113549.1.1.11
+static const uint8_t alg_sha256WithRSAEncryption[] = {
+  0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
+};
+const Input sha256WithRSAEncryption(alg_sha256WithRSAEncryption);
+
 namespace {
 
 inline void
 deleteCharArray(char* chars)
 {
   delete[] chars;
 }
 
@@ -92,60 +97,34 @@ OpenFile(const char* dir, const char* fi
   file = fopen(path.get(), mode);
 #endif
   return file.release();
 }
 
 } // unnamed namespace
 
 Result
-TamperOnce(SECItem& item,
-           const uint8_t* from, size_t fromLen,
-           const uint8_t* to, size_t toLen)
+TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
+           const ByteString& to)
 {
-  if (!item.data || !from || !to || fromLen != toLen) {
+  if (from.length() < 8) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
-
-  if (fromLen < 8) {
+  if (from.length() != to.length()) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
-
-  uint8_t* p = item.data;
-  size_t remaining = item.len;
-  bool alreadyFoundMatch = false;
-  for (;;) {
-    uint8_t* foundFirstByte = static_cast<uint8_t*>(memchr(p, from[0],
-                                                           remaining));
-    if (!foundFirstByte) {
-      if (alreadyFoundMatch) {
-        return Success;
-      }
-      return Result::FATAL_ERROR_INVALID_ARGS;
-    }
-    remaining -= (foundFirstByte - p);
-    if (remaining < fromLen) {
-      if (alreadyFoundMatch) {
-        return Success;
-      }
-      return Result::FATAL_ERROR_INVALID_ARGS;
-    }
-    if (!memcmp(foundFirstByte, from, fromLen)) {
-      if (alreadyFoundMatch) {
-        return Result::FATAL_ERROR_INVALID_ARGS;
-      }
-      alreadyFoundMatch = true;
-      memmove(foundFirstByte, to, toLen);
-      p = foundFirstByte + toLen;
-      remaining -= toLen;
-    } else {
-      p = foundFirstByte + 1;
-      --remaining;
-    }
+  size_t pos = item.find(from);
+  if (pos == string::npos) {
+    return Result::FATAL_ERROR_INVALID_ARGS; // No matches.
   }
+  if (item.find(from, pos + from.length()) != string::npos) {
+    return Result::FATAL_ERROR_INVALID_ARGS; // More than once match.
+  }
+  item.replace(pos, from.length(), to);
+  return Success;
 }
 
 Result
 InitInputFromSECItem(const SECItem* secItem, /*out*/ Input& input)
 {
   if (!secItem) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
@@ -243,17 +222,16 @@ OCSPResponseContext::OCSPResponseContext
   , skipResponseBytes(false)
   , signerNameDER(nullptr)
   , producedAt(time)
   , extensions(nullptr)
   , includeEmptyExtensions(false)
   , badSignature(false)
   , certs(nullptr)
 
-  , certIDHashAlg(SEC_OID_SHA1)
   , certStatus(good)
   , revocationTime(0)
   , thisUpdate(time)
   , nextUpdate(time + 10)
   , includeNextUpdate(true)
 {
 }
 
@@ -271,84 +249,42 @@ EncodeNested(PLArenaPool* arena, uint8_t
 {
   Output output;
   if (output.Add(inner) != Success) {
     return nullptr;
   }
   return output.Squash(arena, tag);
 }
 
-// A return value of 0 is an error, but this should never happen in practice
-// because this function aborts in that case.
-static size_t
-HashAlgorithmToLength(SECOidTag hashAlg)
+static SECItem*
+HashedOctetString(PLArenaPool* arena, const SECItem& bytes)
 {
-  switch (hashAlg) {
-    case SEC_OID_SHA1:
-      return SHA1_LENGTH;
-    case SEC_OID_SHA256:
-      return SHA256_LENGTH;
-    case SEC_OID_SHA384:
-      return SHA384_LENGTH;
-    case SEC_OID_SHA512:
-      return SHA512_LENGTH;
-    default:
-      abort();
-  }
-  return 0;
-}
-
-static SECItem*
-HashedOctetString(PLArenaPool* arena, const SECItem& bytes, SECOidTag hashAlg)
-{
-  size_t hashLen = HashAlgorithmToLength(hashAlg);
-  if (hashLen == 0) {
-    return nullptr;
-  }
-  SECItem* hashBuf = SECITEM_AllocItem(arena, nullptr, hashLen);
+  SECItem* hashBuf = SECITEM_AllocItem(arena, nullptr, SHA1_LENGTH);
   if (!hashBuf) {
     return nullptr;
   }
-  if (PK11_HashBuf(hashAlg, hashBuf->data, bytes.data, bytes.len)
-        != SECSuccess) {
+
+  Input input;
+  if (input.Init(bytes.data, bytes.len) != Success) {
+    return nullptr;
+  }
+  if (DigestBuf(input, hashBuf->data, hashBuf->len) != Success) {
     return nullptr;
   }
 
   return EncodeNested(arena, der::OCTET_STRING, hashBuf);
 }
 
 static SECItem*
 KeyHashHelper(PLArenaPool* arena, const CERTSubjectPublicKeyInfo* spki)
 {
   // We only need a shallow copy here.
   SECItem spk = spki->subjectPublicKey;
   DER_ConvertBitString(&spk); // bits to bytes
-  return HashedOctetString(arena, spk, SEC_OID_SHA1);
-}
-
-static SECItem*
-AlgorithmIdentifier(PLArenaPool* arena, SECOidTag algTag)
-{
-  SECAlgorithmIDStr aid;
-  aid.algorithm.data = nullptr;
-  aid.algorithm.len = 0;
-  aid.parameters.data = nullptr;
-  aid.parameters.len = 0;
-  if (SECOID_SetAlgorithmID(arena, &aid, algTag, nullptr) != SECSuccess) {
-    return nullptr;
-  }
-  static const SEC_ASN1Template algorithmIDTemplate[] = {
-    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECAlgorithmID) },
-    { SEC_ASN1_OBJECT_ID, offsetof(SECAlgorithmID, algorithm) },
-    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, offsetof(SECAlgorithmID, parameters) },
-    { 0 }
-  };
-  SECItem* algorithmID = SEC_ASN1EncodeItem(arena, nullptr, &aid,
-                                            algorithmIDTemplate);
-  return algorithmID;
+  return HashedOctetString(arena, spk);
 }
 
 static SECItem*
 BitString(PLArenaPool* arena, const SECItem* rawBytes, bool corrupt)
 {
   // We have to add a byte at the beginning indicating no unused bits.
   // TODO: add ability to have bit strings of bit length not divisible by 8,
   // resulting in unused bits in the bitstring encoding
@@ -392,26 +328,16 @@ Integer(PLArenaPool* arena, long value)
     return nullptr;
   }
   encoded->data[0] = der::INTEGER;
   encoded->data[1] = 1; // length
   encoded->data[2] = value;
   return encoded;
 }
 
-static SECItem*
-OID(PLArenaPool* arena, SECOidTag tag)
-{
-  const SECOidData* extnIDData(SECOID_FindOIDByTag(tag));
-  if (!extnIDData) {
-    return nullptr;
-  }
-  return EncodeNested(arena, der::OIDTag, &extnIDData->oid);
-}
-
 enum TimeEncoding { UTCTime = 0, GeneralizedTime = 1 };
 
 // Windows doesn't provide gmtime_r, but it provides something very similar.
 #ifdef WIN32
 static tm*
 gmtime_r(const time_t* t, /*out*/ tm* exploded)
 {
   if (gmtime_s(exploded, t) != 0) {
@@ -553,41 +479,47 @@ YMDHMS(int16_t year, int16_t month, int1
   totalSeconds += hour * 60 * 60;
   totalSeconds += minutes * 60;
   totalSeconds += seconds;
   return TimeFromElapsedSecondsAD(totalSeconds);
 }
 
 static SECItem*
 SignedData(PLArenaPool* arena, const SECItem* tbsData,
-           SECKEYPrivateKey* privKey, SECOidTag hashAlg,
+           SECKEYPrivateKey* privKey,
+           SignatureAlgorithm signatureAlgorithm,
            bool corrupt, /*optional*/ SECItem const* const* certs)
 {
   assert(arena);
   assert(tbsData);
   assert(privKey);
   if (!arena || !tbsData || !privKey) {
     return nullptr;
   }
 
-  SECOidTag signatureAlgTag = SEC_GetSignatureAlgorithmOidTag(privKey->keyType,
-                                                              hashAlg);
-  if (signatureAlgTag == SEC_OID_UNKNOWN) {
-    return nullptr;
-  }
-  SECItem* signatureAlgorithm = AlgorithmIdentifier(arena, signatureAlgTag);
-  if (!signatureAlgorithm) {
-    return nullptr;
+  SECOidTag signatureAlgorithmOidTag;
+  Input signatureAlgorithmDER;
+  switch (signatureAlgorithm) {
+    case SignatureAlgorithm::rsa_pkcs1_with_sha256:
+      signatureAlgorithmOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
+      if (signatureAlgorithmDER.Init(alg_sha256WithRSAEncryption,
+                                     sizeof(alg_sha256WithRSAEncryption))
+            != Success) {
+        return nullptr;
+      }
+      break;
+    default:
+      return nullptr;
   }
 
   // SEC_SignData doesn't take an arena parameter, so we have to manage
   // the memory allocated in signature.
   SECItem signature;
   if (SEC_SignData(&signature, tbsData->data, tbsData->len, privKey,
-                   signatureAlgTag) != SECSuccess)
+                   signatureAlgorithmOidTag) != SECSuccess)
   {
     return nullptr;
   }
   // TODO: add ability to have signatures of bit length not divisible by 8,
   // resulting in unused bits in the bitstring encoding
   SECItem* signatureNested = BitString(arena, &signature, corrupt);
   SECITEM_FreeItem(&signature, false);
   if (!signatureNested) {
@@ -612,17 +544,20 @@ SignedData(PLArenaPool* arena, const SEC
       return nullptr;
     }
   }
 
   Output output;
   if (output.Add(tbsData) != Success) {
     return nullptr;
   }
-  if (output.Add(signatureAlgorithm) != Success) {
+
+  SECItem sigantureAlgorithmDERItem =
+    UnsafeMapInputToSECItem(signatureAlgorithmDER);
+  if (output.Add(&sigantureAlgorithmDERItem) != Success) {
     return nullptr;
   }
   if (output.Add(signatureNested) != Success) {
     return nullptr;
   }
   if (certsNested) {
     if (output.Add(certsNested) != Success) {
       return nullptr;
@@ -635,31 +570,28 @@ SignedData(PLArenaPool* arena, const SEC
 //      extnID      OBJECT IDENTIFIER,
 //      critical    BOOLEAN DEFAULT FALSE,
 //      extnValue   OCTET STRING
 //                  -- contains the DER encoding of an ASN.1 value
 //                  -- corresponding to the extension type identified
 //                  -- by extnID
 //      }
 static SECItem*
-Extension(PLArenaPool* arena, SECOidTag extnIDTag,
+Extension(PLArenaPool* arena, Input extnID,
           ExtensionCriticality criticality, Output& value)
 {
   assert(arena);
   if (!arena) {
     return nullptr;
   }
 
   Output output;
 
-  const SECItem* extnID(OID(arena, extnIDTag));
-  if (!extnID) {
-    return nullptr;
-  }
-  if (output.Add(extnID) != Success) {
+  const SECItem extnIDItem = UnsafeMapInputToSECItem(extnID);
+  if (output.Add(&extnIDItem) != Success) {
     return nullptr;
   }
 
   if (criticality == ExtensionCriticality::Critical) {
     SECItem* critical(Boolean(arena, true));
     if (output.Add(critical) != Success) {
       return nullptr;
     }
@@ -757,34 +689,34 @@ GenerateKeyPair(/*out*/ ScopedSECKEYPubl
   return MapPRErrorCodeToResult(PR_GetError());
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // Certificates
 
 static SECItem* TBSCertificate(PLArenaPool* arena, long version,
-                               const SECItem* serialNumber, SECOidTag signature,
+                               const SECItem* serialNumber, Input signature,
                                const SECItem* issuer, time_t notBefore,
                                time_t notAfter, const SECItem* subject,
                                const SECKEYPublicKey* subjectPublicKey,
                                /*optional*/ SECItem const* const* extensions);
 
 // Certificate  ::=  SEQUENCE  {
 //         tbsCertificate       TBSCertificate,
 //         signatureAlgorithm   AlgorithmIdentifier,
 //         signatureValue       BIT STRING  }
 SECItem*
 CreateEncodedCertificate(PLArenaPool* arena, long version,
-                         SECOidTag signature, const SECItem* serialNumber,
+                         Input signature, const SECItem* serialNumber,
                          const SECItem* issuerNameDER, time_t notBefore,
                          time_t notAfter, const SECItem* subjectNameDER,
                          /*optional*/ SECItem const* const* extensions,
                          /*optional*/ SECKEYPrivateKey* issuerPrivateKey,
-                         SECOidTag signatureHashAlg,
+                         SignatureAlgorithm signatureAlgorithm,
                          /*out*/ ScopedSECKEYPrivateKey& privateKeyResult)
 {
   assert(arena);
   assert(issuerNameDER);
   assert(subjectNameDER);
   if (!arena || !issuerNameDER || !subjectNameDER) {
     return nullptr;
   }
@@ -805,17 +737,17 @@ CreateEncodedCertificate(PLArenaPool* ar
   if (!tbsCertificate) {
     return nullptr;
   }
 
   SECItem*
     result(MaybeLogOutput(SignedData(arena, tbsCertificate,
                                      issuerPrivateKey ? issuerPrivateKey
                                                       : privateKeyTemp.get(),
-                                     signatureHashAlg, false, nullptr),
+                                     signatureAlgorithm, false, nullptr),
                           "cert"));
   if (!result) {
     return nullptr;
   }
   privateKeyResult = privateKeyTemp.release();
   return result;
 }
 
@@ -830,17 +762,17 @@ CreateEncodedCertificate(PLArenaPool* ar
 //      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]  Extensions OPTIONAL
 //                           -- If present, version MUST be v3 --  }
 static SECItem*
 TBSCertificate(PLArenaPool* arena, long versionValue,
-               const SECItem* serialNumber, SECOidTag signatureOidTag,
+               const SECItem* serialNumber, Input signature,
                const SECItem* issuer, time_t notBeforeTime,
                time_t notAfterTime, const SECItem* subject,
                const SECKEYPublicKey* subjectPublicKey,
                /*optional*/ SECItem const* const* extensions)
 {
   assert(arena);
   assert(issuer);
   assert(subject);
@@ -866,21 +798,18 @@ TBSCertificate(PLArenaPool* arena, long 
       return nullptr;
     }
   }
 
   if (output.Add(serialNumber) != Success) {
     return nullptr;
   }
 
-  SECItem* signature(AlgorithmIdentifier(arena, signatureOidTag));
-  if (!signature) {
-    return nullptr;
-  }
-  if (output.Add(signature) != Success) {
+  SECItem signatureItem = UnsafeMapInputToSECItem(signature);
+  if (output.Add(&signatureItem) != Success) {
     return nullptr;
   }
 
   if (output.Add(issuer) != Success) {
     return nullptr;
   }
 
   // Validity ::= SEQUENCE {
@@ -997,43 +926,44 @@ CreateEncodedBasicConstraints(PLArenaPoo
     if (!pathLenConstraint) {
       return nullptr;
     }
     if (value.Add(pathLenConstraint) != Success) {
       return nullptr;
     }
   }
 
-  return Extension(arena, SEC_OID_X509_BASIC_CONSTRAINTS, criticality, value);
+  // python DottedOIDToCode.py --tlv id-ce-basicConstraints 2.5.29.19
+  static const uint8_t tlv_id_ce_basicConstraints[] = {
+    0x06, 0x03, 0x55, 0x1d, 0x13
+  };
+
+  return Extension(arena, Input(tlv_id_ce_basicConstraints), criticality, value);
 }
 
 // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
 // KeyPurposeId ::= OBJECT IDENTIFIER
 SECItem*
-CreateEncodedEKUExtension(PLArenaPool* arena, SECOidTag const* ekus,
-                          size_t ekusCount, ExtensionCriticality criticality)
+CreateEncodedEKUExtension(PLArenaPool* arena, Input ekuOID,
+                          ExtensionCriticality criticality)
 {
   assert(arena);
-  assert(ekus);
-  if (!arena || (!ekus && ekusCount != 0)) {
+
+  Output value;
+  SECItem ekuOIDItem = UnsafeMapInputToSECItem(ekuOID);
+  if (value.Add(&ekuOIDItem) != Success) {
     return nullptr;
   }
 
-  Output value;
-  for (size_t i = 0; i < ekusCount; ++i) {
-    SECItem* encodedEKUOID = OID(arena, ekus[i]);
-    if (!encodedEKUOID) {
-      return nullptr;
-    }
-    if (value.Add(encodedEKUOID) != Success) {
-      return nullptr;
-    }
-  }
+  // python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
+  static const uint8_t tlv_id_ce_extKeyUsage[] = {
+    0x06, 0x03, 0x55, 0x1d, 0x25
+  };
 
-  return Extension(arena, SEC_OID_X509_EXT_KEY_USAGE, criticality, value);
+  return Extension(arena, Input(tlv_id_ce_extKeyUsage), criticality, value);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // OCSP responses
 
 SECItem*
 CreateEncodedOCSPResponse(OCSPResponseContext& context)
 {
@@ -1141,17 +1071,18 @@ BasicOCSPResponse(OCSPResponseContext& c
 {
   SECItem* tbsResponseData = ResponseData(context);
   if (!tbsResponseData) {
     return nullptr;
   }
 
   // TODO(bug 980538): certs
   return SignedData(context.arena, tbsResponseData,
-                    context.signerPrivateKey.get(), SEC_OID_SHA1,
+                    context.signerPrivateKey.get(),
+                    SignatureAlgorithm::rsa_pkcs1_with_sha256,
                     context.badSignature, context.certs);
 }
 
 // Extension ::= SEQUENCE {
 //   id               OBJECT IDENTIFIER,
 //   critical         BOOLEAN DEFAULT FALSE
 //   value            OCTET STRING
 // }
@@ -1370,24 +1301,18 @@ SingleResponse(OCSPResponseContext& cont
 // CertID          ::=     SEQUENCE {
 //        hashAlgorithm       AlgorithmIdentifier,
 //        issuerNameHash      OCTET STRING, -- Hash of issuer's DN
 //        issuerKeyHash       OCTET STRING, -- Hash of issuer's public key
 //        serialNumber        CertificateSerialNumber }
 SECItem*
 CertID(OCSPResponseContext& context)
 {
-  SECItem* hashAlgorithm = AlgorithmIdentifier(context.arena,
-                                               context.certIDHashAlg);
-  if (!hashAlgorithm) {
-    return nullptr;
-  }
   SECItem issuerSECItem = UnsafeMapInputToSECItem(context.certID.issuer);
-  SECItem* issuerNameHash = HashedOctetString(context.arena, issuerSECItem,
-                                              context.certIDHashAlg);
+  SECItem* issuerNameHash = HashedOctetString(context.arena, issuerSECItem);
   if (!issuerNameHash) {
     return nullptr;
   }
 
   SECItem issuerSubjectPublicKeyInfoSECItem =
     UnsafeMapInputToSECItem(context.certID.issuerSubjectPublicKeyInfo);
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
     spki(SECKEY_DecodeDERSubjectPublicKeyInfo(
@@ -1409,17 +1334,28 @@ CertID(OCSPResponseContext& context)
   SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr,
                                              &serialNumberSECItem,
                                              serialTemplate);
   if (!serialNumber) {
     return nullptr;
   }
 
   Output output;
-  if (output.Add(hashAlgorithm) != Success) {
+
+  // python DottedOIDToCode.py --alg id-sha1 1.3.14.3.2.26
+  static const uint8_t alg_id_sha1[] = {
+    0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a
+  };
+  static const SECItem id_sha1 = {
+    siBuffer,
+    const_cast<uint8_t*>(alg_id_sha1),
+    sizeof(alg_id_sha1)
+  };
+
+  if (output.Add(&id_sha1) != Success) {
     return nullptr;
   }
   if (output.Add(issuerNameHash) != Success) {
     return nullptr;
   }
   if (output.Add(issuerKeyHash) != Success) {
     return nullptr;
   }
--- a/lib/mozpkix/test/lib/pkixtestutil.h
+++ b/lib/mozpkix/test/lib/pkixtestutil.h
@@ -21,26 +21,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix_test__pkixtestutils_h
 #define mozilla_pkix_test__pkixtestutils_h
 
 #include <ctime>
-#include <stdint.h>
+#include <stdint.h> // Some Mozilla-supported compilers lack <cstdint>
+#include <string>
 
 #include "keyhi.h"
 #include "pkix/enumclass.h"
 #include "pkix/pkixtypes.h"
 #include "pkix/ScopedPtr.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
+typedef std::basic_string<uint8_t> ByteString;
+
 // XXX: Ideally, we should define this instead:
 //
 //   template <typename T, std::size_t N>
 //   constexpr inline std::size_t
 //   ArrayLength(T (&)[N])
 //   {
 //     return N;
 //   }
@@ -72,84 +75,92 @@ SECITEM_FreeItem_true(SECItem* item)
 } // unnamed namespace
 
 typedef mozilla::pkix::ScopedPtr<SECItem, SECITEM_FreeItem_true> ScopedSECItem;
 typedef mozilla::pkix::ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
   ScopedSECKEYPublicKey;
 typedef mozilla::pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
   ScopedSECKEYPrivateKey;
 
+// python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9
+static const uint8_t tlv_id_kp_OCSPSigning[] = {
+  0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09
+};
+
+// python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1
+static const uint8_t tlv_id_kp_serverAuth[] = {
+  0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01
+};
+
+extern const Input sha256WithRSAEncryption;
+
 // e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT)
 mozilla::pkix::Time YMDHMS(int16_t year, int16_t month, int16_t day,
                            int16_t hour, int16_t minutes, int16_t seconds);
 
 Result GenerateKeyPair(/*out*/ ScopedSECKEYPublicKey& publicKey,
                        /*out*/ ScopedSECKEYPrivateKey& privateKey);
 
 // The result will be owned by the arena
 const SECItem* ASCIIToDERName(PLArenaPool* arena, const char* cn);
 
 // Replace one substring in item with another of the same length, but only if
-// the substring was found exactly once. The "only once" restriction is helpful
-// for avoiding making multiple changes at once.
+// the substring was found exactly once. The "same length" restriction is
+// useful for avoiding invalidating lengths encoded within the item. The
+// "only once" restriction is helpful for avoiding making accidental changes.
 //
 // The string to search for must be 8 or more bytes long so that it is
 // extremely unlikely that there will ever be any false positive matches
 // in digital signatures, keys, hashes, etc.
-//
-// Returns true on success, false on failure.
-Result TamperOnce(SECItem& item, const uint8_t* from, size_t fromLen,
-                  const uint8_t* to, size_t toLen);
+Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
+                  const ByteString& to);
 
 Result InitInputFromSECItem(const SECItem* secItem, /*out*/ Input& input);
 
 ///////////////////////////////////////////////////////////////////////////////
 // Encode Certificates
 
 enum Version { v1 = 0, v2 = 1, v3 = 2 };
 
+// signature is assumed to be the DER encoding of an AlgorithmIdentifer.
 // 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 a null-terminated array of SECItem*. If the first
 // item of the array is null 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.
 //
 // The return value, if non-null, is owned by the arena in the context and
 // MUST NOT be freed.
 SECItem* CreateEncodedCertificate(PLArenaPool* arena, long version,
-                                  SECOidTag signature,
+                                  Input signature,
                                   const SECItem* serialNumber,
                                   const SECItem* issuerNameDER,
                                   std::time_t notBefore, std::time_t notAfter,
                                   const SECItem* subjectNameDER,
                      /*optional*/ SECItem const* const* extensions,
                      /*optional*/ SECKEYPrivateKey* issuerPrivateKey,
-                                  SECOidTag signatureHashAlg,
+                                  SignatureAlgorithm signatureAlgorithm,
                           /*out*/ ScopedSECKEYPrivateKey& privateKey);
 
 SECItem* CreateEncodedSerialNumber(PLArenaPool* arena, long value);
 
 MOZILLA_PKIX_ENUM_CLASS ExtensionCriticality { NotCritical = 0, Critical = 1 };
 
 // The return value, if non-null, is owned by the arena and MUST NOT be freed.
 SECItem* CreateEncodedBasicConstraints(PLArenaPool* arena, bool isCA,
                                        /*optional*/ long* pathLenConstraint,
                                        ExtensionCriticality criticality);
 
-// ekus must be non-null and must must point to a SEC_OID_UNKNOWN-terminated
-// array of SECOidTags. If the first item of the array is SEC_OID_UNKNOWN then
-// an empty EKU extension will be encoded.
-//
-// The return value, if non-null, is owned by the arena and MUST NOT be freed.
-SECItem* CreateEncodedEKUExtension(PLArenaPool* arena,
-                                   const SECOidTag* ekus, size_t ekusCount,
+// Creates a DER-encoded extKeyUsage extension with one EKU OID. The return
+// value, if non-null, is owned by the arena and MUST NOT be freed.
+SECItem* CreateEncodedEKUExtension(PLArenaPool* arena, Input eku,
                                    ExtensionCriticality criticality);
 
 ///////////////////////////////////////////////////////////////////////////////
 // Encode OCSP responses
 
 class OCSPResponseExtension
 {
 public:
@@ -194,17 +205,16 @@ public:
                                // regardless of if there are any actual
                                // extensions.
   ScopedSECKEYPrivateKey signerPrivateKey;
   bool badSignature; // If true, alter the signature to fail verification
   SECItem const* const* certs; // non-owning pointer to certs to embed
 
   // The following fields are on a per-SingleResponse basis. In the future we
   // may support including multiple SingleResponses per response.
-  SECOidTag certIDHashAlg;
   enum CertStatus {
     good = 0,
     revoked = 1,
     unknown = 2,
   };
   uint8_t certStatus; // CertStatus or an invalid value
   std::time_t revocationTime; // For certStatus == revoked
   std::time_t thisUpdate;