Bug 1041186, Part 1: Improve buffer overflow protection in mozilla::pkix, r=keeler
authorBrian Smith <brian@briansmith.org>
Fri, 18 Jul 2014 22:30:51 -0700
changeset 14637 8c4c865dffb1f8bfd5a49073b2e29fe86e61c9d4
parent 14636 028a548273ee208df724770232f0e1658052f0f0
child 14638 be95f831b8156779b5cf9cd763c64601751d01bf
push id3202
push userfranziskuskiefer@gmail.com
push dateMon, 01 Oct 2018 08:30:12 +0000
reviewerskeeler
bugs1041186
Bug 1041186, Part 1: Improve buffer overflow protection in mozilla::pkix, r=keeler
lib/mozpkix/include/pkix/Input.h
lib/mozpkix/include/pkix/pkix.h
lib/mozpkix/include/pkix/pkixnss.h
lib/mozpkix/include/pkix/pkixtypes.h
lib/mozpkix/lib/pkixbuild.cpp
lib/mozpkix/lib/pkixcert.cpp
lib/mozpkix/lib/pkixcheck.cpp
lib/mozpkix/lib/pkixcheck.h
lib/mozpkix/lib/pkixder.cpp
lib/mozpkix/lib/pkixder.h
lib/mozpkix/lib/pkixnss.cpp
lib/mozpkix/lib/pkixocsp.cpp
lib/mozpkix/lib/pkixutil.h
lib/mozpkix/test/gtest/pkixbuild_tests.cpp
lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
lib/mozpkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
lib/mozpkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
lib/mozpkix/test/gtest/pkixder_input_tests.cpp
lib/mozpkix/test/gtest/pkixder_pki_types_tests.cpp
lib/mozpkix/test/gtest/pkixder_universal_types_tests.cpp
lib/mozpkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
lib/mozpkix/test/lib/pkixtestutil.cpp
lib/mozpkix/test/lib/pkixtestutil.h
--- a/lib/mozpkix/include/pkix/Input.h
+++ b/lib/mozpkix/include/pkix/Input.h
@@ -20,73 +20,145 @@
  * 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.
  */
 
 #ifndef mozilla_pkix__Input_h
 #define mozilla_pkix__Input_h
 
+#include <cstring>
+
 #include "pkix/nullptr.h"
 #include "pkix/Result.h"
-#include "seccomon.h"
+#include "prlog.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
-// Expect* functions advance the input mark and return Success if the input
-// matches the given criteria; they fail with the input mark in an undefined
-// state if the input does not match the criteria.
+class Input;
+
+// An InputBuffer is a safety-oriented immutable weak reference to a array of
+// bytes of a known size. The data can only be legally accessed by constructing
+// an Input object, which guarantees all accesses to the data are memory safe.
+// Neither InputBuffer not Input provide any facilities for modifying the data
+// they reference.
+//
+// InputBuffers are small and should usually be passed by value, not by
+// reference, though for inline functions the distinction doesn't matter.
+//
+//    Result GoodExample(InputBuffer input);
+//    Result BadExample(const InputBuffer& input);
+//    Result WorseExample(const uint8_t* input, size_t len);
 //
-// Match* functions advance the input mark and return true if the input matches
-// the given criteria; they return false without changing the input mark if the
-// input does not match the criteria.
+// Note that in the example, GoodExample has the same performance
+// characteristics as WorseExample, but with much better safety guarantees.
+class InputBuffer
+{
+public:
+  // This constructor is useful for input buffers that are statically known to
+  // be of a fixed size, e.g.:
+  //
+  //   static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 };
+  //   const InputBuffer expected(EXPECTED_BYTES);
+  //
+  // This is equivalent to (and preferred over):
+  //
+  //   static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 };
+  //   InputBuffer expected;
+  //   Result rv = expected.Init(EXPECTED_BYTES, sizeof EXPECTED_BYTES);
+  template <uint16_t N>
+  explicit InputBuffer(const uint8_t (&data)[N])
+    : data(data)
+    , len(N)
+  {
+  }
+
+  // Construct a valid, empty, Init-able InputBuffer.
+  InputBuffer()
+    : data(nullptr)
+    , len(0u)
+  {
+  }
+
+  // Initialize the input buffer. data must be non-null and len must be less
+  // than 65536. Init may not be called more than once.
+  Result Init(const uint8_t* data, size_t len)
+  {
+    if (this->data) {
+      // already initialized
+      return Result::FATAL_ERROR_INVALID_ARGS;
+    }
+    if (!data || len > 0xffffu) {
+      // input too large
+      return Result::ERROR_BAD_DER;
+    }
+
+    this->data = data;
+    this->len = len;
+
+    return Success;
+  }
+
+  // Initialize the input buffer to be equivalent to the given input buffer.
+  // Init may not be called more than once.
+  //
+  // This is basically operator=, but it wasn't given that name because
+  // normally callers do not check the result of operator=, and normally
+  // operator= can be used multiple times.
+  Result Init(InputBuffer other)
+  {
+    return Init(other.data, other.len);
+  }
+
+  // Returns the length of the buffer.
+  //
+  // Having the return type be uint16_t instead of size_t avoids the need for
+  // callers to ensure that the result is small enough.
+  uint16_t GetLength() const { return static_cast<uint16_t>(len); }
+
+  // Don't use this. It is here because we have some "friend" functions that we
+  // don't want to declare in this header file.
+  const uint8_t* UnsafeGetData() const { return data; }
+
+private:
+  const uint8_t* data;
+  size_t len;
+
+  void operator=(const InputBuffer&) /* = delete */; // Use Init instead.
+};
+
+inline bool
+InputBuffersAreEqual(const InputBuffer& a, const InputBuffer& b)
+{
+  return a.GetLength() == b.GetLength() &&
+         !std::memcmp(a.UnsafeGetData(), b.UnsafeGetData(), a.GetLength());
+}
+
+// An Input is a cursor/iterator through the contents of an InputBuffer,
+// designed to maximize safety during parsing while minimizing the performance
+// cost of that safety. In particular, all methods do strict bounds checking to
+// ensure buffer overflows are impossible, and they are all inline so that the
+// compiler can coalesce as many of those checks together as possible.
 //
-// Skip* functions unconditionally advance the input mark and return Success if
-// they are able to do so; otherwise they fail with the input mark in an
-// undefined state.
+// In general, Input allows for one byte of lookahead and no backtracking.
+// However, the Match* functions internally may have more lookahead.
 class Input
 {
 public:
   Input()
     : input(nullptr)
     , end(nullptr)
   {
   }
 
-  Result Init(const uint8_t* data, size_t len)
+  explicit Input(InputBuffer buffer)
+    : input(buffer.UnsafeGetData())
+    , end(buffer.UnsafeGetData() + buffer.GetLength())
   {
-    if (input) {
-      // already initialized
-      return Result::FATAL_ERROR_INVALID_ARGS;
-    }
-    if (!data || len > 0xffffu) {
-      // input too large
-      return Result::ERROR_BAD_DER;
-    }
-
-    // XXX: this->input = input bug was not caught by tests! Why not?
-    //      this->end = end bug was not caught by tests! Why not?
-    this->input = data;
-    this->end = data + len;
-
-    return Success;
-  }
-
-  Result Expect(const uint8_t* expected, uint16_t expectedLen)
-  {
-    Result rv = EnsureLength(expectedLen);
-    if (rv != Success) {
-      return rv;
-    }
-    if (memcmp(input, expected, expectedLen)) {
-      return Result::ERROR_BAD_DER;
-    }
-    input += expectedLen;
-    return Success;
   }
 
   bool Peek(uint8_t expectedByte) const
   {
     return input < end && *input == expectedByte;
   }
 
   Result Read(uint8_t& out)
@@ -171,25 +243,26 @@ public:
     rv = skippedInput.Init(input, len);
     if (rv != Success) {
       return rv;
     }
     input += len;
     return Success;
   }
 
-  Result Skip(uint16_t len, SECItem& skippedItem)
+  Result Skip(uint16_t len, InputBuffer& skippedItem)
   {
     Result rv = EnsureLength(len);
     if (rv != Success) {
       return rv;
     }
-    skippedItem.type = siBuffer;
-    skippedItem.data = const_cast<uint8_t*>(input);
-    skippedItem.len = len;
+    rv = skippedItem.Init(input, len);
+    if (rv != Success) {
+      return rv;
+    }
     input += len;
     return Success;
   }
 
   void SkipToEnd()
   {
     input = end;
   }
@@ -211,29 +284,37 @@ public:
     Mark(const Input& input, const uint8_t* mark) : input(input), mark(mark) { }
     const Input& input;
     const uint8_t* const mark;
     void operator=(const Mark&) /* = delete */;
   };
 
   Mark GetMark() const { return Mark(*this, input); }
 
-  Result GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item)
+  Result GetInputBuffer(const Mark& mark, /*out*/ InputBuffer& item)
   {
     if (&mark.input != this || mark.mark > input) {
       PR_NOT_REACHED("invalid mark");
       return Result::FATAL_ERROR_INVALID_ARGS;
     }
-    item.type = type;
-    item.data = const_cast<uint8_t*>(mark.mark);
-    item.len = static_cast<decltype(item.len)>(input - mark.mark);
+    return item.Init(mark.mark, static_cast<uint16_t>(input - mark.mark));
+  }
+
+private:
+  Result Init(const uint8_t* data, uint16_t len)
+  {
+    if (input) {
+      // already initialized
+      return Result::FATAL_ERROR_INVALID_ARGS;
+    }
+    input = data;
+    end = data + len;
     return Success;
   }
 
-private:
   const uint8_t* input;
   const uint8_t* end;
 
   Input(const Input&) /* = delete */;
   void operator=(const Input&) /* = delete */;
 };
 
 } } // namespace mozilla::pkix
--- a/lib/mozpkix/include/pkix/pkix.h
+++ b/lib/mozpkix/include/pkix/pkix.h
@@ -83,22 +83,22 @@ namespace mozilla { namespace pkix {
 // Meaning of specific error codes
 //
 // Result::ERROR_UNTRUSTED_CERT means that the end-entity certificate was
 //                              actively distrusted.
 // Result::SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because
 //                                    of active distrust.
 // TODO(bug 968451): Document more of these.
 
-Result BuildCertChain(TrustDomain& trustDomain, const SECItem& cert,
+Result BuildCertChain(TrustDomain& trustDomain, InputBuffer cert,
                       PRTime time, EndEntityOrCA endEntityOrCA,
                       KeyUsage requiredKeyUsageIfPresent,
                       KeyPurposeId requiredEKUIfPresent,
                       const CertPolicyId& requiredPolicy,
-                      /*optional*/ const SECItem* stapledOCSPResponse);
+                      /*optional*/ const InputBuffer* stapledOCSPResponse);
 
 static const size_t OCSP_REQUEST_MAX_LENGTH = 127;
 Result CreateEncodedOCSPRequest(TrustDomain& trustDomain,
                                 const struct CertID& certID,
                                 /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH],
                                 /*out*/ size_t& outLen);
 
 // The out parameter expired will be true if the response has expired. If the
@@ -111,16 +111,16 @@ Result CreateEncodedOCSPRequest(TrustDom
 // good, unknown, or revoked responses that verify correctly are considered
 // trustworthy. If the response is not trustworthy, thisUpdate will be 0.
 // Similarly, the optional parameter validThrough will be the time through
 // which the encoded response is considered trustworthy (that is, if a response had a
 // thisUpdate time of validThrough, it would be considered trustworthy).
 Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
                                  const CertID& certID, PRTime time,
                                  uint16_t maxLifetimeInDays,
-                                 const SECItem& encodedResponse,
+                                 InputBuffer encodedResponse,
                        /* out */ bool& expired,
               /* optional out */ PRTime* thisUpdate = nullptr,
               /* optional out */ PRTime* validThrough = nullptr);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkix_h
--- a/lib/mozpkix/include/pkix/pkixnss.h
+++ b/lib/mozpkix/include/pkix/pkixnss.h
@@ -28,35 +28,35 @@
 #include "pkixtypes.h"
 #include "prerror.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix {
 
 // Verify the given signed data using the given public key.
 Result VerifySignedData(const SignedDataWithSignature& sd,
-                        const SECItem& subjectPublicKeyInfo,
+                        InputBuffer subjectPublicKeyInfo,
                         void* pkcs11PinArg);
 
 // Computes the SHA-1 hash of the data in the current item.
 //
 // item contains the data to hash.
 // digestBuf must point to a buffer to where the SHA-1 hash will be written.
 // digestBufLen must be 20 (the length of a SHA-1 hash,
 //              TrustDomain::DIGEST_LENGTH).
 //
 // TODO(bug 966856): Add SHA-2 support
 // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
 // other, extensive, memory safety efforts in mozilla::pkix, and we should find
 // a way to provide a more-obviously-safe interface.
-Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+Result DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf,
                  size_t digestBufLen);
 
 // Checks, for RSA keys and DSA keys, that the modulus is at least 1024 bits.
-Result CheckPublicKey(const SECItem& subjectPublicKeyInfo);
+Result CheckPublicKey(InputBuffer subjectPublicKeyInfo);
 
 Result MapPRErrorCodeToResult(PRErrorCode errorCode);
 PRErrorCode MapResultToPRErrorCode(Result result);
 
 // Returns the stringified name of the given result, e.g. "Result::Success",
 // or nullptr if result is unknown (invalid).
 const char* MapResultToName(Result result);
 
@@ -71,11 +71,21 @@ static const PRErrorCode ERROR_BASE = -0
 static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000;
 
 enum ErrorCode {
   MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0
 };
 
 void RegisterErrorTable();
 
+inline SECItem UnsafeMapInputBufferToSECItem(InputBuffer ib)
+{
+  SECItem result = {
+    siBuffer,
+    const_cast<uint8_t*>(ib.UnsafeGetData()),
+    ib.GetLength()
+  };
+  return result;
+}
+
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixnss_h
--- a/lib/mozpkix/include/pkix/pkixtypes.h
+++ b/lib/mozpkix/include/pkix/pkixtypes.h
@@ -20,20 +20,18 @@
  * 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.
  */
 
 #ifndef mozilla_pkix__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
-#include "pkix/Result.h"
-#include "pkix/nullptr.h"
+#include "pkix/Input.h"
 #include "prtime.h"
-#include "seccomon.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
 MOZILLA_PKIX_ENUM_CLASS DigestAlgorithm
 {
   sha512 = 1,
   sha384 = 2,
@@ -76,26 +74,22 @@ MOZILLA_PKIX_ENUM_CLASS SignatureAlgorit
 
   // id-dsa-with-sha1 (OID 1.2.840.10040.4.3, RFC 3279 Section 2.2.2)
   dsa_with_sha1 = 18,
 };
 
 struct SignedDataWithSignature
 {
 public:
-  SignedDataWithSignature()
-  {
-    data.data = nullptr;
-    data.len = 0;
-    signature.data = nullptr;
-    signature.len = 0;
-  }
-  SECItem data; // non-owning
+  InputBuffer data;
   SignatureAlgorithm algorithm;
-  SECItem signature; // non-owning
+  InputBuffer signature;
+
+private:
+  void operator=(const SignedDataWithSignature&) /*= delete*/;
 };
 
 MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
 
 MOZILLA_PKIX_ENUM_CLASS KeyUsage : uint8_t {
   digitalSignature = 0,
   nonRepudiation   = 1,
   keyEncipherment  = 2,
@@ -143,40 +137,40 @@ MOZILLA_PKIX_ENUM_CLASS TrustLevel {
 // be encoded exactly the same, and the encoding matters for OCSP.)
 // issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo
 // field from the issuer's certificate. serialNumber is the entire DER-encoded
 // serial number from the subject certificate (the certificate for which we are
 // checking the revocation status).
 struct CertID
 {
 public:
-  CertID(const SECItem& issuer, const SECItem& issuerSubjectPublicKeyInfo,
-         const SECItem& serialNumber)
+  CertID(InputBuffer issuer, InputBuffer issuerSubjectPublicKeyInfo,
+         InputBuffer serialNumber)
     : issuer(issuer)
     , issuerSubjectPublicKeyInfo(issuerSubjectPublicKeyInfo)
     , serialNumber(serialNumber)
   {
   }
-  const SECItem& issuer;
-  const SECItem& issuerSubjectPublicKeyInfo;
-  const SECItem& serialNumber;
+  const InputBuffer issuer;
+  const InputBuffer issuerSubjectPublicKeyInfo;
+  const InputBuffer serialNumber;
 private:
   void operator=(const CertID&) /*= delete*/;
 };
 
 class DERArray
 {
 public:
   // Returns the number of DER-encoded items in the array.
   virtual size_t GetLength() const = 0;
 
   // Returns a weak (non-owning) pointer the ith DER-encoded item in the array
   // (0-indexed). The result is guaranteed to be non-null if i < GetLength(),
   // and the result is guaranteed to be nullptr if i >= GetLength().
-  virtual const SECItem* GetDER(size_t i) const = 0;
+  virtual const InputBuffer* GetDER(size_t i) const = 0;
 protected:
   DERArray() { }
   virtual ~DERArray() { }
 };
 
 // Applications control the behavior of path building and verification by
 // implementing the TrustDomain interface. The TrustDomain is used for all
 // cryptography and for determining which certificates are trusted or
@@ -195,32 +189,32 @@ public:
   // trustLevel == TrustAnchor unless the given cert is considered a trust
   // anchor *for that policy*. In particular, if the user has marked an
   // intermediate certificate as trusted, but that intermediate isn't in the
   // list of EV roots, then GetCertTrust must result in
   // trustLevel == InheritsTrust instead of trustLevel == TrustAnchor
   // (assuming the candidate cert is not actively distrusted).
   virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
                               const CertPolicyId& policy,
-                              const SECItem& candidateCertDER,
+                              InputBuffer candidateCertDER,
                               /*out*/ TrustLevel& trustLevel) = 0;
 
   class IssuerChecker
   {
   public:
     // potentialIssuerDER is the complete DER encoding of the certificate to
     // be checked as a potential issuer.
     //
     // If additionalNameConstraints is not nullptr then it must point to an
     // encoded NameConstraints extension value; in that case, those name
     // constraints will be checked in addition to any any name constraints
     // contained in potentialIssuerDER.
-    virtual Result Check(const SECItem& potentialIssuerDER,
-                         /*optional*/ const SECItem* additionalNameConstraints,
-                         /*out*/ bool& keepGoing) = 0;
+    virtual Result Check(InputBuffer potentialIssuerDER,
+            /*optional*/ const InputBuffer* additionalNameConstraints,
+                 /*out*/ bool& keepGoing) = 0;
   protected:
     IssuerChecker();
     virtual ~IssuerChecker();
   private:
     IssuerChecker(const IssuerChecker&) /*= delete*/;
     void operator=(const IssuerChecker&) /*= delete*/;
   };
 
@@ -260,17 +254,17 @@ public:
   //              [...]
   //                TrustDomain::FindIssuer
   //                  [...]
   //                    IssuerChecker::Check
   //                      [...]
   //
   // checker.Check is responsible for limiting the recursion to a reasonable
   // limit.
-  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+  virtual Result FindIssuer(InputBuffer encodedIssuerName,
                             IssuerChecker& checker, PRTime time) = 0;
 
   // Called as soon as we think we have a valid chain but before revocation
   // checks are done. This function can be used to compute additional checks,
   // especilaly checks that require the entire certificate chain. This callback
   // can also be used to save a copy of the built certificate chain for later
   // use.
   //
@@ -290,49 +284,49 @@ public:
   //
   // certChain.GetDER(0) is the trust anchor.
   virtual Result IsChainValid(const DERArray& certChain) = 0;
 
   // issuerCertToDup is only non-const so CERT_DupCertificate can be called on
   // it.
   virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
                                  const CertID& certID, PRTime time,
-                    /*optional*/ const SECItem* stapledOCSPresponse,
-                    /*optional*/ const SECItem* aiaExtension) = 0;
+                    /*optional*/ const InputBuffer* stapledOCSPresponse,
+                    /*optional*/ const InputBuffer* aiaExtension) = 0;
 
   // Check that the key size, algorithm, and parameters of the given public key
   // are acceptable.
   //
   // VerifySignedData() should do the same checks that this function does, but
   // mainly for efficiency, some keys are not passed to VerifySignedData().
   // This function is called instead for those keys.
-  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo) = 0;
+  virtual Result CheckPublicKey(InputBuffer subjectPublicKeyInfo) = 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.
   //
   // In any case, the implementation must perform checks on the public key
   // similar to how mozilla::pkix::CheckPublicKey() does.
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
-                                  const SECItem& subjectPublicKeyInfo) = 0;
+                                  InputBuffer subjectPublicKeyInfo) = 0;
 
   // Compute the SHA-1 hash of the data in the current item.
   //
   // item contains the data to hash.
   // digestBuf must point to a buffer to where the SHA-1 hash will be written.
   // digestBufLen must be DIGEST_LENGTH (20, the length of a SHA-1 hash).
   //
   // TODO(bug 966856): Add SHA-2 support
   // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
   // other, extensive, memory safety efforts in mozilla::pkix, and we should
   // find a way to provide a more-obviously-safe interface.
   static const size_t DIGEST_LENGTH = 20; // length of SHA-1 digest
-  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+  virtual Result DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) = 0;
 protected:
   TrustDomain() { }
 
 private:
   TrustDomain(const TrustDomain&) /* = delete */;
   void operator=(const TrustDomain&) /* = delete */;
 };
--- a/lib/mozpkix/lib/pkixbuild.cpp
+++ b/lib/mozpkix/lib/pkixbuild.cpp
@@ -22,66 +22,67 @@
  * limitations under the License.
  */
 
 #include "pkix/pkix.h"
 
 #include <limits>
 
 #include "pkixcheck.h"
+#include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 static Result BuildForward(TrustDomain& trustDomain,
                            const BackCert& subject,
                            PRTime time,
                            KeyUsage requiredKeyUsageIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
                            const CertPolicyId& requiredPolicy,
-                           /*optional*/ const SECItem* stapledOCSPResponse,
+                           /*optional*/ const InputBuffer* stapledOCSPResponse,
                            unsigned int subCACount);
 
 TrustDomain::IssuerChecker::IssuerChecker() { }
 TrustDomain::IssuerChecker::~IssuerChecker() { }
 
 // The implementation of TrustDomain::IssuerTracker is in a subclass only to
 // hide the implementation from external users.
 class PathBuildingStep : public TrustDomain::IssuerChecker
 {
 public:
   PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject,
                    PRTime time, KeyPurposeId requiredEKUIfPresent,
                    const CertPolicyId& requiredPolicy,
-                   /*optional*/ const SECItem* stapledOCSPResponse,
+                   /*optional*/ const InputBuffer* stapledOCSPResponse,
                    unsigned int subCACount)
     : trustDomain(trustDomain)
     , subject(subject)
     , time(time)
     , requiredEKUIfPresent(requiredEKUIfPresent)
     , requiredPolicy(requiredPolicy)
     , stapledOCSPResponse(stapledOCSPResponse)
     , subCACount(subCACount)
     , result(Result::FATAL_ERROR_LIBRARY_FAILURE)
     , resultWasSet(false)
   {
   }
 
-  Result Check(const SECItem& potentialIssuerDER,
-               /*optional*/ const SECItem* additionalNameConstraints,
+  Result Check(InputBuffer potentialIssuerDER,
+               /*optional*/ const InputBuffer* additionalNameConstraints,
                /*out*/ bool& keepGoing);
 
   Result CheckResult() const;
 
 private:
   TrustDomain& trustDomain;
   const BackCert& subject;
   const PRTime time;
   const KeyPurposeId requiredEKUIfPresent;
   const CertPolicyId& requiredPolicy;
-  /*optional*/ SECItem const* const stapledOCSPResponse;
+  /*optional*/ InputBuffer const* const stapledOCSPResponse;
   const unsigned int subCACount;
 
   Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
   Result result;
   bool resultWasSet;
 
   PathBuildingStep(const PathBuildingStep&) /*= delete*/;
   void operator=(const PathBuildingStep&) /*= delete*/;
@@ -120,19 +121,19 @@ PathBuildingStep::CheckResult() const
   if (!resultWasSet) {
     return Result::ERROR_UNKNOWN_ISSUER;
   }
   return result;
 }
 
 // The code that executes in the inner loop of BuildForward
 Result
-PathBuildingStep::Check(const SECItem& potentialIssuerDER,
-                        /*optional*/ const SECItem* additionalNameConstraints,
-                        /*out*/ bool& keepGoing)
+PathBuildingStep::Check(InputBuffer potentialIssuerDER,
+           /*optional*/ const InputBuffer* additionalNameConstraints,
+                /*out*/ bool& keepGoing)
 {
   BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
                            &subject);
   Result rv = potentialIssuer.Init();
   if (rv != Success) {
     return RecordResult(rv, keepGoing);
   }
 
@@ -140,20 +141,20 @@ PathBuildingStep::Check(const SECItem& p
   // 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 (const BackCert* prev = potentialIssuer.childCert;
        !loopDetected && prev != nullptr; prev = prev->childCert) {
-    if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(),
-                              &prev->GetSubjectPublicKeyInfo()) &&
-        SECITEM_ItemsAreEqual(&potentialIssuer.GetSubject(),
-                              &prev->GetSubject())) {
+    if (InputBuffersAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
+                             prev->GetSubjectPublicKeyInfo()) &&
+        InputBuffersAreEqual(potentialIssuer.GetSubject(),
+                             prev->GetSubject())) {
       // XXX: error code
       return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
     }
   }
 
   if (potentialIssuer.GetNameConstraints()) {
     rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
                               subject, requiredEKUIfPresent);
@@ -205,17 +206,17 @@ PathBuildingStep::Check(const SECItem& p
 // pkix/pkix.h.
 static Result
 BuildForward(TrustDomain& trustDomain,
              const BackCert& subject,
              PRTime time,
              KeyUsage requiredKeyUsageIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
-             /*optional*/ const SECItem* stapledOCSPResponse,
+             /*optional*/ const InputBuffer* stapledOCSPResponse,
              unsigned int subCACount)
 {
   Result rv;
 
   TrustLevel trustLevel;
   // If this is an end-entity and not a trust anchor, we defer reporting
   // any error found here until after attempting to find a valid chain.
   // See the explanation of error prioritization in pkix.h.
@@ -288,22 +289,22 @@ BuildForward(TrustDomain& trustDomain,
     return deferredEndEntityError;
   }
 
   // We've built a valid chain from the subject cert up to a trusted root.
   return Success;
 }
 
 Result
-BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER,
+BuildCertChain(TrustDomain& trustDomain, InputBuffer certDER,
                PRTime time, EndEntityOrCA endEntityOrCA,
                KeyUsage requiredKeyUsageIfPresent,
                KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
-               /*optional*/ const SECItem* stapledOCSPResponse)
+               /*optional*/ const InputBuffer* stapledOCSPResponse)
 {
   // XXX: Support the legacy use of the subject CN field for indicating the
   // domain name the certificate is valid for.
   BackCert cert(certDER, endEntityOrCA, nullptr);
   Result rv = cert.Init();
   if (rv != Success) {
     return rv;
   }
--- a/lib/mozpkix/lib/pkixcert.cpp
+++ b/lib/mozpkix/lib/pkixcert.cpp
@@ -37,21 +37,17 @@ BackCert::Init()
   //         signatureAlgorithm   AlgorithmIdentifier,
   //         signatureValue       BIT STRING  }
 
   Input tbsCertificate;
 
   // The scope of |input| and |certificate| are limited to this block so we
   // don't accidentally confuse them for tbsCertificate later.
   {
-    Input input;
-    rv = input.Init(der.data, der.len);
-    if (rv != Success) {
-      return rv;
-    }
+    Input input(der);
     Input certificate;
     rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, certificate);
     if (rv != Success) {
       return rv;
     }
     rv = der::End(input);
     if (rv != Success) {
       return rv;
@@ -157,18 +153,20 @@ BackCert::Init()
     if (rv != Success) {
       return rv;
     }
   }
 
   return der::End(tbsCertificate);
 }
 
+// XXX: The second value is of type |const InputBupkffer&| instead of type
+// |InputBuffer| due to limitations in our std::bind polyfill.
 Result
-BackCert::RememberExtension(Input& extnID, const SECItem& extnValue,
+BackCert::RememberExtension(Input& extnID, const InputBuffer& 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
   };
@@ -200,25 +198,25 @@ BackCert::RememberExtension(Input& extnI
   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;
+  InputBuffer* 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 };
+  InputBuffer dummyPolicyConstraints;
 
   // 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)) {
@@ -236,24 +234,23 @@ BackCert::RememberExtension(Input& extnI
   } 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) {
+    // can test out->GetLength() != 0 or out->Init() to check for duplicates.
+    if (extnValue.GetLength() == 0) {
       return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
-    if (out->len != 0) {
+    if (out->Init(extnValue) != Success) {
       // Duplicate extension
       return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
-    *out = extnValue;
     understood = true;
   }
 
   return Success;
 }
 
 } } // namespace mozilla::pkix
--- a/lib/mozpkix/lib/pkixcheck.cpp
+++ b/lib/mozpkix/lib/pkixcheck.cpp
@@ -17,34 +17,32 @@
  *
  * 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 "pkixcheck.h"
+
 #include "cert.h"
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
 #include "pkix/ScopedPtr.h"
-#include "pkixcheck.h"
 #include "pkixder.h"
 #include "pkix/pkixnss.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 Result
-CheckValidity(const SECItem& encodedValidity, PRTime time)
+CheckValidity(InputBuffer encodedValidity, PRTime time)
 {
-  Input validity;
-  if (validity.Init(encodedValidity.data, encodedValidity.len) != Success) {
-    return Result::ERROR_EXPIRED_CERTIFICATE;
-  }
+  Input validity(encodedValidity);
   PRTime notBefore;
   if (der::TimeChoice(validity, notBefore) != Success) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   if (time < notBefore) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
@@ -65,17 +63,17 @@ CheckValidity(const SECItem& encodedVali
 // bit and bit 7 is the least significant bit.
 inline uint8_t KeyUsageToBitMask(KeyUsage keyUsage)
 {
   PR_ASSERT(keyUsage != KeyUsage::noParticularKeyUsageRequired);
   return 0x80u >> static_cast<uint8_t>(keyUsage);
 }
 
 Result
-CheckKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedKeyUsage,
+CheckKeyUsage(EndEntityOrCA endEntityOrCA, const InputBuffer* encodedKeyUsage,
               KeyUsage requiredKeyUsageIfPresent)
 {
   if (!encodedKeyUsage) {
     // TODO(bug 970196): Reject certificates that are being used to verify
     // certificate signatures unless the certificate is a trust anchor, to
     // reduce the chances of an end-entity certificate being abused as a CA
     // certificate.
     // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) {
@@ -83,20 +81,17 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
     // }
     //
     // TODO: Users may configure arbitrary certificates as trust anchors, not
     // just roots. We should only allow a certificate without a key usage to be
     // used as a CA when it is self-issued and self-signed.
     return Success;
   }
 
-  Input input;
-  if (input.Init(encodedKeyUsage->data, encodedKeyUsage->len) != Success) {
-    return Result::ERROR_INADEQUATE_KEY_USAGE;
-  }
+  Input input(*encodedKeyUsage);
   Input value;
   if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != Success) {
     return Result::ERROR_INADEQUATE_KEY_USAGE;
   }
 
   uint8_t numberOfPaddingBits;
   if (value.Read(numberOfPaddingBits) != Success) {
     return Result::ERROR_INADEQUATE_KEY_USAGE;
@@ -228,18 +223,18 @@ CheckPolicyInformation(Input& input, End
   input.SkipToEnd();
 
   return Success;
 }
 
 // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
 Result
 CheckCertificatePolicies(EndEntityOrCA endEntityOrCA,
-                         const SECItem* encodedCertificatePolicies,
-                         const SECItem* encodedInhibitAnyPolicy,
+                         const InputBuffer* encodedCertificatePolicies,
+                         const InputBuffer* encodedInhibitAnyPolicy,
                          TrustLevel trustLevel,
                          const CertPolicyId& requiredPolicy)
 {
   if (requiredPolicy.numBytes == 0 ||
       requiredPolicy.numBytes > sizeof requiredPolicy.bytes) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
@@ -267,21 +262,17 @@ CheckCertificatePolicies(EndEntityOrCA e
   }
 
   if (!encodedCertificatePolicies) {
     return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
 
   bool found = false;
 
-  Input input;
-  if (input.Init(encodedCertificatePolicies->data,
-                 encodedCertificatePolicies->len) != Success) {
-    return Result::ERROR_POLICY_VALIDATION_FAILED;
-  }
+  Input input(*encodedCertificatePolicies);
   if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No,
                     bind(CheckPolicyInformation, _1, endEntityOrCA,
                          requiredPolicy, ref(found))) != Success) {
     return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
   if (der::End(input) != Success) {
     return Result::ERROR_POLICY_VALIDATION_FAILED;
   }
@@ -320,29 +311,25 @@ DecodeBasicConstraints(Input& input, /*o
   }
 
   return Success;
 }
 
 // RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints)
 Result
 CheckBasicConstraints(EndEntityOrCA endEntityOrCA,
-                      const SECItem* encodedBasicConstraints,
+                      const InputBuffer* encodedBasicConstraints,
                       const der::Version version, TrustLevel trustLevel,
                       unsigned int subCACount)
 {
   bool isCA = false;
   long pathLenConstraint = UNLIMITED_PATH_LEN;
 
   if (encodedBasicConstraints) {
-    Input input;
-    if (input.Init(encodedBasicConstraints->data,
-                   encodedBasicConstraints->len) != Success) {
-      return Result::ERROR_EXTENSION_VALUE_INVALID;
-    }
+    Input input(*encodedBasicConstraints);
     if (der::Nested(input, der::SEQUENCE,
                     bind(DecodeBasicConstraints, _1, ref(isCA),
                          ref(pathLenConstraint))) != Success) {
       return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
     if (der::End(input) != Success) {
       return Result::ERROR_EXTENSION_VALUE_INVALID;
     }
@@ -401,40 +388,45 @@ CheckBasicConstraints(EndEntityOrCA endE
 
 inline void
 PORT_FreeArena_false(PLArenaPool* arena) {
   // PL_FreeArenaPool can't be used because it doesn't actually free the
   // memory, which doesn't work well with memory analysis tools
   return PORT_FreeArena(arena, PR_FALSE);
 }
 
-// TODO: remove #include "pkix/pkixnss.h" when this is rewritten to be
-// independent of NSS.
+// TODO: Remove #include "pkix/pkixnss.h", #include "cert.h",
+// #include "ScopedPtr.h", etc. when this is rewritten to be independent of
+// NSS.
 Result
-CheckNameConstraints(const SECItem& encodedNameConstraints,
+CheckNameConstraints(InputBuffer encodedNameConstraints,
                      const BackCert& firstChild,
                      KeyPurposeId requiredEKUIfPresent)
 {
   ScopedPtr<PLArenaPool, PORT_FreeArena_false>
     arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
     return Result::FATAL_ERROR_NO_MEMORY;
   }
 
+  SECItem encodedNameConstraintsSECItem =
+    UnsafeMapInputBufferToSECItem(encodedNameConstraints);
+
   // Owned by arena
   const CERTNameConstraints* constraints =
-    CERT_DecodeNameConstraintsExtension(arena.get(), &encodedNameConstraints);
+    CERT_DecodeNameConstraintsExtension(arena.get(),
+                                        &encodedNameConstraintsSECItem);
   if (!constraints) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
   for (const BackCert* child = &firstChild; child; child = child->childCert) {
+    SECItem childCertDER = UnsafeMapInputBufferToSECItem(child->GetDER());
     ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
-      nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
-                                      const_cast<SECItem*>(&child->GetDER()),
+      nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &childCertDER,
                                       nullptr, false, true));
     if (!nssCert) {
       return MapPRErrorCodeToResult(PR_GetError());
     }
 
     bool includeCN = child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
                      requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth;
     // owned by arena
@@ -543,34 +535,30 @@ MatchEKU(Input& value, KeyPurposeId requ
 
   value.SkipToEnd(); // ignore unmatched OIDs.
 
   return Success;
 }
 
 Result
 CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
-                      const SECItem* encodedExtendedKeyUsage,
+                      const InputBuffer* encodedExtendedKeyUsage,
                       KeyPurposeId requiredEKU)
 {
   // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers
   // can distinguish EKU mismatch from KU mismatch from basic constraints
   // mismatch. We should probably add a new error code that is more clear for
   // this type of problem.
 
   bool foundOCSPSigning = false;
 
   if (encodedExtendedKeyUsage) {
     bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage;
 
-    Input input;
-    if (input.Init(encodedExtendedKeyUsage->data,
-                   encodedExtendedKeyUsage->len) != Success) {
-      return Result::ERROR_INADEQUATE_CERT_TYPE;
-    }
+    Input input(*encodedExtendedKeyUsage);
     if (der::NestedOf(input, der::SEQUENCE, der::OIDTag, der::EmptyAllowed::No,
                       bind(MatchEKU, _1, requiredEKU, endEntityOrCA,
                            ref(found), ref(foundOCSPSigning)))
           != Success) {
       return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
     if (der::End(input) != Success) {
       return Result::ERROR_INADEQUATE_CERT_TYPE;
--- a/lib/mozpkix/lib/pkixcheck.h
+++ b/lib/mozpkix/lib/pkixcheck.h
@@ -21,30 +21,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixcheck_h
 #define mozilla_pkix__pkixcheck_h
 
 #include "pkix/pkixtypes.h"
-#include "pkixutil.h"
-#include "certt.h"
 
 namespace mozilla { namespace pkix {
 
+class BackCert;
+
 Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
           const BackCert& cert,
           PRTime time,
           KeyUsage requiredKeyUsageIfPresent,
           KeyPurposeId requiredEKUIfPresent,
           const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*out*/ TrustLevel& trustLevel);
 
-Result CheckNameConstraints(const SECItem& encodedNameConstraints,
+Result CheckNameConstraints(InputBuffer encodedNameConstraints,
                             const BackCert& firstChild,
                             KeyPurposeId requiredEKUIfPresent);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixcheck_h
--- a/lib/mozpkix/lib/pkixder.cpp
+++ b/lib/mozpkix/lib/pkixder.cpp
@@ -284,47 +284,58 @@ SignedData(Input& input, /*out*/ Input& 
   Input::Mark mark(input.GetMark());
 
   Result rv;
   rv = ExpectTagAndGetValue(input, SEQUENCE, tbs);
   if (rv != Success) {
     return rv;
   }
 
-  rv = input.GetSECItem(siBuffer, mark, signedData.data);
+  rv = input.GetInputBuffer(mark, signedData.data);
   if (rv != Success) {
     return rv;
   }
 
   rv = SignatureAlgorithmIdentifier(input, signedData.algorithm);
   if (rv != Success) {
     return rv;
   }
 
-  rv = ExpectTagAndGetValue(input, BIT_STRING, signedData.signature);
+  rv = BitStringWithNoUnusedBits(input, signedData.signature);
+  if (rv == Result::ERROR_BAD_DER) {
+    rv = Result::ERROR_BAD_SIGNATURE;
+  }
+  return rv;
+}
+
+Result
+BitStringWithNoUnusedBits(Input& input, /*out*/ InputBuffer& value)
+{
+  Input valueWithUnusedBits;
+  Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits);
   if (rv != Success) {
     return rv;
   }
-  if (signedData.signature.len == 0) {
-    return Result::ERROR_BAD_SIGNATURE;
+
+  uint8_t unusedBitsAtEnd;
+  if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) {
+    return Result::ERROR_BAD_DER;
   }
-  unsigned int unusedBitsAtEnd = signedData.signature.data[0];
   // XXX: Really the constraint should be that unusedBitsAtEnd must be less
-  // than 7. But, we suspect there are no real-world OCSP responses or X.509
-  // certificates with non-zero unused bits. It seems like NSS assumes this in
-  // various places, so we enforce it too in order to simplify this code. If we
-  // find compatibility issues, we'll know we're wrong and we'll have to figure
-  // out how to shift the bits around.
+  // than 7. But, we suspect there are no real-world values in OCSP responses
+  // or certificates with non-zero unused bits. It seems like NSS assumes this
+  // in various places, so we enforce it too in order to simplify this code. If
+  // we find compatibility issues, we'll know we're wrong and we'll have to
+  // figure out how to shift the bits around.
   if (unusedBitsAtEnd != 0) {
-    return Result::ERROR_BAD_SIGNATURE;
+    return Result::ERROR_BAD_DER;
   }
-  ++signedData.signature.data;
-  --signedData.signature.len;
-
-  return Success;
+  Input::Mark mark(valueWithUnusedBits.GetMark());
+  valueWithUnusedBits.SkipToEnd();
+  return valueWithUnusedBits.GetInputBuffer(mark, value);
 }
 
 static inline Result
 ReadDigit(Input& input, /*out*/ int& value)
 {
   uint8_t b;
   if (input.Read(b) != Success) {
     return Result::ERROR_INVALID_TIME;
--- a/lib/mozpkix/lib/pkixder.h
+++ b/lib/mozpkix/lib/pkixder.h
@@ -32,23 +32,19 @@
 // Match* functions advance the input mark and return true if the input matches
 // the given criteria; they return false without changing the input mark if the
 // input does not match the criteria.
 //
 // Skip* functions unconditionally advance the input mark and return Success if
 // they are able to do so; otherwise they fail with the input mark in an
 // undefined state.
 
-#include "pkix/enumclass.h"
 #include "pkix/Input.h"
 #include "pkix/pkixtypes.h"
 #include "prtime.h"
-#include "secoidt.h"
-
-typedef struct CERTSignedDataStr CERTSignedData;
 
 namespace mozilla { namespace pkix { namespace der {
 
 enum Class
 {
    UNIVERSAL = 0 << 6,
 // APPLICATION = 1 << 6, // unused
    CONTEXT_SPECIFIC = 2 << 6,
@@ -112,17 +108,17 @@ ExpectTagAndSkipValue(Input& input, uint
   Result rv = internal::ExpectTagAndGetLength(input, tag, length);
   if (rv != Success) {
     return rv;
   }
   return input.Skip(length);
 }
 
 inline Result
-ExpectTagAndGetValue(Input& input, uint8_t tag, /*out*/ SECItem& value)
+ExpectTagAndGetValue(Input& input, uint8_t tag, /*out*/ InputBuffer& value)
 {
   uint16_t length;
   Result rv = internal::ExpectTagAndGetLength(input, tag, length);
   if (rv != Success) {
     return rv;
   }
   return input.Skip(length, value);
 }
@@ -133,32 +129,32 @@ ExpectTagAndGetValue(Input& input, uint8
   uint16_t length;
   Result rv = internal::ExpectTagAndGetLength(input, tag, length);
   if (rv != Success) {
     return rv;
   }
   return input.Skip(length, value);
 }
 
-// Like ExpectTagAndGetValue, except the output SECItem will contain the
+// Like ExpectTagAndGetValue, except the output InputBuffer will contain the
 // encoded tag and length along with the value.
 inline Result
-ExpectTagAndGetTLV(Input& input, uint8_t tag, /*out*/ SECItem& tlv)
+ExpectTagAndGetTLV(Input& input, uint8_t tag, /*out*/ InputBuffer& tlv)
 {
   Input::Mark mark(input.GetMark());
   uint16_t length;
   Result rv = internal::ExpectTagAndGetLength(input, tag, length);
   if (rv != Success) {
     return rv;
   }
   rv = input.Skip(length);
   if (rv != Success) {
     return rv;
   }
-  return input.GetSECItem(siBuffer, mark, tlv);
+  return input.GetInputBuffer(mark, tlv);
 }
 
 inline Result
 End(Input& input)
 {
   if (!input.AtEnd()) {
     return Result::ERROR_BAD_DER;
   }
@@ -271,16 +267,19 @@ IntegralValue(Input& input, uint8_t tag,
     return Result::ERROR_BAD_DER;
   }
   value = valueByte;
   return Success;
 }
 
 } // namespace internal
 
+Result
+BitStringWithNoUnusedBits(Input& input, /*out*/ InputBuffer& value);
+
 inline Result
 Boolean(Input& input, /*out*/ bool& value)
 {
   Result rv = ExpectTagAndLength(input, BOOLEAN, 1);
   if (rv != Success) {
     return rv;
   }
 
@@ -397,28 +396,31 @@ Null(Input& input)
 {
   return ExpectTagAndLength(input, NULLTag, 0);
 }
 
 template <uint8_t Len>
 Result
 OID(Input& input, const uint8_t (&expectedOid)[Len])
 {
-  Result rv = ExpectTagAndLength(input, OIDTag, Len);
+  Input value;
+  Result rv = ExpectTagAndGetValue(input, OIDTag, value);
   if (rv != Success) {
     return rv;
   }
-
-  return input.Expect(expectedOid, Len);
+  if (!value.MatchRest(expectedOid)) {
+    return Result::ERROR_BAD_DER;
+  }
+  return Success;
 }
 
 // PKI-specific types
 
 inline Result
-CertificateSerialNumber(Input& input, /*out*/ SECItem& value)
+CertificateSerialNumber(Input& input, /*out*/ InputBuffer& value)
 {
   // http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
   //
   // * "The serial number MUST be a positive integer assigned by the CA to
   //   each certificate."
   // * "Certificate users MUST be able to handle serialNumber values up to 20
   //   octets. Conforming CAs MUST NOT use serialNumber values longer than 20
   //   octets."
@@ -426,28 +428,39 @@ CertificateSerialNumber(Input& input, /*
   //   that are negative or zero.  Certificate users SHOULD be prepared to
   //   gracefully handle such certificates."
 
   Result rv = ExpectTagAndGetValue(input, INTEGER, value);
   if (rv != Success) {
     return rv;
   }
 
-  if (value.len == 0) {
+  if (value.GetLength() == 0) {
     return Result::ERROR_BAD_DER;
   }
 
   // Check for overly-long encodings. If the first byte is 0x00 then the high
   // bit on the second byte must be 1; otherwise the same *positive* value
   // could be encoded without the leading 0x00 byte. If the first byte is 0xFF
   // then the second byte must NOT have its high bit set; otherwise the same
   // *negative* value could be encoded without the leading 0xFF byte.
-  if (value.len > 1) {
-    if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) ||
-        (value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) {
+  if (value.GetLength() > 1) {
+    Input valueInput(value);
+    uint8_t firstByte;
+    rv = valueInput.Read(firstByte);
+    if (rv != Success) {
+      return rv;
+    }
+    uint8_t secondByte;
+    rv = valueInput.Read(secondByte);
+    if (rv != Success) {
+      return rv;
+    }
+    if ((firstByte == 0x00 && (secondByte & 0x80) == 0) ||
+        (firstByte == 0xff && (secondByte & 0x80) != 0)) {
       return Result::ERROR_BAD_DER;
     }
   }
 
   return Success;
 }
 
 // x.509 and OCSP both use this same version numbering scheme, though OCSP
@@ -540,17 +553,17 @@ OptionalExtensions(Input& input, uint8_t
     if (rv != Success) {
       return rv;
     }
     bool critical;
     rv = OptionalBoolean(extension, false, critical);
     if (rv != Success) {
       return rv;
     }
-    SECItem extnValue;
+    InputBuffer extnValue;
     rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue);
     if (rv != Success) {
       return rv;
     }
     rv = End(extension);
     if (rv != Success) {
       return rv;
     }
--- a/lib/mozpkix/lib/pkixnss.cpp
+++ b/lib/mozpkix/lib/pkixnss.cpp
@@ -20,36 +20,37 @@
  * 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/pkixnss.h"
 
 #include <limits>
-#include <stdint.h>
 
 #include "cert.h"
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "pk11pub.h"
 #include "pkix/pkix.h"
 #include "pkix/ScopedPtr.h"
 #include "secerr.h"
 
 namespace mozilla { namespace pkix {
 
 typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey> ScopedSECKeyPublicKey;
 
 Result
-CheckPublicKeySize(const SECItem& subjectPublicKeyInfo,
+CheckPublicKeySize(InputBuffer subjectPublicKeyInfo,
                    /*out*/ ScopedSECKeyPublicKey& publicKey)
 {
+  SECItem subjectPublicKeyInfoSECItem =
+    UnsafeMapInputBufferToSECItem(subjectPublicKeyInfo);
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
-    spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfo));
+    spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
   if (!spki) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   publicKey = SECKEY_ExtractPublicKey(spki.get());
   if (!publicKey) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
@@ -76,33 +77,29 @@ CheckPublicKeySize(const SECItem& subjec
     default:
       return Result::ERROR_UNSUPPORTED_KEYALG;
   }
 
   return Success;
 }
 
 Result
-CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+CheckPublicKey(InputBuffer subjectPublicKeyInfo)
 {
   ScopedSECKeyPublicKey unused;
   return CheckPublicKeySize(subjectPublicKeyInfo, unused);
 }
 
 Result
 VerifySignedData(const SignedDataWithSignature& sd,
-                 const SECItem& subjectPublicKeyInfo, void* pkcs11PinArg)
+                 InputBuffer subjectPublicKeyInfo, void* pkcs11PinArg)
 {
-  if (!sd.data.data || !sd.signature.data) {
-    PR_NOT_REACHED("invalid args to VerifySignedData");
-    return Result::FATAL_ERROR_INVALID_ARGS;
-  }
-
   // See bug 921585.
-  if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
+  if (sd.data.GetLength() >
+        static_cast<unsigned int>(std::numeric_limits<int>::max())) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   SECOidTag pubKeyAlg;
   SECOidTag digestAlg;
   switch (sd.algorithm) {
     case SignatureAlgorithm::ecdsa_with_sha512:
       pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
@@ -153,43 +150,48 @@ VerifySignedData(const SignedDataWithSig
   ScopedSECKeyPublicKey pubKey;
   rv = CheckPublicKeySize(subjectPublicKeyInfo, pubKey);
   if (rv != Success) {
     return rv;
   }
 
   // The static_cast is safe according to the check above that references
   // bug 921585.
-  SECStatus srv = VFY_VerifyDataDirect(sd.data.data,
-                                       static_cast<int>(sd.data.len),
-                                       pubKey.get(), &sd.signature, pubKeyAlg,
-                                       digestAlg, nullptr, pkcs11PinArg);
+  SECItem dataSECItem(UnsafeMapInputBufferToSECItem(sd.data));
+  SECItem signatureSECItem(UnsafeMapInputBufferToSECItem(sd.signature));
+  SECStatus srv = VFY_VerifyDataDirect(dataSECItem.data,
+                                       static_cast<int>(dataSECItem.len),
+                                       pubKey.get(), &signatureSECItem,
+                                       pubKeyAlg, digestAlg, nullptr,
+                                       pkcs11PinArg);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
   return Success;
 }
 
 Result
-DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH,
                 "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)");
   if (digestBufLen != TrustDomain::DIGEST_LENGTH) {
     PR_NOT_REACHED("invalid hash length");
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
-  if (item.len >
-      static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) {
-    PR_NOT_REACHED("large OCSP responses should have already been rejected");
+  SECItem itemSECItem = UnsafeMapInputBufferToSECItem(item);
+  if (itemSECItem.len >
+        static_cast<decltype(itemSECItem.len)>(
+          std::numeric_limits<int32_t>::max())) {
+    PR_NOT_REACHED("large items should not be possible here");
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
-  SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data,
-                               static_cast<int32_t>(item.len));
+  SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, itemSECItem.data,
+                               static_cast<int32_t>(itemSECItem.len));
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   return Success;
 }
 
 #define MAP_LIST \
     MAP(Result::Success, 0) \
--- a/lib/mozpkix/lib/pkixocsp.cpp
+++ b/lib/mozpkix/lib/pkixocsp.cpp
@@ -22,16 +22,17 @@
  * limitations under the License.
  */
 
 #include <limits>
 
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
+#include "pkixutil.h"
 #include "pkixder.h"
 
 namespace mozilla { namespace pkix {
 
 static const PRTime ONE_DAY
   = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
 static const PRTime SLOP = ONE_DAY;
 
@@ -79,18 +80,18 @@ private:
   void operator=(const Context&); // delete
 };
 
 // Verify that potentialSigner is a valid delegated OCSP response signing cert
 // according to RFC 6960 section 4.2.2.2.
 static Result
 CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
                             BackCert& potentialSigner,
-                            const SECItem& issuerSubject,
-                            const SECItem& issuerSubjectPublicKeyInfo,
+                            InputBuffer issuerSubject,
+                            InputBuffer issuerSubjectPublicKeyInfo,
                             PRTime time)
 {
   Result rv;
 
   // We don't need to do a complete verification of the signer (i.e. we don't
   // have to call BuildCertChain to verify the entire chain) because we
   // already know that the issuer is valid, since revocation checking is done
   // from the root to the parent after we've built a complete chain that we
@@ -123,17 +124,17 @@ CheckOCSPResponseSignerCert(TrustDomain&
     return rv;
   }
 
   // It is possible that there exists a certificate with the same key as the
   // issuer but with a different name, so we need to compare names
   // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name
   // comparison.
   // TODO: needs test
-  if (!SECITEM_ItemsAreEqual(&potentialSigner.GetIssuer(), &issuerSubject)) {
+  if (!InputBuffersAreEqual(potentialSigner.GetIssuer(), issuerSubject)) {
     return Result::ERROR_OCSP_RESPONDER_CERT_INVALID;
   }
 
   // TODO(bug 926260): check name constraints
   rv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(),
                                     issuerSubjectPublicKeyInfo);
 
   // TODO: check for revocation of the OCSP responder certificate unless no-check
@@ -153,89 +154,84 @@ static inline Result OCSPResponse(Input&
 static inline Result ResponseBytes(Input&, Context&);
 static inline Result BasicResponse(Input&, Context&);
 static inline Result ResponseData(
                        Input& tbsResponseData,
                        Context& context,
                        const SignedDataWithSignature& signedResponseData,
                        const DERArray& certs);
 static inline Result SingleResponse(Input& input, Context& context);
-static Result ExtensionNotUnderstood(Input& extnID,
-                                     const SECItem& extnValue,
+static Result ExtensionNotUnderstood(Input& extnID, InputBuffer extnValue,
                                      /*out*/ bool& understood);
 static inline Result CertID(Input& input,
                             const Context& context,
                             /*out*/ bool& match);
 static Result MatchKeyHash(TrustDomain& trustDomain,
-                           const SECItem& issuerKeyHash,
-                           const SECItem& issuerSubjectPublicKeyInfo,
+                           InputBuffer issuerKeyHash,
+                           InputBuffer issuerSubjectPublicKeyInfo,
                            /*out*/ bool& match);
 static Result KeyHash(TrustDomain& trustDomain,
-                      const SECItem& subjectPublicKeyInfo,
+                      InputBuffer subjectPublicKeyInfo,
                       /*out*/ uint8_t* hashBuf, size_t hashBufSize);
 
 static Result
 MatchResponderID(TrustDomain& trustDomain,
                  ResponderIDType responderIDType,
-                 const SECItem& responderIDItem,
-                 const SECItem& potentialSignerSubject,
-                 const SECItem& potentialSignerSubjectPublicKeyInfo,
+                 InputBuffer responderID,
+                 InputBuffer potentialSignerSubject,
+                 InputBuffer potentialSignerSubjectPublicKeyInfo,
                  /*out*/ bool& match)
 {
   match = false;
 
   switch (responderIDType) {
     case ResponderIDType::byName:
       // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name
       // comparison.
-      match = SECITEM_ItemsAreEqual(&responderIDItem, &potentialSignerSubject);
+      match = InputBuffersAreEqual(responderID, potentialSignerSubject);
       return Success;
 
     case ResponderIDType::byKey:
     {
-      Input responderID;
-      Result rv = responderID.Init(responderIDItem.data, responderIDItem.len);
-      if (rv != Success) {
-        return rv;
-      }
-      SECItem keyHash;
-      rv = der::ExpectTagAndGetValue(responderID, der::OCTET_STRING, keyHash);
+      Input input(responderID);
+      InputBuffer keyHash;
+      Result rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, keyHash);
       if (rv != Success) {
         return rv;
       }
       return MatchKeyHash(trustDomain, keyHash,
                           potentialSignerSubjectPublicKeyInfo, match);
     }
 
     default:
       return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
 }
 
 static Result
 VerifyOCSPSignedData(TrustDomain& trustDomain,
                      const SignedDataWithSignature& signedResponseData,
-                     const SECItem& spki)
+                     InputBuffer spki)
 {
   Result rv = trustDomain.VerifySignedData(signedResponseData, spki);
   if (rv == Result::ERROR_BAD_SIGNATURE) {
     rv = Result::ERROR_OCSP_BAD_SIGNATURE;
   }
   return rv;
 }
 
 // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of
 // the cert or it must be a delegated OCSP response signing cert directly
 // issued by the issuer. If the OCSP responder is a delegated OCSP response
 // signer, then its certificate is (probably) embedded within the OCSP
 // response and we'll need to verify that it is a valid certificate that chains
 // *directly* to issuerCert.
 static Result
 VerifySignature(Context& context, ResponderIDType responderIDType,
-                const SECItem& responderID, const DERArray& certs,
+                InputBuffer responderID, const DERArray& certs,
                 const SignedDataWithSignature& signedResponseData)
 {
   bool match;
   Result rv = MatchResponderID(context.trustDomain, responderIDType,
                                responderID, context.certID.issuer,
                                context.certID.issuerSubjectPublicKeyInfo,
                                match);
   if (rv != Success) {
@@ -290,38 +286,33 @@ MapBadDERToMalformedOCSPResponse(Result 
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
   return rv;
 }
 
 Result
 VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
                           PRTime time, uint16_t maxOCSPLifetimeInDays,
-                          const SECItem& encodedResponse,
+                          InputBuffer encodedResponse,
                           /*out*/ bool& expired,
                           /*optional out*/ PRTime* thisUpdate,
                           /*optional out*/ PRTime* validThrough)
 {
   // Always initialize this to something reasonable.
   expired = false;
 
-  Input input;
-  Result rv = input.Init(encodedResponse.data, encodedResponse.len);
+  Context context(trustDomain, certID, time, maxOCSPLifetimeInDays,
+                  thisUpdate, validThrough);
+
+  Input input(encodedResponse);
+  Result rv = der::Nested(input, der::SEQUENCE,
+                          bind(OCSPResponse, _1, ref(context)));
   if (rv != Success) {
     return MapBadDERToMalformedOCSPResponse(rv);
   }
-
-  Context context(trustDomain, certID, time, maxOCSPLifetimeInDays,
-                  thisUpdate, validThrough);
-
-  rv = der::Nested(input, der::SEQUENCE, bind(OCSPResponse, _1, ref(context)));
-  if (rv != Success) {
-    return MapBadDERToMalformedOCSPResponse(rv);
-  }
-
   rv = der::End(input);
   if (rv != Success) {
     return MapBadDERToMalformedOCSPResponse(rv);
   }
 
   expired = context.expired;
 
   switch (context.certStatus) {
@@ -441,17 +432,17 @@ BasicResponse(Input& input, Context& con
     }
     rv = der::End(wrapped);
     if (rv != Success) {
       return rv;
     }
 
     // sequence of certificates
     while (!certsSequence.AtEnd()) {
-      SECItem cert;
+      InputBuffer cert;
       rv = der::ExpectTagAndGetTLV(certsSequence, der::SEQUENCE, cert);
       if (rv != Success) {
         return rv;
       }
       rv = certs.Append(cert);
       if (rv != Success) {
         return rv;
       }
@@ -480,17 +471,17 @@ ResponseData(Input& input, Context& cont
   if (version != der::Version::v1) {
     // TODO: more specific error code for bad version?
     return Result::ERROR_BAD_DER;
   }
 
   // ResponderID ::= CHOICE {
   //    byName              [1] Name,
   //    byKey               [2] KeyHash }
-  SECItem responderID;
+  InputBuffer responderID;
   ResponderIDType responderIDType
     = input.Peek(static_cast<uint8_t>(ResponderIDType::byName))
     ? ResponderIDType::byName
     : ResponderIDType::byKey;
   rv = der::ExpectTagAndGetValue(input, static_cast<uint8_t>(responderIDType),
                                  responderID);
   if (rv != Success) {
     return rv;
@@ -679,64 +670,65 @@ CertID(Input& input, const Context& cont
     if (rv == Result::ERROR_INVALID_ALGORITHM) {
       // Skip entries that are hashed with algorithms we don't support.
       input.SkipToEnd();
       return Success;
     }
     return rv;
   }
 
-  SECItem issuerNameHash;
+  InputBuffer issuerNameHash;
   rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash);
   if (rv != Success) {
     return rv;
   }
 
-  SECItem issuerKeyHash;
+  InputBuffer issuerKeyHash;
   rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash);
   if (rv != Success) {
     return rv;
   }
 
-  SECItem serialNumber;
+  InputBuffer serialNumber;
   rv = der::CertificateSerialNumber(input, serialNumber);
   if (rv != Success) {
     return rv;
   }
 
-  if (!SECITEM_ItemsAreEqual(&serialNumber, &context.certID.serialNumber)) {
+  if (!InputBuffersAreEqual(serialNumber, context.certID.serialNumber)) {
     // This does not reference the certificate we're interested in.
     // Consume the rest of the input and return successfully to
     // potentially continue processing other responses.
     input.SkipToEnd();
     return Success;
   }
 
   // TODO: support SHA-2 hashes.
 
   if (hashAlgorithm != DigestAlgorithm::sha1) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
     return Success;
   }
 
-  if (issuerNameHash.len != TrustDomain::DIGEST_LENGTH) {
+  if (issuerNameHash.GetLength() != TrustDomain::DIGEST_LENGTH) {
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
 
   // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
   // "The hash shall be calculated over the DER encoding of the
   // issuer's name field in the certificate being checked."
   uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
   rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf,
                                      sizeof(hashBuf));
   if (rv != Success) {
     return rv;
   }
-  if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) {
+  InputBuffer computed(hashBuf);
+  if (!InputBuffersAreEqual(computed, issuerNameHash)) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
     return Success;
   }
 
   return MatchKeyHash(context.trustDomain, issuerKeyHash,
                       context.certID.issuerSubjectPublicKeyInfo, match);
 }
@@ -747,35 +739,36 @@ CertID(Input& input, const Context& cont
 //
 // From http://tools.ietf.org/html/rfc6960#appendix-B.1:
 // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
 //                          -- (i.e., the SHA-1 hash of the value of the
 //                          -- BIT STRING subjectPublicKey [excluding
 //                          -- the tag, length, and number of unused
 //                          -- bits] in the responder's certificate)
 static Result
-MatchKeyHash(TrustDomain& trustDomain, const SECItem& keyHash,
-             const SECItem& subjectPublicKeyInfo, /*out*/ bool& match)
+MatchKeyHash(TrustDomain& trustDomain, InputBuffer keyHash,
+             const InputBuffer subjectPublicKeyInfo, /*out*/ bool& match)
 {
-  if (keyHash.len != TrustDomain::DIGEST_LENGTH)  {
+  if (keyHash.GetLength() != TrustDomain::DIGEST_LENGTH)  {
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
   static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
   Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf,
                       sizeof hashBuf);
   if (rv != Success) {
     return rv;
   }
-  match = !memcmp(hashBuf, keyHash.data, keyHash.len);
+  InputBuffer computed(hashBuf);
+  match = InputBuffersAreEqual(computed, keyHash);
   return Success;
 }
 
 // TODO(bug 966856): support SHA-2 hashes
 Result
-KeyHash(TrustDomain& trustDomain, const SECItem& subjectPublicKeyInfo,
+KeyHash(TrustDomain& trustDomain, const InputBuffer subjectPublicKeyInfo,
         /*out*/ uint8_t* hashBuf, size_t hashBufSize)
 {
   if (!hashBuf || hashBufSize != TrustDomain::DIGEST_LENGTH) {
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   // RFC 5280 Section 4.1
   //
@@ -784,60 +777,48 @@ KeyHash(TrustDomain& trustDomain, const 
   //    subjectPublicKey     BIT STRING  }
 
   Input spki;
   Result rv;
 
   {
     // The scope of input is limited to reduce the possibility of confusing it
     // with spki in places we need to be using spki below.
-    Input input;
-    rv = input.Init(subjectPublicKeyInfo.data, subjectPublicKeyInfo.len);
-    if (rv != Success) {
-      return rv;
-    }
+    Input input(subjectPublicKeyInfo);
     rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, spki);
     if (rv != Success) {
       return rv;
     }
     rv = der::End(input);
     if (rv != Success) {
       return rv;
     }
   }
 
   // Skip AlgorithmIdentifier
   rv = der::ExpectTagAndSkipValue(spki, der::SEQUENCE);
   if (rv != Success) {
     return rv;
   }
 
-  SECItem subjectPublicKey;
-  rv = der::ExpectTagAndGetValue(spki, der::BIT_STRING, subjectPublicKey);
+  InputBuffer subjectPublicKey;
+  rv = der::BitStringWithNoUnusedBits(spki, subjectPublicKey);
   if (rv != Success) {
     return rv;
   }
-
   rv = der::End(spki);
   if (rv != Success) {
     return rv;
   }
 
-  // Assume/require that the number of unused bits in the public key is zero.
-  if (subjectPublicKey.len == 0 || subjectPublicKey.data[0] != 0) {
-    return Result::ERROR_BAD_DER;
-  }
-  ++subjectPublicKey.data;
-  --subjectPublicKey.len;
-
   return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize);
 }
 
 Result
-ExtensionNotUnderstood(Input& /*extnID*/, const SECItem& /*extnValue*/,
+ExtensionNotUnderstood(Input& /*extnID*/, InputBuffer /*extnValue*/,
                        /*out*/ bool& understood)
 {
   understood = false;
   return Success;
 }
 
 //   1. The certificate identified in a received response corresponds to
 //      the certificate that was identified in the corresponding request;
@@ -904,22 +885,22 @@ CreateEncodedOCSPRequest(TrustDomain& tr
   // The only way we could have a request this large is if the serialNumber was
   // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST
   // NOT use serialNumber values longer than 20 octets." With this restriction,
   // we allow for some amount of non-conformance with that requirement while
   // still ensuring we can encode the length values in the ASN.1 TLV structures
   // in a single byte.
   static_assert(totalLenWithoutSerialNumberData < OCSP_REQUEST_MAX_LENGTH,
                 "totalLenWithoutSerialNumberData too big");
-  if (certID.serialNumber.len >
+  if (certID.serialNumber.GetLength() >
         OCSP_REQUEST_MAX_LENGTH - totalLenWithoutSerialNumberData) {
     return Result::ERROR_BAD_DER;
   }
 
-  outLen = totalLenWithoutSerialNumberData + certID.serialNumber.len;
+  outLen = totalLenWithoutSerialNumberData + certID.serialNumber.GetLength();
 
   uint8_t totalLen = static_cast<uint8_t>(outLen);
 
   uint8_t* d = out;
   *d++ = 0x30; *d++ = totalLen - 2u;  // OCSPRequest (SEQUENCE)
   *d++ = 0x30; *d++ = totalLen - 4u;  //   tbsRequest (SEQUENCE)
   *d++ = 0x30; *d++ = totalLen - 6u;  //     requestList (SEQUENCE OF)
   *d++ = 0x30; *d++ = totalLen - 8u;  //       Request (SEQUENCE)
@@ -945,19 +926,24 @@ CreateEncodedOCSPRequest(TrustDomain& tr
   rv = KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen);
   if (rv != Success) {
     return rv;
   }
   d += hashLen;
 
   // reqCert.serialNumber (INTEGER)
   *d++ = 0x02; // INTEGER
-  *d++ = static_cast<uint8_t>(certID.serialNumber.len);
-  for (size_t i = 0; i < certID.serialNumber.len; ++i) {
-    *d++ = certID.serialNumber.data[i];
-  }
+  *d++ = static_cast<uint8_t>(certID.serialNumber.GetLength());
+  Input serialNumber(certID.serialNumber);
+  do {
+    rv = serialNumber.Read(*d);
+    if (rv != Success) {
+      return rv;
+    }
+    ++d;
+  } while (!serialNumber.AtEnd());
 
   PR_ASSERT(d == out + totalLen);
 
   return Success;
 }
 
 } } // namespace mozilla::pkix
--- a/lib/mozpkix/lib/pkixutil.h
+++ b/lib/mozpkix/lib/pkixutil.h
@@ -37,116 +37,111 @@ namespace mozilla { namespace pkix {
 //
 // Each BackCert contains pointers to all the given certificate's extensions
 // so that we can parse the extension block once and then process the
 // extensions in an order that may be different than they appear in the cert.
 class BackCert
 {
 public:
   // certDER and childCert must be valid for the lifetime of BackCert.
-  BackCert(const SECItem& certDER, EndEntityOrCA endEntityOrCA,
+  BackCert(InputBuffer certDER, EndEntityOrCA endEntityOrCA,
            const BackCert* childCert)
     : der(certDER)
     , endEntityOrCA(endEntityOrCA)
     , childCert(childCert)
   {
   }
 
   Result Init();
 
-  const SECItem& GetDER() const { return der; }
+  const InputBuffer GetDER() const { return der; }
   const der::Version GetVersion() const { return version; }
   const SignedDataWithSignature& GetSignedData() const { return signedData; }
-  const SECItem& GetIssuer() const { return issuer; }
+  const InputBuffer 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
+  const InputBuffer GetValidity() const { return validity; }
+  const InputBuffer GetSerialNumber() const { return serialNumber; }
+  const InputBuffer GetSubject() const { return subject; }
+  const InputBuffer GetSubjectPublicKeyInfo() const
   {
     return subjectPublicKeyInfo;
   }
-  const SECItem* GetAuthorityInfoAccess() const
+  const InputBuffer* GetAuthorityInfoAccess() const
   {
-    return MaybeSECItem(authorityInfoAccess);
+    return MaybeInputBuffer(authorityInfoAccess);
   }
-  const SECItem* GetBasicConstraints() const
+  const InputBuffer* GetBasicConstraints() const
   {
-    return MaybeSECItem(basicConstraints);
+    return MaybeInputBuffer(basicConstraints);
   }
-  const SECItem* GetCertificatePolicies() const
+  const InputBuffer* GetCertificatePolicies() const
+  {
+    return MaybeInputBuffer(certificatePolicies);
+  }
+  const InputBuffer* GetExtKeyUsage() const
   {
-    return MaybeSECItem(certificatePolicies);
+    return MaybeInputBuffer(extKeyUsage);
   }
-  const SECItem* GetExtKeyUsage() const { return MaybeSECItem(extKeyUsage); }
-  const SECItem* GetKeyUsage() const { return MaybeSECItem(keyUsage); }
-  const SECItem* GetInhibitAnyPolicy() const
+  const InputBuffer* GetKeyUsage() const
   {
-    return MaybeSECItem(inhibitAnyPolicy);
+    return MaybeInputBuffer(keyUsage);
   }
-  const SECItem* GetNameConstraints() const
+  const InputBuffer* GetInhibitAnyPolicy() const
   {
-    return MaybeSECItem(nameConstraints);
+    return MaybeInputBuffer(inhibitAnyPolicy);
+  }
+  const InputBuffer* GetNameConstraints() const
+  {
+    return MaybeInputBuffer(nameConstraints);
   }
 
 private:
-  const SECItem& der;
+  const InputBuffer der;
 
 public:
   const EndEntityOrCA endEntityOrCA;
   BackCert const* const childCert;
 
 private:
   der::Version version;
 
   // 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)
+  static inline const InputBuffer* MaybeInputBuffer(const InputBuffer& item)
   {
-    return item.len > 0 ? &item : nullptr;
+    return item.GetLength() > 0 ? &item : nullptr;
   }
 
-  // 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;
-    }
-  };
-
   SignedDataWithSignature signedData;
-  NonOwningSECItem issuer;
+  InputBuffer 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;
+  InputBuffer validity;
+  InputBuffer serialNumber;
+  InputBuffer subject;
+  InputBuffer subjectPublicKeyInfo;
 
-  NonOwningSECItem authorityInfoAccess;
-  NonOwningSECItem basicConstraints;
-  NonOwningSECItem certificatePolicies;
-  NonOwningSECItem extKeyUsage;
-  NonOwningSECItem inhibitAnyPolicy;
-  NonOwningSECItem keyUsage;
-  NonOwningSECItem nameConstraints;
-  NonOwningSECItem subjectAltName;
+  InputBuffer authorityInfoAccess;
+  InputBuffer basicConstraints;
+  InputBuffer certificatePolicies;
+  InputBuffer extKeyUsage;
+  InputBuffer inhibitAnyPolicy;
+  InputBuffer keyUsage;
+  InputBuffer nameConstraints;
+  InputBuffer subjectAltName;
 
-  Result RememberExtension(Input& extnID, const SECItem& extnValue,
-                                /*out*/ bool& understood);
+  Result RememberExtension(Input& extnID, const InputBuffer& extnValue,
+                           /*out*/ bool& understood);
 
   BackCert(const BackCert&) /* = delete */;
   void operator=(const BackCert&); /* = delete */;
 };
 
 class NonOwningDERArray : public DERArray
 {
 public:
@@ -154,35 +149,38 @@ public:
     : numItems(0)
   {
     // we don't need to initialize the items array because we always check
     // numItems before accessing i.
   }
 
   virtual size_t GetLength() const { return numItems; }
 
-  virtual const SECItem* GetDER(size_t i) const
+  virtual const InputBuffer* GetDER(size_t i) const
   {
     return i < numItems ? &items[i] : nullptr;
   }
 
-  Result Append(const SECItem& der)
+  Result Append(InputBuffer der)
   {
     if (numItems >= MAX_LENGTH) {
       return Result::FATAL_ERROR_INVALID_ARGS;
     }
-    items[numItems] = der; // structure assignment
+    Result rv = items[numItems].Init(der); // structure assignment
+    if (rv != Success) {
+      return rv;
+    }
     ++numItems;
     return Success;
   }
 
   // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT.
   static const size_t MAX_LENGTH = 8;
 private:
-  SECItem items[MAX_LENGTH]; // avoids any heap allocations
+  InputBuffer items[MAX_LENGTH]; // avoids any heap allocations
   size_t numItems;
 
   NonOwningDERArray(const NonOwningDERArray&) /* = delete*/;
   void operator=(const NonOwningDERArray&) /* = delete*/;
 };
 
 } } // namespace mozilla::pkix
 
--- a/lib/mozpkix/test/gtest/pkixbuild_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixbuild_tests.cpp
@@ -28,61 +28,57 @@
 #include "pkixgtest.h"
 #include "pkixtestutil.h"
 #include "prinit.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
-static bool
+// The result is owned by the arena
+static InputBuffer
 CreateCert(PLArenaPool* arena, const char* issuerStr,
            const char* subjectStr, EndEntityOrCA endEntityOrCA,
            /*optional*/ SECKEYPrivateKey* issuerKey,
            /*out*/ ScopedSECKEYPrivateKey& subjectKey,
-           /*out*/ ScopedCERTCertificate& subjectCert)
+           /*out*/ ScopedCERTCertificate* subjectCert = nullptr)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
                                                         serialNumberValue));
-  if (!serialNumber) {
-    return false;
-  }
+  EXPECT_TRUE(serialNumber);
   const SECItem* issuerDER(ASCIIToDERName(arena, issuerStr));
-  if (!issuerDER) {
-    return false;
-  }
+  EXPECT_TRUE(issuerDER);
   const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
-  if (!subjectDER) {
-    return false;
-  }
+  EXPECT_TRUE(subjectDER);
 
   const SECItem* extensions[2] = { nullptr, nullptr };
   if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
     extensions[0] =
       CreateEncodedBasicConstraints(arena, true, nullptr,
                                     ExtensionCriticality::Critical);
-    if (!extensions[0]) {
-      return false;
-    }
+    EXPECT_TRUE(extensions[0]);
   }
 
   SECItem* certDER(CreateEncodedCertificate(
                      arena, v3, SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
                      serialNumber, issuerDER,
                      PR_Now() - ONE_DAY, PR_Now() + ONE_DAY,
                      subjectDER, extensions, issuerKey, SEC_OID_SHA256,
                      subjectKey));
-  if (!certDER) {
-    return false;
+  EXPECT_TRUE(certDER);
+  if (subjectCert) {
+    *subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
+                                           nullptr, false, true);
+    EXPECT_TRUE(*subjectCert);
   }
-  subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
-                                        nullptr, false, true);
-  return subjectCert.get() != nullptr;
+  InputBuffer result;
+  EXPECT_EQ(Success, result.Init(certDER->data, certDER->len));
+  return result;
 }
 
 class TestTrustDomain : public TrustDomain
 {
 public:
   // The "cert chain tail" is a longish chain of certificates that is used by
   // all of the tests here. We share this chain across all the tests in order
   // to speed up the tests (generating keypairs for the certs is very slow).
@@ -99,91 +95,101 @@ public:
     ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     if (!arena) {
       return false;
     }
 
     for (size_t i = 0; i < PR_ARRAY_SIZE(names); ++i) {
       const char* issuerName = i == 0 ? names[0]
                                       : certChainTail[i - 1]->subjectName;
-      if (!CreateCert(arena.get(), issuerName, names[i],
-                      EndEntityOrCA::MustBeCA, leafCAKey.get(), leafCAKey,
-                      certChainTail[i])) {
-        return false;
-      }
+      (void) CreateCert(arena.get(), issuerName, names[i],
+                 EndEntityOrCA::MustBeCA, leafCAKey.get(), leafCAKey,
+                 &certChainTail[i]);
     }
 
     return true;
   }
 
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
-                              const SECItem& candidateCert,
+                              InputBuffer candidateCert,
                               /*out*/ TrustLevel& trustLevel)
   {
-    if (SECITEM_ItemsAreEqual(&candidateCert, &certChainTail[0]->derCert)) {
+    InputBuffer rootDER;
+    Result rv = rootDER.Init(certChainTail[0]->derCert.data,
+                             certChainTail[0]->derCert.len);
+    EXPECT_EQ(Success, rv);
+    if (InputBuffersAreEqual(candidateCert, rootDER)) {
       trustLevel = TrustLevel::TrustAnchor;
     } else {
       trustLevel = TrustLevel::InheritsTrust;
     }
     return Success;
   }
 
-  virtual Result FindIssuer(const SECItem& encodedIssuerName,
+  virtual Result FindIssuer(InputBuffer encodedIssuerName,
                             IssuerChecker& checker, PRTime time)
   {
+    SECItem encodedIssuerNameSECItem =
+      UnsafeMapInputBufferToSECItem(encodedIssuerName);
     ScopedCERTCertList
       candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
-                                            &encodedIssuerName, time, true));
+                                            &encodedIssuerNameSECItem, time,
+                                            true));
     if (candidates) {
       for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
            !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
         bool keepGoing;
-        Result rv = checker.Check(n->cert->derCert,
-                                  nullptr/*additionalNameConstraints*/,
-                                  keepGoing);
+        InputBuffer derCert;
+        Result rv = derCert.Init(n->cert->derCert.data, n->cert->derCert.len);
+        EXPECT_EQ(Success, rv);
+        if (rv != Success) {
+          return rv;
+        }
+        rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
+                           keepGoing);
         if (rv != Success) {
           return rv;
         }
         if (!keepGoing) {
           break;
         }
       }
     }
 
     return Success;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                                 /*optional*/ const SECItem*,
-                                 /*optional*/ const SECItem*)
+                                 /*optional*/ const InputBuffer*,
+                                 /*optional*/ const InputBuffer*)
   {
     return Success;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
-                                  const SECItem& subjectPublicKeyInfo)
+                                  InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+  virtual Result DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 
   // We hold references to CERTCertificates in the cert chain tail so that we
   // CERT_CreateSubjectCertList can find them.
   ScopedCERTCertificate certChainTail[7];
 
@@ -211,63 +217,78 @@ public:
 protected:
   static TestTrustDomain trustDomain;
 };
 
 /*static*/ TestTrustDomain pkixbuild::trustDomain;
 
 TEST_F(pkixbuild, MaxAcceptableCertChainLength)
 {
-  ASSERT_EQ(Success,
-            BuildCertChain(trustDomain, trustDomain.GetLeafCACert()->derCert,
-                           now, EndEntityOrCA::MustBeCA,
-                           KeyUsage::noParticularKeyUsageRequired,
-                           KeyPurposeId::id_kp_serverAuth,
-                           CertPolicyId::anyPolicy,
-                           nullptr/*stapledOCSPResponse*/));
+  {
+    InputBuffer certDER;
+    ASSERT_EQ(Success, certDER.Init(trustDomain.GetLeafCACert()->derCert.data,
+                                    trustDomain.GetLeafCACert()->derCert.len));
+    ASSERT_EQ(Success,
+              BuildCertChain(trustDomain, certDER, now,
+                             EndEntityOrCA::MustBeCA,
+                             KeyUsage::noParticularKeyUsageRequired,
+                             KeyPurposeId::id_kp_serverAuth,
+                             CertPolicyId::anyPolicy,
+                             nullptr/*stapledOCSPResponse*/));
+  }
 
-  ScopedSECKEYPrivateKey privateKey;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(),
-                         trustDomain.GetLeafCACert()->subjectName,
-                         "CN=Direct End-Entity",
-                         EndEntityOrCA::MustBeEndEntity,
-                         trustDomain.leafCAKey.get(), privateKey, cert));
-  ASSERT_EQ(Success,
-            BuildCertChain(trustDomain, cert->derCert, now,
-                           EndEntityOrCA::MustBeEndEntity,
-                           KeyUsage::noParticularKeyUsageRequired,
-                           KeyPurposeId::id_kp_serverAuth,
-                           CertPolicyId::anyPolicy,
-                           nullptr/*stapledOCSPResponse*/));
+  {
+    ScopedSECKEYPrivateKey privateKey;
+    ScopedCERTCertificate cert;
+    InputBuffer certDER(CreateCert(arena.get(),
+                                   trustDomain.GetLeafCACert()->subjectName,
+                                   "CN=Direct End-Entity",
+                                   EndEntityOrCA::MustBeEndEntity,
+                                   trustDomain.leafCAKey.get(), privateKey));
+    ASSERT_EQ(Success,
+              BuildCertChain(trustDomain, certDER, now,
+                             EndEntityOrCA::MustBeEndEntity,
+                             KeyUsage::noParticularKeyUsageRequired,
+                             KeyPurposeId::id_kp_serverAuth,
+                             CertPolicyId::anyPolicy,
+                             nullptr/*stapledOCSPResponse*/));
+  }
 }
 
 TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
 {
+  static char const* const caCertName = "CN=CA Too Far";
   ScopedSECKEYPrivateKey caPrivateKey;
+
+  // We need a CERTCertificate for caCert so that the trustdomain's FindIssuer
+  // method can find it through the NSS cert DB.
   ScopedCERTCertificate caCert;
-  ASSERT_TRUE(CreateCert(arena.get(),
-                         trustDomain.GetLeafCACert()->subjectName,
-                         "CN=CA Too Far", EndEntityOrCA::MustBeCA,
-                         trustDomain.leafCAKey.get(),
-                         caPrivateKey, caCert));
-  ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
-            BuildCertChain(trustDomain, caCert->derCert, now,
-                           EndEntityOrCA::MustBeCA,
-                           KeyUsage::noParticularKeyUsageRequired,
-                           KeyPurposeId::id_kp_serverAuth,
-                           CertPolicyId::anyPolicy,
-                           nullptr/*stapledOCSPResponse*/));
 
-  ScopedSECKEYPrivateKey privateKey;
-  ScopedCERTCertificate cert;
-  ASSERT_TRUE(CreateCert(arena.get(), caCert->subjectName,
-                         "CN=End-Entity Too Far",
-                         EndEntityOrCA::MustBeEndEntity,
-                         caPrivateKey.get(), privateKey, cert));
-  ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
-            BuildCertChain(trustDomain, cert->derCert, now,
-                           EndEntityOrCA::MustBeEndEntity,
-                           KeyUsage::noParticularKeyUsageRequired,
-                           KeyPurposeId::id_kp_serverAuth,
-                           CertPolicyId::anyPolicy,
-                           nullptr/*stapledOCSPResponse*/));
+  {
+    InputBuffer cert(CreateCert(arena.get(),
+                                trustDomain.GetLeafCACert()->subjectName,
+                                caCertName, EndEntityOrCA::MustBeCA,
+                                trustDomain.leafCAKey.get(), caPrivateKey,
+                                &caCert));
+    ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
+              BuildCertChain(trustDomain, cert, now,
+                             EndEntityOrCA::MustBeCA,
+                             KeyUsage::noParticularKeyUsageRequired,
+                             KeyPurposeId::id_kp_serverAuth,
+                             CertPolicyId::anyPolicy,
+                             nullptr/*stapledOCSPResponse*/));
+  }
+
+  {
+    ScopedSECKEYPrivateKey privateKey;
+    InputBuffer cert(CreateCert(arena.get(), caCertName,
+                                "CN=End-Entity Too Far",
+                                EndEntityOrCA::MustBeEndEntity,
+                                caPrivateKey.get(), privateKey));
+    ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
+              BuildCertChain(trustDomain, cert, now,
+                             EndEntityOrCA::MustBeEndEntity,
+                             KeyUsage::noParticularKeyUsageRequired,
+                             KeyPurposeId::id_kp_serverAuth,
+                             CertPolicyId::anyPolicy,
+                             nullptr/*stapledOCSPResponse*/));
+  }
 }
--- a/lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixcert_extension_tests.cpp
@@ -27,100 +27,98 @@
 #include "pkix/pkixnss.h"
 #include "pkixtestutil.h"
 #include "secerr.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 // Creates a self-signed certificate with the given extension.
-static const SECItem*
+static InputBuffer
 CreateCert(PLArenaPool* arena, const char* subjectStr,
            SECItem const* const* extensions, // null-terminated array
            /*out*/ ScopedSECKEYPrivateKey& subjectKey)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
                                                         serialNumberValue));
-  if (!serialNumber) {
-    return nullptr;
-  }
+  EXPECT_TRUE(serialNumber);
   const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
-  if (!issuerDER) {
-    return nullptr;
-  }
+  EXPECT_TRUE(issuerDER);
   const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
-  if (!subjectDER) {
-    return nullptr;
-  }
-
-  return CreateEncodedCertificate(arena, v3,
+  EXPECT_TRUE(subjectDER);
+  SECItem* cert = CreateEncodedCertificate(
+                                  arena, v3,
                                   SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION,
                                   serialNumber, issuerDER,
                                   PR_Now() - ONE_DAY,
                                   PR_Now() + ONE_DAY,
                                   subjectDER, extensions,
                                   nullptr, SEC_OID_SHA256, subjectKey);
+  EXPECT_TRUE(cert);
+  InputBuffer result;
+  EXPECT_EQ(Success, result.Init(cert->data, cert->len));
+  return result;
 }
 
 // Creates a self-signed certificate with the given extension.
-static const SECItem*
+static InputBuffer
 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:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
-                              const SECItem& candidateCert,
+                              InputBuffer candidateCert,
                               /*out*/ TrustLevel& trustLevel)
   {
     trustLevel = TrustLevel::TrustAnchor;
     return Success;
   }
 
-  virtual Result FindIssuer(const SECItem& /*encodedIssuerName*/,
+  virtual Result FindIssuer(InputBuffer /*encodedIssuerName*/,
                             IssuerChecker& /*checker*/, PRTime /*time*/)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                                 /*optional*/ const SECItem*,
-                                 /*optional*/ const SECItem*)
+                                 /*optional*/ const InputBuffer*,
+                                 /*optional*/ const InputBuffer*)
   {
     return Success;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
-                                  const SECItem& subjectPublicKeyInfo)
+                                  InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  virtual Result DigestBuf(const SECItem&, /*out*/ uint8_t*, size_t)
+  virtual Result DigestBuf(InputBuffer, /*out*/ uint8_t*, size_t)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 };
 
 class pkixcert_extension: public NSSTest
 {
 public:
@@ -152,21 +150,20 @@ TEST_F(pkixcert_extension, UnknownCritic
   static const SECItem unknownCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(unknownCriticalExtensionBytes),
     sizeof(unknownCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown Critical Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN,
-                                 &unknownCriticalExtension, key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN,
+                              &unknownCriticalExtension, key));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
@@ -184,21 +181,20 @@ TEST_F(pkixcert_extension, UnknownNonCri
   static const SECItem unknownNonCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(unknownNonCriticalExtensionBytes),
     sizeof(unknownNonCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Unknown NonCritical Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN,
-                                 &unknownNonCriticalExtension, key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN,
+                              &unknownNonCriticalExtension, key));
   ASSERT_EQ(Success,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that an incorrect OID for id-pe-authorityInformationAccess
@@ -217,21 +213,20 @@ TEST_F(pkixcert_extension, WrongOIDCriti
   static const SECItem wrongOIDCriticalExtension = {
     siBuffer,
     const_cast<unsigned char*>(wrongOIDCriticalExtensionBytes),
     sizeof(wrongOIDCriticalExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical Wrong OID Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN,
-                                 &wrongOIDCriticalExtension, key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN,
+                              &wrongOIDCriticalExtension, key));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a id-pe-authorityInformationAccess critical extension
@@ -252,21 +247,19 @@ TEST_F(pkixcert_extension, CriticalAIAEx
   static const SECItem criticalAIAExtension = {
     siBuffer,
     const_cast<unsigned char*>(criticalAIAExtensionBytes),
     sizeof(criticalAIAExtensionBytes)
   };
   const char* certCN = "CN=Cert With Critical AIA Extension";
   ScopedSECKEYPrivateKey key;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN, &criticalAIAExtension,
-                                 key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN, &criticalAIAExtension, key));
   ASSERT_EQ(Success,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
@@ -284,21 +277,20 @@ TEST_F(pkixcert_extension, UnknownCritic
   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;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN,
-                                 &unknownCriticalCEExtension, key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN,
+                              &unknownCriticalCEExtension, key));
   ASSERT_EQ(Result::ERROR_UNKNOWN_CRITICAL_EXTENSION,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // Tests that a certificate with a known critical id-ce extension (in this case,
@@ -316,21 +308,19 @@ TEST_F(pkixcert_extension, KnownCritical
   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;
   // cert is owned by the arena
-  const SECItem* cert(CreateCert(arena.get(), certCN, &criticalCEExtension,
-                                 key));
-  ASSERT_TRUE(cert);
+  InputBuffer cert(CreateCert(arena.get(), certCN, &criticalCEExtension, key));
   ASSERT_EQ(Success,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
 
 // Two subjectAltNames must result in an error.
@@ -349,18 +339,17 @@ TEST_F(pkixcert_extension, DuplicateSubj
     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);
+  InputBuffer cert(CreateCert(arena.get(), certCN, extensions, key));
   ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID,
-            BuildCertChain(trustDomain, *cert, now,
+            BuildCertChain(trustDomain, cert, now,
                            EndEntityOrCA::MustBeEndEntity,
                            KeyUsage::noParticularKeyUsageRequired,
                            KeyPurposeId::anyExtendedKeyUsage,
                            CertPolicyId::anyPolicy,
                            nullptr/*stapledOCSPResponse*/));
 }
--- a/lib/mozpkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
@@ -19,52 +19,48 @@
  * 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 "gtest/gtest.h"
 #include "pkix/pkixtypes.h"
+#include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
 extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA,
-                            const SECItem* encodedKeyUsage,
+                            const InputBuffer* encodedKeyUsage,
                             KeyUsage requiredKeyUsageIfPresent);
 
 } } // namespace mozilla::pkix
 
 class pkixcheck_CheckKeyUsage : public ::testing::Test { };
 
 #define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x)
 
 // Make it easy to define test data for the common, simplest cases.
 #define NAMED_SIMPLE_KU(name, unusedBits, bits) \
   const uint8_t name##_bytes[4] = { \
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \
   }; \
-  const SECItem name = { \
-    siBuffer, \
-    const_cast<uint8_t*>(name##_bytes), \
-    4 \
-  }
+  const InputBuffer name(name##_bytes);
 
-static uint8_t dummy;
-static const SECItem empty_null    = { siBuffer, nullptr, 0 };
-static const SECItem empty_nonnull = { siBuffer, &dummy, 0 };
+static const InputBuffer empty_null;
 
 // Note that keyCertSign is really the only interesting case for CA
 // certificates since we don't support cRLSign.
 
 TEST_F(pkixcheck_CheckKeyUsage, EE_none)
 {
-  // The input SECItem is nullptr. This means the cert had no keyUsage
+  // The input InputBuffer is nullptr. This means the cert had no keyUsage
   // extension. This is always valid because no key usage in an end-entity
   // means that there are no key usage restrictions.
 
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
                                    KeyUsage::noParticularKeyUsageRequired));
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
                                    KeyUsage::digitalSignature));
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
@@ -74,100 +70,88 @@ TEST_F(pkixcheck_CheckKeyUsage, EE_none)
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
                                    KeyUsage::dataEncipherment));
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
                                    KeyUsage::keyAgreement));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, EE_empty)
 {
-  // The input SECItem is empty. The cert had an empty keyUsage extension,
+  // The input InputBuffer is empty. The cert had an empty keyUsage extension,
   // which is syntactically invalid.
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
                            KeyUsage::digitalSignature));
+  static const uint8_t dummy = 0x00;
+  InputBuffer empty_nonnull;
+  ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
                            KeyUsage::digitalSignature));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, CA_none)
 {
   // A CA certificate does not have a KU extension.
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
                                    KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, CA_empty)
 {
   // A CA certificate has an empty KU extension.
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
                            KeyUsage::keyCertSign));
+  static const uint8_t dummy = 0x00;
+  InputBuffer empty_nonnull;
+  ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
                            KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits)
 {
   NAMED_SIMPLE_KU(encoded, 7, 0x80);
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
                                    KeyUsage::digitalSignature));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits)
 {
   static uint8_t oneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80
   };
-  const SECItem oneValueByte = {
-    siBuffer,
-    oneValueByteData,
-    sizeof(oneValueByteData)
-  };
+  static const InputBuffer oneValueByte(oneValueByteData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
                            KeyUsage::digitalSignature));
 
   static uint8_t twoValueBytesData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00
   };
-  const SECItem twoValueBytes = {
-    siBuffer,
-    twoValueBytesData,
-    sizeof(twoValueBytesData)
-  };
+  static const InputBuffer twoValueBytes(twoValueBytesData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
                            KeyUsage::digitalSignature));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_NoPaddingBits)
 {
   static const uint8_t DER_BYTES[] = {
     0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 0/*unused bits*/
   };
-  static const SECItem DER = {
-    siBuffer,
-    const_cast<uint8_t*>(DER_BYTES),
-    sizeof(DER_BYTES)
-  };
-
+  static const InputBuffer DER(DER_BYTES);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
                            KeyUsage::digitalSignature));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
                            KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_7PaddingBits)
 {
   static const uint8_t DER_BYTES[] = {
     0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 7/*unused bits*/
   };
-  static const SECItem DER = {
-    siBuffer,
-    const_cast<uint8_t*>(DER_BYTES),
-    sizeof(DER_BYTES)
-  };
-
+  static const InputBuffer DER(DER_BYTES);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
                            KeyUsage::digitalSignature));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
                            KeyUsage::keyCertSign));
 }
 
 void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage)
 {
@@ -186,24 +170,22 @@ void ASSERT_SimpleCase(uint8_t unusedBit
   // key usage in the single-byte value case.
   NAMED_SIMPLE_KU(notGood, unusedBits,
                   static_cast<uint8_t>((~bits >> unusedBits) << unusedBits));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood, usage));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood, usage));
 
   // Test that none of the other non-padding bits are mistaken for the given
   // key usage in the two-byte value case.
-  uint8_t twoByteNotGoodData[] = {
+  const uint8_t twoByteNotGoodData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits,
     static_cast<uint8_t>(~bits),
     static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits)
   };
-  const SECItem twoByteNotGood = {
-    siBuffer, twoByteNotGoodData, sizeof(twoByteNotGoodData)
-  };
+  InputBuffer twoByteNotGood(twoByteNotGoodData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
                            usage));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, simpleCases)
 {
   ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature);
@@ -230,81 +212,63 @@ TEST_F(pkixcheck_CheckKeyUsage, keyCertS
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood,
                            KeyUsage::keyCertSign));
 
   // Test that none of the other non-padding bits are mistaken for the given
   // key usage in the two-byte value case.
   static uint8_t twoByteNotGoodData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu
   };
-  static const SECItem twoByteNotGood = {
-    siBuffer, twoByteNotGoodData, sizeof(twoByteNotGoodData)
-  };
+  static const InputBuffer twoByteNotGood(twoByteNotGoodData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
                            KeyUsage::keyCertSign));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood,
                            KeyUsage::keyCertSign));
 }
 
 TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero)
 {
   // single byte control case
   static uint8_t controlOneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
   };
-  const SECItem controlOneValueByte = {
-    siBuffer,
-    controlOneValueByteData,
-    sizeof(controlOneValueByteData)
-  };
+  static const InputBuffer controlOneValueByte(controlOneValueByteData);
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                    &controlOneValueByte,
                                    KeyUsage::digitalSignature));
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
                                    &controlOneValueByte,
                                    KeyUsage::digitalSignature));
 
   // single-byte test case
   static uint8_t oneValueByteData[] = {
     0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01
   };
-  const SECItem oneValueByte = {
-    siBuffer,
-    oneValueByteData,
-    sizeof(oneValueByteData)
-  };
+  static const InputBuffer oneValueByte(oneValueByteData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
                            KeyUsage::digitalSignature));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte,
                            KeyUsage::digitalSignature));
 
   // two-byte control case
   static uint8_t controlTwoValueBytesData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
     0x80 | 0x01, 0x80
   };
-  const SECItem controlTwoValueBytes = {
-    siBuffer,
-    controlTwoValueBytesData,
-    sizeof(controlTwoValueBytesData)
-  };
+  static const InputBuffer controlTwoValueBytes(controlTwoValueBytesData);
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                    &controlTwoValueBytes,
                                    KeyUsage::digitalSignature));
   ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
                                    &controlTwoValueBytes,
                                    KeyUsage::digitalSignature));
 
   // two-byte test case
   static uint8_t twoValueBytesData[] = {
     0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
     0x80 | 0x01, 0x80 | 0x01
   };
-  const SECItem twoValueBytes = {
-    siBuffer,
-    twoValueBytesData,
-    sizeof(twoValueBytesData)
-  };
+  static const InputBuffer twoValueBytes(twoValueBytesData);
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
                            KeyUsage::digitalSignature));
   ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes,
                            KeyUsage::digitalSignature));
 }
--- a/lib/mozpkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
@@ -26,17 +26,17 @@
 #include "pkix/pkixtypes.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
-Result CheckValidity(const SECItem& encodedValidity, PRTime time);
+Result CheckValidity(const InputBuffer encodedValidity, PRTime time);
 
 } } // namespace mozilla::pkix
 
 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 */ \
@@ -64,106 +64,79 @@ static const PRTime FUTURE_TIME(YMDHMS(2
 class pkixcheck_CheckValidity : public ::testing::Test { };
 
 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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(Result::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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(Result::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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(Result::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)
-};
+static const InputBuffer
+OLDER_UTCTIME_NEWER_UTCTIME(OLDER_UTCTIME_NEWER_UTCTIME_DATA);
 
 TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
 {
   ASSERT_EQ(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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(Success, CheckValidity(validity, NOW));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
 {
   ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
             CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, PAST_TIME));
 }
@@ -175,15 +148,11 @@ TEST_F(pkixcheck_CheckValidity, InvalidA
 }
 
 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)
-  };
+  static const InputBuffer validity(DER);
   ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(validity, NOW));
 }
--- a/lib/mozpkix/test/gtest/pkixder_input_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixder_input_tests.cpp
@@ -81,134 +81,102 @@ const uint8_t DER_OVERRUN_SEQUENCE_OF_IN
 };
 
 const uint8_t DER_INT16[] = {
   0x02,                       // INTEGER
   0x02,                       // length
   0x12, 0x34                  // 0x1234
 };
 
-TEST_F(pkixder_input_tests, InputInit)
+TEST_F(pkixder_input_tests, InputBufferInit)
 {
-  Input input;
+  InputBuffer buf;
   ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 }
 
-TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
+TEST_F(pkixder_input_tests, InputBufferInitWithNullPointerOrZeroLength)
 {
-  Input input;
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 0));
+  InputBuffer buf;
+  ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 0));
 
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init(nullptr, 100));
+  ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 100));
 
   // Though it seems odd to initialize with zero-length and non-null ptr, this
   // is working as intended. The Input class was intended to protect against
   // buffer overflows, and there's no risk with the current behavior. See bug
   // 1000354.
-  ASSERT_EQ(Success, input.Init((const uint8_t*) "hello", 0));
-  ASSERT_TRUE(input.AtEnd());
-}
-
-TEST_F(pkixder_input_tests, InputInitWithLargeData)
-{
-  Input input;
-  // Data argument length does not matter, it is not touched, just
-  // needs to be non-null
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Init((const uint8_t*) "", 0xffff+1));
-
-  ASSERT_EQ(Success, input.Init((const uint8_t*) "", 0xffff));
-}
-
-TEST_F(pkixder_input_tests, InputInitMultipleTimes)
-{
-  Input input;
-
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
-
-  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  ASSERT_EQ(Success, buf.Init((const uint8_t*) "hello", 0));
+  ASSERT_TRUE(buf.GetLength() == 0);
 }
 
-TEST_F(pkixder_input_tests, ExpectSuccess)
+TEST_F(pkixder_input_tests, InputBufferInitWithLargeData)
 {
-  Input input;
+  InputBuffer buf;
+  // Data argument length does not matter, it is not touched, just
+  // needs to be non-null
+  ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init((const uint8_t*) "", 0xffff+1));
 
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
-  ASSERT_EQ(Success,
-            input.Expect(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
-  ASSERT_TRUE(input.AtEnd());
+  ASSERT_EQ(Success, buf.Init((const uint8_t*) "", 0xffff));
 }
 
-TEST_F(pkixder_input_tests, ExpectMismatch)
+TEST_F(pkixder_input_tests, InputBufferInitMultipleTimes)
 {
-  Input input;
+  InputBuffer buf;
 
   ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
-
-  const uint8_t expected[] = { 0x11, 0x22 };
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected));
-}
+            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 
-TEST_F(pkixder_input_tests, ExpectTooMuch)
-{
-  Input input;
-
-  const uint8_t der[] = { 0x11, 0x22 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
-
-  const uint8_t expected[] = { 0x11, 0x22, 0x33 };
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Expect(expected, sizeof expected));
+  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
+            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
 }
 
 TEST_F(pkixder_input_tests, PeekWithinBounds)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x11 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   ASSERT_TRUE(input.Peek(0x11));
   ASSERT_FALSE(input.Peek(0x22));
 }
 
 TEST_F(pkixder_input_tests, PeekPastBounds)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22 };
-  ASSERT_EQ(Success, input.Init(der, 1));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 1));
+  Input input(buf);
 
   uint8_t readByte;
   ASSERT_EQ(Success, input.Read(readByte));
   ASSERT_EQ(0x11, readByte);
   ASSERT_FALSE(input.Peek(0x22));
 }
 
 TEST_F(pkixder_input_tests, ReadByte)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   uint8_t readByte1;
   ASSERT_EQ(Success, input.Read(readByte1));
   ASSERT_EQ(0x11, readByte1);
 
   uint8_t readByte2;
   ASSERT_EQ(Success, input.Read(readByte2));
   ASSERT_EQ(0x22, readByte2);
 }
 
 TEST_F(pkixder_input_tests, ReadBytePastEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22 };
-  // Initialize with too-short length
-  ASSERT_EQ(Success, input.Init(der, 1));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 1));
+  Input input(buf);
 
   uint8_t readByte1 = 0;
   ASSERT_EQ(Success, input.Read(readByte1));
   ASSERT_EQ(0x11, readByte1);
 
   uint8_t readByte2 = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readByte2));
   ASSERT_NE(0x22, readByte2);
@@ -219,122 +187,126 @@ TEST_F(pkixder_input_tests, ReadByteWrap
   // The original implementation of our buffer read overflow checks was
   // susceptible to integer overflows which could make the checks ineffective.
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
-  Input input;
-  ASSERT_EQ(Success, input.Init(der, 0));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 0));
+  Input input(buf);
+
   uint8_t b;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
 }
 
 TEST_F(pkixder_input_tests, ReadWord)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   uint16_t readWord1 = 0;
   ASSERT_EQ(Success, input.Read(readWord1));
   ASSERT_EQ(0x1122, readWord1);
 
   uint16_t readWord2 = 0;
   ASSERT_EQ(Success, input.Read(readWord2));
   ASSERT_EQ(0x3344, readWord2);
 }
 
 TEST_F(pkixder_input_tests, ReadWordPastEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  // Initialize with too-short length
-  ASSERT_EQ(Success, input.Init(der, 2));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 2)); // Initialize with too-short length
+  Input input(buf);
 
   uint16_t readWord1 = 0;
   ASSERT_EQ(Success, input.Read(readWord1));
   ASSERT_EQ(0x1122, readWord1);
 
   uint16_t readWord2 = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord2));
   ASSERT_NE(0x3344, readWord2);
 }
 
 TEST_F(pkixder_input_tests, ReadWordWithInsufficentData)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22 };
-  ASSERT_EQ(Success, input.Init(der, 1));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 1));
+  Input input(buf);
 
   uint16_t readWord1 = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1));
   ASSERT_NE(0x1122, readWord1);
 }
 
 TEST_F(pkixder_input_tests, ReadWordWrapAroundPointer)
 {
   // The original implementation of our buffer read overflow checks was
   // susceptible to integer overflows which could make the checks ineffective.
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
-  Input input;
-  ASSERT_EQ(Success, input.Init(der, 0));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 0));
+  Input input(buf);
   uint16_t b;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
 }
 
 TEST_F(pkixder_input_tests, InputSkip)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   ASSERT_EQ(Success, input.Skip(1));
 
   uint8_t readByte1 = 0;
   ASSERT_EQ(Success, input.Read(readByte1));
   ASSERT_EQ(0x22, readByte1);
 
   ASSERT_EQ(Success, input.Skip(1));
 
   uint8_t readByte2 = 0;
   ASSERT_EQ(Success, input.Read(readByte2));
   ASSERT_EQ(0x44, readByte2);
 }
 
 TEST_F(pkixder_input_tests, InputSkipToEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   ASSERT_EQ(Success, input.Skip(sizeof der));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, InputSkipPastEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1));
 }
 
 TEST_F(pkixder_input_tests, InputSkipToNewInput)
 {
-  Input input;
   const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   Input skippedInput;
   ASSERT_EQ(Success, input.Skip(3, skippedInput));
 
   uint8_t readByte1 = 0;
   ASSERT_EQ(Success, input.Read(readByte1));
   ASSERT_EQ(0x04, readByte1);
 
@@ -349,409 +321,374 @@ TEST_F(pkixder_input_tests, InputSkipToN
     ASSERT_EQ(i, readByte);
   }
 
   ASSERT_TRUE(skippedInput.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, InputSkipToNewInputPastEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   Input skippedInput;
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput));
 }
 
-TEST_F(pkixder_input_tests, InputSkipToSECItem)
+TEST_F(pkixder_input_tests, InputSkipToInputBuffer)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
 
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Success, input.Skip(sizeof expectedItemData, item));
-  ASSERT_EQ(siBuffer, item.type);
-  ASSERT_EQ(sizeof expectedItemData, item.len);
-  ASSERT_EQ(der, item.data);
-  ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
+
+  InputBuffer expected(expectedItemData);
+  ASSERT_TRUE(InputBuffersAreEqual(expected, item));
 }
 
 TEST_F(pkixder_input_tests, SkipWrapAroundPointer)
 {
   // The original implementation of our buffer read overflow checks was
   // susceptible to integer overflows which could make the checks ineffective.
   // This attempts to verify that we've fixed that. Unfortunately, decrementing
   // a null pointer is undefined behavior according to the C++ language spec.,
   // but this should catch the problem on at least some compilers, if not all of
   // them.
   const uint8_t* der = nullptr;
   --der;
-  Input input;
-  ASSERT_EQ(Success, input.Init(der, 0));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 0));
+  Input input(buf);
   ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1));
 }
 
-TEST_F(pkixder_input_tests, SkipToSECItemPastEnd)
+TEST_F(pkixder_input_tests, SkipToInputBufferPastEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
-  SECItem skippedSECItem;
-  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skippedSECItem));
+  InputBuffer skipped;
+  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skipped));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
   ASSERT_EQ(Success, End(input));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OVERRUN_SEQUENCE_OF_INT8,
-                                sizeof DER_OVERRUN_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_OVERRUN_SEQUENCE_OF_INT8);
+  Input input(buf);
   ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
   ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput)
 {
   Input input;
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, AtEndAtBeginning)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   ASSERT_FALSE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, AtEndAtEnd)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   ASSERT_EQ(Success, input.Skip(sizeof der));
   ASSERT_TRUE(input.AtEnd());
 }
 
-TEST_F(pkixder_input_tests, MarkAndGetSECItem)
+TEST_F(pkixder_input_tests, MarkAndGetInputBuffer)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   Input::Mark mark = input.GetMark();
 
   const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
 
   ASSERT_EQ(Success, input.Skip(sizeof expectedItemData));
 
-  SECItem item;
-  memset(&item, 0x00, sizeof item);
-
-  ASSERT_EQ(Success, input.GetSECItem(siBuffer, mark, item));
-  ASSERT_EQ(siBuffer, item.type);
-  ASSERT_EQ(sizeof expectedItemData, item.len);
-  ASSERT_TRUE(item.data);
-  ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
+  InputBuffer item;
+  ASSERT_EQ(Success, input.GetInputBuffer(mark, item));
+  InputBuffer expected(expectedItemData);
+  ASSERT_TRUE(InputBuffersAreEqual(expected, item));
 }
 
 // Cannot run this test on debug builds because of the PR_NOT_REACHED
 #ifndef DEBUG
-TEST_F(pkixder_input_tests, MarkAndGetSECItemDifferentInput)
+TEST_F(pkixder_input_tests, MarkAndGetInputBufferDifferentInput)
 {
-  Input input;
   const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
 
   Input another;
   Input::Mark mark = another.GetMark();
 
   ASSERT_EQ(Success, input.Skip(3));
 
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
-            input.GetSECItem(siBuffer, mark, item));
+            input.GetInputBuffer(mark, item));
 }
 #endif
 
 TEST_F(pkixder_input_tests, ExpectTagAndLength)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   ASSERT_EQ(Success, ExpectTagAndLength(input, SEQUENCE,
                                         sizeof DER_SEQUENCE_OF_INT8 - 2));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongLength)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  InputBuffer buf(DER_INT16);
+  Input input(buf);
 
   // Wrong length
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, INTEGER, 4));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongTag)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  InputBuffer buf(DER_INT16);
+  Input input(buf);
 
   // Wrong type
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndLength(input, OCTET_STRING, 2));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLength)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   uint16_t length = 0;
   ASSERT_EQ(Success,
             der::internal::ExpectTagAndGetLength(input, SEQUENCE, length));
   ASSERT_EQ(sizeof DER_SEQUENCE_OF_INT8 - 2, length);
   ASSERT_EQ(Success, input.Skip(length));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongTag)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   uint16_t length = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             der::internal::ExpectTagAndGetLength(input, INTEGER, length));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongLength)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   uint16_t length = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             der::internal::ExpectTagAndGetLength(input, SEQUENCE, length));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
+  InputBuffer buf(DER_SEQUENCE_EMPTY);
+  Input input(buf);
   Input value;
   ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
   ASSERT_TRUE(value.AtEnd());
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidNotEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
   Input value;
   ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
   ASSERT_TRUE(value.MatchRest(DER_SEQUENCE_NOT_EMPTY_VALUE));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests,
        ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
-                       sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
+  Input input(buf);
   Input value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
   Input value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
 TEST_F(pkixder_input_tests, ExpectTagAndGetLength_Input_InvalidWrongTag)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
   Input value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, INTEGER, value));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_ValidEmpty)
+TEST_F(pkixder_input_tests, ExpectTagAndGetValue_InputBuffer_ValidEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
-  SECItem value = { siBuffer, nullptr, 5 };
+  InputBuffer buf(DER_SEQUENCE_EMPTY);
+  Input input(buf);
+  InputBuffer value;
   ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
-  ASSERT_EQ(0u, value.len);
+  ASSERT_EQ(0u, value.GetLength());
   ASSERT_TRUE(input.AtEnd());
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_ValidNotEmpty)
+TEST_F(pkixder_input_tests, ExpectTagAndGetValue_InputBuffer_ValidNotEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
-  SECItem value;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
+  InputBuffer value;
   ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
-  ASSERT_EQ(sizeof(DER_SEQUENCE_NOT_EMPTY_VALUE), value.len);
-  ASSERT_TRUE(value.data);
-  ASSERT_FALSE(memcmp(value.data, DER_SEQUENCE_NOT_EMPTY_VALUE,
-                      sizeof(DER_SEQUENCE_NOT_EMPTY_VALUE)));
+  InputBuffer expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
+  ASSERT_TRUE(InputBuffersAreEqual(expected, value));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests,
-       ExpectTagAndGetValue_SECItem_InvalidNotEmptyValueTruncated)
+       ExpectTagAndGetValue_InputBuffer_InvalidNotEmptyValueTruncated)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
-                       sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
-  SECItem value;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
+  Input input(buf);
+  InputBuffer value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetValue_SECItem_InvalidWrongLength)
+TEST_F(pkixder_input_tests, ExpectTagAndGetValue_InputBuffer_InvalidWrongLength)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
-  SECItem value;
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
+  InputBuffer value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, SEQUENCE, value));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetLength_SECItem_InvalidWrongTag)
+TEST_F(pkixder_input_tests, ExpectTagAndGetLength_InputBuffer_InvalidWrongTag)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
-  SECItem value;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
+  InputBuffer value;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             ExpectTagAndGetValue(input, INTEGER, value));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_ValidEmpty)
+TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_InputBuffer_ValidEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_EMPTY, sizeof DER_SEQUENCE_EMPTY));
-  SECItem tlv = { siBuffer, nullptr, 5 };
+  InputBuffer buf(DER_SEQUENCE_EMPTY);
+  Input input(buf);
+  InputBuffer tlv;
   ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
-  ASSERT_EQ(sizeof DER_SEQUENCE_EMPTY, tlv.len);
-  ASSERT_TRUE(tlv.data);
-  ASSERT_FALSE(memcmp(tlv.data, DER_SEQUENCE_EMPTY,
-                      sizeof DER_SEQUENCE_EMPTY));
+  InputBuffer expected(DER_SEQUENCE_EMPTY);
+  ASSERT_TRUE(InputBuffersAreEqual(expected, tlv));
   ASSERT_TRUE(input.AtEnd());
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_ValidNotEmpty)
+TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_InputBuffer_ValidNotEmpty)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
-  SECItem tlv;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
+  InputBuffer tlv;
   ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
-  ASSERT_EQ(sizeof(DER_SEQUENCE_NOT_EMPTY), tlv.len);
-  ASSERT_TRUE(tlv.data);
-  ASSERT_FALSE(memcmp(tlv.data, DER_SEQUENCE_NOT_EMPTY,
-                      sizeof(DER_SEQUENCE_NOT_EMPTY)));
+  InputBuffer expected(DER_SEQUENCE_NOT_EMPTY);
+  ASSERT_TRUE(InputBuffersAreEqual(expected, tlv));
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests,
-       ExpectTagAndGetTLV_SECItem_InvalidNotEmptyValueTruncated)
+       ExpectTagAndGetTLV_InputBuffer_InvalidNotEmptyValueTruncated)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED,
-                       sizeof DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED));
-  SECItem tlv;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
+  Input input(buf);
+  InputBuffer tlv;
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongLength)
+TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_InputBuffer_InvalidWrongLength)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
-  SECItem tlv;
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
+  InputBuffer tlv;
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
 }
 
-TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_SECItem_InvalidWrongTag)
+TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_InputBuffer_InvalidWrongTag)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_NOT_EMPTY, sizeof DER_SEQUENCE_NOT_EMPTY));
-  SECItem tlv;
+  InputBuffer buf(DER_SEQUENCE_NOT_EMPTY);
+  Input input(buf);
+  InputBuffer tlv;
   ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv));
 }
 
 TEST_F(pkixder_input_tests, EndAtEnd)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  InputBuffer buf(DER_INT16);
+  Input input(buf);
   ASSERT_EQ(Success, input.Skip(4));
   ASSERT_EQ(Success, End(input));
 }
 
 TEST_F(pkixder_input_tests, EndBeforeEnd)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  InputBuffer buf(DER_INT16);
+  Input input(buf);
   ASSERT_EQ(Success, input.Skip(2));
   ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 TEST_F(pkixder_input_tests, EndAtBeginning)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  InputBuffer buf(DER_INT16);
+  Input input(buf);
   ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
 }
 
 // TODO: Need tests for Nested too?
 
 Result NestedOfHelper(Input& input, std::vector<uint8_t>& readValues)
 {
   uint8_t value = 0;
@@ -761,87 +698,86 @@ Result NestedOfHelper(Input& input, std:
     return rv;
   }
   readValues.push_back(value);
   return Success;
 }
 
 TEST_F(pkixder_input_tests, NestedOf)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   std::vector<uint8_t> readValues;
   ASSERT_EQ(Success,
     NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
              mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
                                  mozilla::pkix::ref(readValues))));
   ASSERT_EQ((size_t) 3, readValues.size());
   ASSERT_EQ(0x01, readValues[0]);
   ASSERT_EQ(0x02, readValues[1]);
   ASSERT_EQ(0x03, readValues[2]);
   ASSERT_EQ(Success, End(input));
 }
 
 TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
 {
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
-                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+  InputBuffer buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
+  Input input(buf);
 
   std::vector<uint8_t> readValues;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
                      mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
                                          mozilla::pkix::ref(readValues))));
   ASSERT_EQ((size_t) 0, readValues.size());
 }
 
 TEST_F(pkixder_input_tests, MatchRestAtEnd)
 {
-  Input input;
   static const uint8_t der[1] = { };
-  ASSERT_EQ(Success, input.Init(der, 0));
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(der, 0));
+  Input input(buf);
   ASSERT_TRUE(input.AtEnd());
   static const uint8_t toMatch[] = { 1 };
   ASSERT_FALSE(input.MatchRest(toMatch));
 }
 
 TEST_F(pkixder_input_tests, MatchRest1Match)
 {
-  Input input;
   static const uint8_t der[] = { 1 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   ASSERT_FALSE(input.AtEnd());
   ASSERT_TRUE(input.MatchRest(der));
 }
 
 TEST_F(pkixder_input_tests, MatchRest1Mismatch)
 {
-  Input input;
   static const uint8_t der[] = { 1 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   static const uint8_t toMatch[] = { 2 };
   ASSERT_FALSE(input.MatchRest(toMatch));
   ASSERT_FALSE(input.AtEnd());
 }
 
 TEST_F(pkixder_input_tests, MatchRest2WithTrailingByte)
 {
-  Input input;
   static const uint8_t der[] = { 1, 2, 3 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   static const uint8_t toMatch[] = { 1, 2 };
   ASSERT_FALSE(input.MatchRest(toMatch));
 }
 
 TEST_F(pkixder_input_tests, MatchRest2Mismatch)
 {
-  Input input;
   static const uint8_t der[] = { 1, 2, 3 };
-  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  InputBuffer buf(der);
+  Input input(buf);
   static const uint8_t toMatchMismatch[] = { 1, 3 };
   ASSERT_FALSE(input.MatchRest(toMatchMismatch));
   ASSERT_TRUE(input.MatchRest(der));
 }
 
 } // unnamed namespace
--- a/lib/mozpkix/test/gtest/pkixder_pki_types_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -39,171 +39,155 @@ class pkixder_pki_types_tests : public :
 
 TEST_F(pkixder_pki_types_tests, CertificateSerialNumber)
 {
   const uint8_t DER_CERT_SERIAL[] = {
     0x02,                       // INTEGER
     8,                          // length
     0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
   };
+  InputBuffer buf(DER_CERT_SERIAL);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL, sizeof DER_CERT_SERIAL));
-
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Success, CertificateSerialNumber(input, item));
 
-  ASSERT_EQ(sizeof DER_CERT_SERIAL - 2, item.len);
-  ASSERT_TRUE(memcmp(item.data, DER_CERT_SERIAL + 2,
-                     sizeof DER_CERT_SERIAL - 2) == 0);
+  InputBuffer expected;
+  ASSERT_EQ(Success,
+            expected.Init(DER_CERT_SERIAL + 2, sizeof DER_CERT_SERIAL - 2));
+  ASSERT_TRUE(InputBuffersAreEqual(expected, item));
 }
 
 TEST_F(pkixder_pki_types_tests, CertificateSerialNumberLongest)
 {
   const uint8_t DER_CERT_SERIAL_LONGEST[] = {
     0x02,                       // INTEGER
     20,                         // length
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
   };
+  InputBuffer buf(DER_CERT_SERIAL_LONGEST);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_LONGEST,
-                                sizeof DER_CERT_SERIAL_LONGEST));
-
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Success, CertificateSerialNumber(input, item));
 
-  ASSERT_EQ(sizeof DER_CERT_SERIAL_LONGEST - 2, item.len);
-  ASSERT_TRUE(memcmp(item.data, DER_CERT_SERIAL_LONGEST + 2,
-                     sizeof DER_CERT_SERIAL_LONGEST - 2) == 0);
+  InputBuffer expected;
+  ASSERT_EQ(Success,
+            expected.Init(DER_CERT_SERIAL_LONGEST + 2,
+                          sizeof DER_CERT_SERIAL_LONGEST - 2));
+  ASSERT_TRUE(InputBuffersAreEqual(expected, item));
 }
 
 TEST_F(pkixder_pki_types_tests, CertificateSerialNumberCrazyLong)
 {
   const uint8_t DER_CERT_SERIAL_CRAZY_LONG[] = {
     0x02,                       // INTEGER
     32,                         // length
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
     17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
   };
+  InputBuffer buf(DER_CERT_SERIAL_CRAZY_LONG);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_CRAZY_LONG,
-                                sizeof DER_CERT_SERIAL_CRAZY_LONG));
-
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Success, CertificateSerialNumber(input, item));
 }
 
 TEST_F(pkixder_pki_types_tests, CertificateSerialNumberZeroLength)
 {
   const uint8_t DER_CERT_SERIAL_ZERO_LENGTH[] = {
     0x02,                       // INTEGER
     0x00                        // length
   };
+  InputBuffer buf(DER_CERT_SERIAL_ZERO_LENGTH);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_ZERO_LENGTH,
-                                sizeof DER_CERT_SERIAL_ZERO_LENGTH));
-
-  SECItem item;
+  InputBuffer item;
   ASSERT_EQ(Result::ERROR_BAD_DER, CertificateSerialNumber(input, item));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed)
 {
   const uint8_t DER_OPTIONAL_VERSION_V1[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x00              // INTEGER(0)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_V1,
-                                sizeof DER_OPTIONAL_VERSION_V1));
+  InputBuffer buf(DER_OPTIONAL_VERSION_V1);
+  Input input(buf);
 
   // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
   // do here for compatibility reasons.
   // Version version;
   // ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
   der::Version version = der::Version::v3;
   ASSERT_EQ(Success, OptionalVersion(input, version));
   ASSERT_EQ(der::Version::v1, version);
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV2)
 {
   const uint8_t DER_OPTIONAL_VERSION_V2[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x01              // INTEGER(1)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_V2,
-                                sizeof DER_OPTIONAL_VERSION_V2));
+  InputBuffer buf(DER_OPTIONAL_VERSION_V2);
+  Input input(buf);
 
   der::Version version = der::Version::v1;
   ASSERT_EQ(Success, OptionalVersion(input, version));
   ASSERT_EQ(der::Version::v2, version);
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionV3)
 {
   const uint8_t DER_OPTIONAL_VERSION_V3[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x02              // INTEGER(2)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_V3,
-                                sizeof DER_OPTIONAL_VERSION_V3));
+  InputBuffer buf(DER_OPTIONAL_VERSION_V3);
+  Input input(buf);
 
   der::Version version = der::Version::v1;
   ASSERT_EQ(Success, OptionalVersion(input, version));
   ASSERT_EQ(der::Version::v3, version);
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionUnknown)
 {
   const uint8_t DER_OPTIONAL_VERSION_INVALID[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x01, 0x42              // INTEGER(0x42)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID,
-                                sizeof DER_OPTIONAL_VERSION_INVALID));
+  InputBuffer buf(DER_OPTIONAL_VERSION_INVALID);
+  Input input(buf);
 
   der::Version version = der::Version::v1;
   ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionInvalidTooLong)
 {
   const uint8_t DER_OPTIONAL_VERSION_INVALID_TOO_LONG[] = {
     0xa0, 0x03,                   // context specific 0
     0x02, 0x02, 0x12, 0x34        // INTEGER(0x1234)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_INVALID_TOO_LONG,
-                                sizeof DER_OPTIONAL_VERSION_INVALID_TOO_LONG));
+  InputBuffer buf(DER_OPTIONAL_VERSION_INVALID_TOO_LONG);
+  Input input(buf);
 
   der::Version version;
   ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(input, version));
 }
 
 TEST_F(pkixder_pki_types_tests, OptionalVersionMissing)
 {
   const uint8_t DER_OPTIONAL_VERSION_MISSING[] = {
     0x02, 0x11, 0x22              // INTEGER
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_OPTIONAL_VERSION_MISSING,
-                                sizeof DER_OPTIONAL_VERSION_MISSING));
+  InputBuffer buf(DER_OPTIONAL_VERSION_MISSING);
+  Input input(buf);
 
   der::Version version = der::Version::v3;
   ASSERT_EQ(Success, OptionalVersion(input, version));
   ASSERT_EQ(der::Version::v1, version);
 }
 
 static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13;
 
@@ -246,33 +230,35 @@ DIGEST_ALGORITHM_TEST_INFO[] = {
   },
 };
 
 TEST_P(pkixder_DigestAlgorithmIdentifier, Valid)
 {
   const AlgorithmIdentifierTestInfo<DigestAlgorithm>& param(GetParam());
 
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(param.der, param.derLength));
+    InputBuffer buf;
+    ASSERT_EQ(Success, buf.Init(param.der, param.derLength));
+    Input input(buf);
     DigestAlgorithm alg;
     ASSERT_EQ(Success, DigestAlgorithmIdentifier(input, alg));
     ASSERT_EQ(param.algorithm, alg);
     ASSERT_EQ(Success, End(input));
   }
 
   {
     uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
     memcpy(derWithNullParam, param.der, param.derLength);
     derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes
     derWithNullParam[param.derLength] = 0x05; // NULL tag
     derWithNullParam[param.derLength + 1] = 0x00; // length zero
 
-    Input input;
-    ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
+    InputBuffer buf;
+    ASSERT_EQ(Success, buf.Init(derWithNullParam, param.derLength + 2));
+    Input input(buf);
     DigestAlgorithm alg;
     ASSERT_EQ(Success, DigestAlgorithmIdentifier(input, alg));
     ASSERT_EQ(param.algorithm, alg);
     ASSERT_EQ(Success, End(input));
   }
 }
 
 INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier,
@@ -282,35 +268,35 @@ INSTANTIATE_TEST_CASE_P(pkixder_DigestAl
 TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_MD5)
 {
   // The OID identifies MD5 (1.2.840.113549.2.5). It is invalid because we
   // don't accept MD5 as a hash algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0a, 0x06, 0x08,
     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05
   };
+  InputBuffer buf(DER);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   DigestAlgorithm alg;
   ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
             DigestAlgorithmIdentifier(input, alg));
 }
 
 TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_Digest_ECDSA_WITH_SHA256)
 {
   // The OID identifies ecdsa-with-SHA256 (1.2.840.10045.4.3.2). It is invalid
   // because ECDSA-with-SHA256 is not a hash algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0a, 0x06, 0x08,
     0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, //
   };
+  InputBuffer buf(DER);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   DigestAlgorithm alg;
   ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
             DigestAlgorithmIdentifier(input, alg));
 }
 
 static const AlgorithmIdentifierTestInfo<SignatureAlgorithm>
   SIGNATURE_ALGORITHM_TEST_INFO[] =
 {
@@ -377,33 +363,35 @@ class pkixder_SignatureAlgorithmIdentifi
 {
 };
 
 TEST_P(pkixder_SignatureAlgorithmIdentifier, Valid)
 {
   const AlgorithmIdentifierTestInfo<SignatureAlgorithm>& param(GetParam());
 
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(param.der, param.derLength));
+    InputBuffer buf;
+    ASSERT_EQ(Success, buf.Init(param.der, param.derLength));
+    Input input(buf);
     SignatureAlgorithm alg;
     ASSERT_EQ(Success, SignatureAlgorithmIdentifier(input, alg));
     ASSERT_EQ(param.algorithm, alg);
     ASSERT_EQ(Success, End(input));
   }
 
   {
     uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
     memcpy(derWithNullParam, param.der, param.derLength);
     derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes
     derWithNullParam[param.derLength] = 0x05; // NULL tag
     derWithNullParam[param.derLength + 1] = 0x00; // length zero
 
-    Input input;
-    ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
+    InputBuffer buf;
+    ASSERT_EQ(Success, buf.Init(derWithNullParam, param.derLength + 2));
+    Input input(buf);
     SignatureAlgorithm alg;
     ASSERT_EQ(Success, SignatureAlgorithmIdentifier(input, alg));
     ASSERT_EQ(param.algorithm, alg);
     ASSERT_EQ(Success, End(input));
   }
 }
 
 INSTANTIATE_TEST_CASE_P(pkixder_SignatureAlgorithmIdentifier,
@@ -413,33 +401,33 @@ INSTANTIATE_TEST_CASE_P(pkixder_Signatur
 TEST_F(pkixder_SignatureAlgorithmIdentifier, Invalid_RSA_With_MD5)
 {
   // The OID identifies RSA-with-MD5 (1.2.840.113549.1.1.4). It is invalid
   // because no MD5-based signatures algorithms are supported by the parser.
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
   };
+  InputBuffer buf(DER);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   SignatureAlgorithm alg;
   ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
             SignatureAlgorithmIdentifier(input, alg));
 }
 
 TEST_F(pkixder_SignatureAlgorithmIdentifier, Invalid_SignatureAlgorithm_SHA256)
 {
   // The OID identifies id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid
   // because SHA-256 is not a signature algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
   };
+  InputBuffer buf(DER);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
   SignatureAlgorithm alg;
   ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
             SignatureAlgorithmIdentifier(input, alg));
 }
 
 } // unnamed namespace
--- a/lib/mozpkix/test/gtest/pkixder_universal_types_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -42,100 +42,87 @@ class pkixder_universal_types_tests : pu
 
 TEST_F(pkixder_universal_types_tests, BooleanTrue01)
 {
   const uint8_t DER_BOOLEAN_TRUE_01[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x01                        // invalid
   };
-
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_BOOLEAN_TRUE_01, sizeof DER_BOOLEAN_TRUE_01));
-
+  InputBuffer buf(DER_BOOLEAN_TRUE_01);
+  Input input(buf);
   bool value = false;
   ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanTrue42)
 {
   const uint8_t DER_BOOLEAN_TRUE_42[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x42                        // invalid
   };
-
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_BOOLEAN_TRUE_42, sizeof DER_BOOLEAN_TRUE_42));
-
+  InputBuffer buf(DER_BOOLEAN_TRUE_42);
+  Input input(buf);
   bool value = false;
   ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 static const uint8_t DER_BOOLEAN_TRUE[] = {
   0x01,                       // BOOLEAN
   0x01,                       // length
   0xff                        // true
 };
 
 TEST_F(pkixder_universal_types_tests, BooleanTrueFF)
 {
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
-
+  InputBuffer buf(DER_BOOLEAN_TRUE);
+  Input input(buf);
   bool value = false;
   ASSERT_EQ(Success, Boolean(input, value));
   ASSERT_TRUE(value);
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanFalse)
 {
   const uint8_t DER_BOOLEAN_FALSE[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x00                        // false
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_FALSE, sizeof DER_BOOLEAN_FALSE));
+  InputBuffer buf(DER_BOOLEAN_FALSE);
+  Input input(buf);
 
   bool value = true;
   ASSERT_EQ(Success, Boolean(input, value));
   ASSERT_FALSE(value);
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanInvalidLength)
 {
   const uint8_t DER_BOOLEAN_INVALID_LENGTH[] = {
     0x01,                       // BOOLEAN
     0x02,                       // length
     0x42, 0x42                  // invalid
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_LENGTH,
-                                sizeof DER_BOOLEAN_INVALID_LENGTH));
+  InputBuffer buf(DER_BOOLEAN_INVALID_LENGTH);
+  Input input(buf);
 
   bool value = true;
   ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, BooleanInvalidZeroLength)
 {
   const uint8_t DER_BOOLEAN_INVALID_ZERO_LENGTH[] = {
     0x01,                       // BOOLEAN
     0x00                        // length
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_ZERO_LENGTH,
-                                sizeof DER_BOOLEAN_INVALID_ZERO_LENGTH));
+  InputBuffer buf(DER_BOOLEAN_INVALID_ZERO_LENGTH);
+  Input input(buf);
 
   bool value = true;
   ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(input, value));
 }
 
 // OptionalBoolean implements decoding of OPTIONAL BOOLEAN DEFAULT FALSE.
 // If the field is present, it must be a valid encoding of a BOOLEAN with
 // value TRUE. If the field is not present, it defaults to FALSE. For
@@ -144,157 +131,146 @@ TEST_F(pkixder_universal_types_tests, Bo
 // valid DER encoding).
 TEST_F(pkixder_universal_types_tests, OptionalBooleanValidEncodings)
 {
   const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_TRUE[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0xff                        // true
   };
-
-  Input input1;
-  ASSERT_EQ(Success, input1.Init(DER_OPTIONAL_BOOLEAN_PRESENT_TRUE,
-                                 sizeof DER_OPTIONAL_BOOLEAN_PRESENT_TRUE));
+  InputBuffer buf1(DER_OPTIONAL_BOOLEAN_PRESENT_TRUE);
+  Input input1(buf1);
   bool value = false;
   ASSERT_EQ(Success, OptionalBoolean(input1, false, value)) <<
     "Should accept the only valid encoding of a present OPTIONAL BOOLEAN";
   ASSERT_TRUE(value);
   ASSERT_TRUE(input1.AtEnd());
 
   // The OPTIONAL BOOLEAN is omitted in this data.
   const uint8_t DER_INTEGER_05[] = {
     0x02,                       // INTEGER
     0x01,                       // length
     0x05
   };
-
-  Input input2;
-  ASSERT_EQ(Success, input2.Init(DER_INTEGER_05, sizeof DER_INTEGER_05));
+  InputBuffer buf2(DER_INTEGER_05);
+  Input input2(buf2);
   value = true;
   ASSERT_EQ(Success, OptionalBoolean(input2, false, value)) <<
     "Should accept a valid encoding of an omitted OPTIONAL BOOLEAN";
   ASSERT_FALSE(value);
   ASSERT_FALSE(input2.AtEnd());
 
-  Input input3;
-  ASSERT_EQ(Success, input3.Init(reinterpret_cast<const uint8_t*>(""), 0));
+  InputBuffer buf3;
+  ASSERT_EQ(Success, buf3.Init(reinterpret_cast<const uint8_t*>(""), 0));
+  Input input3(buf3);
   value = true;
   ASSERT_EQ(Success, OptionalBoolean(input3, false, value)) <<
     "Should accept another valid encoding of an omitted OPTIONAL BOOLEAN";
   ASSERT_FALSE(value);
   ASSERT_TRUE(input3.AtEnd());
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalBooleanInvalidEncodings)
 {
   const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_FALSE[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x00                        // false
   };
 
-  Input input1;
-  ASSERT_EQ(Success, input1.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE,
-                                 sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE));
+  InputBuffer buf1(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE);
+  Input input1(buf1);
   bool value;
   // If the second parameter to OptionalBoolean is false, invalid encodings
   // that include the field even when it is the DEFAULT FALSE are rejected.
   bool allowInvalidEncodings = false;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             OptionalBoolean(input1, allowInvalidEncodings, value)) <<
     "Should reject an invalid encoding of present OPTIONAL BOOLEAN";
 
-  Input input2;
-  ASSERT_EQ(Success, input2.Init(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE,
-                                 sizeof DER_OPTIONAL_BOOLEAN_PRESENT_FALSE));
+  InputBuffer buf2(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE);
+  Input input2(buf2);
   value = true;
   // If the second parameter to OptionalBoolean is true, invalid encodings
   // that include the field even when it is the DEFAULT FALSE are accepted.
   allowInvalidEncodings = true;
   ASSERT_EQ(Success, OptionalBoolean(input2, allowInvalidEncodings, value)) <<
     "Should now accept an invalid encoding of present OPTIONAL BOOLEAN";
   ASSERT_FALSE(value);
   ASSERT_TRUE(input2.AtEnd());
 
   const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_42[] = {
     0x01,                       // BOOLEAN
     0x01,                       // length
     0x42                        // (invalid value for a BOOLEAN)
   };
-
-  Input input3;
-  ASSERT_EQ(Success, input3.Init(DER_OPTIONAL_BOOLEAN_PRESENT_42,
-                                 sizeof DER_OPTIONAL_BOOLEAN_PRESENT_42));
+  InputBuffer buf3(DER_OPTIONAL_BOOLEAN_PRESENT_42);
+  Input input3(buf3);
   // Even with the second parameter to OptionalBoolean as true, encodings
   // of BOOLEAN that are invalid altogether are rejected.
   ASSERT_EQ(Result::ERROR_BAD_DER,
             OptionalBoolean(input3, allowInvalidEncodings, value)) <<
     "Should reject another invalid encoding of present OPTIONAL BOOLEAN";
 }
 
 TEST_F(pkixder_universal_types_tests, Enumerated)
 {
   const uint8_t DER_ENUMERATED[] = {
     0x0a,                       // ENUMERATED
     0x01,                       // length
     0x42                        // value
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED));
+  InputBuffer buf(DER_ENUMERATED);
+  Input input(buf);
 
   uint8_t value = 0;
   ASSERT_EQ(Success, Enumerated(input, value));
   ASSERT_EQ(0x42, value);
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedNotShortestPossibleDER)
 {
   const uint8_t DER_ENUMERATED[] = {
     0x0a,                       // ENUMERATED
     0x02,                       // length
     0x00, 0x01                  // value
   };
+  InputBuffer buf(DER_ENUMERATED);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED));
   uint8_t value = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedOutOfAcceptedRange)
 {
   // Although this is a valid ENUMERATED value according to ASN.1, we
   // intentionally don't support these large values because there are no
   // ENUMERATED values in X.509 certs or OCSP this large, and we're trying to
   // keep the parser simple and fast.
   const uint8_t DER_ENUMERATED_INVALID_LENGTH[] = {
     0x0a,                       // ENUMERATED
     0x02,                       // length
     0x12, 0x34                  // value
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_LENGTH,
-                                sizeof DER_ENUMERATED_INVALID_LENGTH));
+  InputBuffer buf(DER_ENUMERATED_INVALID_LENGTH);
+  Input input(buf);
 
   uint8_t value = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
 {
   const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = {
     0x0a,                       // ENUMERATED
     0x00                        // length
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_ZERO_LENGTH,
-                                sizeof DER_ENUMERATED_INVALID_ZERO_LENGTH));
+  InputBuffer buf(DER_ENUMERATED_INVALID_ZERO_LENGTH);
+  Input input(buf);
 
   uint8_t value = 0;
   ASSERT_EQ(Result::ERROR_BAD_DER, Enumerated(input, value));
 }
 
 ////////////////////////////////////////
 // GeneralizedTime and TimeChoice
 //
@@ -331,43 +307,39 @@ TimeChoiceForEquivalentUTCTime(const uin
   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];
   }
 
-  Input input;
-  Result rv = input.Init(utcTimeDER, sizeof utcTimeDER);
-  EXPECT_EQ(Success, rv);
-  if (rv != Success) {
-    return rv;
-  }
+  InputBuffer buf(utcTimeDER);
+  Input input(buf);
   return TimeChoice(input, value);
 }
 
 template <uint16_t LENGTH>
 void
 ExpectGoodTime(PRTime expectedValue,
                const uint8_t (&generalizedTimeDER)[LENGTH])
 {
   // GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
+    InputBuffer buf(generalizedTimeDER);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Success, GeneralizedTime(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
+    InputBuffer buf(generalizedTimeDER);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Success, TimeChoice(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: UTCTime
   {
     PRTime value = 0;
@@ -378,26 +350,26 @@ ExpectGoodTime(PRTime expectedValue,
 }
 
 template <uint16_t LENGTH>
 void
 ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
 {
   // GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
+    InputBuffer buf(generalizedTimeDER);
+    Input input(buf);
     PRTime value;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
+    InputBuffer buf(generalizedTimeDER);
+    Input input(buf);
     PRTime value;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 
   // TimeChoice: UTCTime
   {
     PRTime value;
     ASSERT_EQ(Result::ERROR_INVALID_TIME,
@@ -432,37 +404,32 @@ TEST_F(pkixder_universal_types_tests, Ti
   const uint8_t DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH[] = {
     0x18,                           // GeneralizedTime
     0x00                            // Length = 0
   };
 
   PRTime value;
 
   // GeneralizedTime
-  Input gt;
-  ASSERT_EQ(Success,
-            gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
-                    sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
+  InputBuffer gtBuf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
+  Input gt(gtBuf);
   ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(gt, value));
 
   // TimeChoice: GeneralizedTime
-  Input tc_gt;
-  ASSERT_EQ(Success,
-            tc_gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
-                       sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
+  InputBuffer tc_gt_buf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
+  Input tc_gt(tc_gt_buf);
   ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_gt, value));
 
   // TimeChoice: UTCTime
   const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = {
     0x17, // UTCTime
     0x00  // Length = 0
   };
-  Input tc_utc;
-  ASSERT_EQ(Success, tc_utc.Init(DER_UTCTIME_INVALID_ZERO_LENGTH,
-                                 sizeof DER_UTCTIME_INVALID_ZERO_LENGTH));
+  InputBuffer tc_utc_buf(DER_UTCTIME_INVALID_ZERO_LENGTH);
+  Input tc_utc(tc_utc_buf);
   ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(tc_utc, value));
 }
 
 // A non zulu time should fail
 TEST_F(pkixder_universal_types_tests, TimeInvalidLocal)
 {
   const uint8_t DER_GENERALIZED_TIME_INVALID_LOCAL[] = {
     0x18,                           // Generalized Time
@@ -533,35 +500,34 @@ TEST_F(pkixder_universal_types_tests, Ge
     PRTime expectedValue = YMDHMS(i, 12, 31, 23, 59, 59);
 
     // We have to test GeneralizedTime separately from UTCTime instead of using
     // ExpectGooDtime because the range of UTCTime is less than the range of
     // GeneralizedTime.
 
     // GeneralizedTime
     {
-      Input input;
-      ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+      InputBuffer buf(DER);
+      Input input(buf);
       PRTime value = 0;
       ASSERT_EQ(Success, GeneralizedTime(input, value));
       EXPECT_EQ(expectedValue, value);
     }
 
     // TimeChoice: GeneralizedTime
     {
-      Input input;
-      ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+      InputBuffer buf(DER);
+      Input input(buf);
       PRTime value = 0;
       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;
       PRTime value = 0;
       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
@@ -689,27 +655,27 @@ TEST_F(pkixder_universal_types_tests, Ti
   };
 
   // We don't use ExpectGoodTime here because UTCTime can't represent 2400.
 
   PRTime expectedValue = YMDHMS(2400, 2, 29, 16, 45, 40);
 
   // GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+    InputBuffer buf(DER);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Success, GeneralizedTime(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+    InputBuffer buf(DER);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Success, TimeChoice(input, value));
     EXPECT_EQ(expectedValue, value);
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2014)
 {
@@ -730,26 +696,26 @@ TEST_F(pkixder_universal_types_tests, Ti
     '2', '1', '0', '0', '0', '2', '2', '9', // 2100-02-29
     '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
   };
 
   // We don't use ExpectBadTime here because UTCTime can't represent 2100.
 
   // GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+    InputBuffer buf(DER);
+    Input input(buf);
     PRTime value;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
+    InputBuffer buf(DER);
+    Input input(buf);
     PRTime value;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeHoursValidRange)
 {
   for (uint8_t i = 0; i <= 23; ++i) {
@@ -868,30 +834,26 @@ TEST_F(pkixder_universal_types_tests, Ti
   };
 
   // We can't use ExpectBadTime here, because ExpectBadTime requires
   // consistent results for GeneralizedTime and UTCTime, but the results
   // for this input are different.
 
   // GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success,
-              input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR,
-                         sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR));
+    InputBuffer buf(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(input, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
-    Input input;
-    ASSERT_EQ(Success,
-              input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR,
-                         sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR));
+    InputBuffer buf(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
+    Input input(buf);
     PRTime value = 0;
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(input, value));
   }
 
   // This test is not applicable to TimeChoice: UTCTime
 }
 
 TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar)
@@ -941,19 +903,18 @@ TEST_F(pkixder_universal_types_tests, Ti
 TEST_F(pkixder_universal_types_tests, Integer_0_127)
 {
   for (uint8_t i = 0; i <= 127; ++i) {
     const uint8_t DER[] = {
       0x02, // INTEGER
       0x01, // length
       i,    // value
     };
-
-    Input input;
-    ASSERT_EQ(Success, input.Init(DER, sizeof DER));
+    InputBuffer buf(DER);
+    Input input(buf);
 
     uint8_t value = i + 1; // initialize with a value that is NOT i.
     ASSERT_EQ(Success, Integer(input, value));
     ASSERT_EQ(i, value);
   }
 }
 
 TEST_F(pkixder_universal_types_tests, Integer_Negative1)
@@ -961,242 +922,231 @@ TEST_F(pkixder_universal_types_tests, In
   // This is a valid integer value but our integer parser cannot parse
   // negative values.
 
   static const uint8_t DER[] = {
     0x02, // INTEGER
     0x01, // length
     0xff, // -1 (two's complement)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof DER));
+  InputBuffer buf(DER);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer_Negative128)
 {
   // This is a valid integer value but our integer parser cannot parse
   // negative values.
 
   static const uint8_t DER[] = {
     0x02, // INTEGER
     0x01, // length
     0x80, // -128 (two's complement)
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof DER));
+  InputBuffer buf(DER);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer_128)
 {
   // This is a valid integer value but our integer parser cannot parse
   // values that require more than one byte to encode.
 
   static const uint8_t DER[] = {
     0x02, // INTEGER
     0x02, // length
     0x00, 0x80 // 128
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof DER));
+  InputBuffer buf(DER);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, Integer11223344)
 {
   // This is a valid integer value but our integer parser cannot parse
   // values that require more than one byte to be encoded.
 
   static const uint8_t DER[] = {
     0x02,                       // INTEGER
     0x04,                       // length
     0x11, 0x22, 0x33, 0x44      // 0x11223344
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof DER));
+  InputBuffer buf(DER);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerTruncatedOneByte)
 {
   const uint8_t DER_INTEGER_TRUNCATED[] = {
     0x02,                       // INTEGER
     0x01,                       // length
     // MISSING DATA HERE
   };
-
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
+  InputBuffer buf(DER_INTEGER_TRUNCATED);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerTruncatedLarge)
 {
   const uint8_t DER_INTEGER_TRUNCATED[] = {
     0x02,                       // INTEGER
     0x04,                       // length
     0x11, 0x22                  // 0x1122
     // MISSING DATA HERE
   };
-
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_INTEGER_TRUNCATED, sizeof DER_INTEGER_TRUNCATED));
+  InputBuffer buf(DER_INTEGER_TRUNCATED);
+  Input input(buf);
 
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerZeroLength)
 {
   const uint8_t DER_INTEGER_ZERO_LENGTH[] = {
     0x02,                       // INTEGER
     0x00                        // length
   };
+  InputBuffer buf(DER_INTEGER_ZERO_LENGTH);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INTEGER_ZERO_LENGTH,
-                                sizeof DER_INTEGER_ZERO_LENGTH));
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerOverlyLong1)
 {
   const uint8_t DER_INTEGER_OVERLY_LONG1[] = {
     0x02,                       // INTEGER
     0x02,                       // length
     0x00, 0x01                  //
   };
+  InputBuffer buf(DER_INTEGER_OVERLY_LONG1);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG1,
-                                sizeof DER_INTEGER_OVERLY_LONG1));
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2)
 {
   const uint8_t DER_INTEGER_OVERLY_LONG2[] = {
     0x02,                       // INTEGER
     0x02,                       // length
     0xff, 0x80                  //
   };
+  InputBuffer buf(DER_INTEGER_OVERLY_LONG2);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG2,
-                                sizeof DER_INTEGER_OVERLY_LONG2));
   uint8_t value;
   ASSERT_EQ(Result::ERROR_BAD_DER, Integer(input, value));
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefault)
 {
   // The input is a BOOLEAN and not INTEGER for the input so we'll not parse
   // anything and instead use the default value.
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
+  InputBuffer buf(DER_BOOLEAN_TRUE);
+  Input input(buf);
+
   long value = 1;
   ASSERT_EQ(Success, OptionalInteger(input, -1, value));
   ASSERT_EQ(-1, value);
   bool boolValue;
   ASSERT_EQ(Success, Boolean(input, boolValue));
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerUnsupportedDefault)
 {
   // The same as the previous test, except with an unsupported default value
   // passed in.
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_TRUE, sizeof DER_BOOLEAN_TRUE));
+  InputBuffer buf(DER_BOOLEAN_TRUE);
+  Input input(buf);
+
   long value;
   ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, OptionalInteger(input, 0, value));
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefaultAtEnd)
 {
   static const uint8_t dummy = 1;
+  InputBuffer buf;
+  ASSERT_EQ(Success, buf.Init(&dummy, 0));
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(&dummy, 0));
   long value = 1;
   ASSERT_EQ(Success, OptionalInteger(input, -1, value));
   ASSERT_EQ(-1, value);
 }
 
 TEST_F(pkixder_universal_types_tests, OptionalIntegerNonDefaultValue)
 {
   static const uint8_t DER[] = {
     0x02, // INTEGER
     0x01, // length
     0x00
   };
+  InputBuffer buf(DER);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER, sizeof DER));
   long value = 2;
   ASSERT_EQ(Success, OptionalInteger(input, -1, value));
   ASSERT_EQ(0, value);
   ASSERT_TRUE(input.AtEnd());
 }
 
 TEST_F(pkixder_universal_types_tests, Null)
 {
   const uint8_t DER_NUL[] = {
     0x05,
     0x00
   };
+  InputBuffer buf(DER_NUL);
+  Input input(buf);
 
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_NUL, sizeof DER_NUL));
   ASSERT_EQ(Success, Null(input));
 }
 
 TEST_F(pkixder_universal_types_tests, NullWithBadLength)
 {
   const uint8_t DER_NULL_BAD_LENGTH[] = {
     0x05,
     0x01,
     0x00
   };
-
-  Input input;
-  ASSERT_EQ(Success,
-            input.Init(DER_NULL_BAD_LENGTH, sizeof DER_NULL_BAD_LENGTH));
+  InputBuffer buf(DER_NULL_BAD_LENGTH);
+  Input input(buf);
 
   ASSERT_EQ(Result::ERROR_BAD_DER, Null(input));
 }
 
 TEST_F(pkixder_universal_types_tests, OID)
 {
   const uint8_t DER_VALID_OID[] = {
     0x06,
     0x09,
     0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
   };
-
-  Input input;
-  ASSERT_EQ(Success, input.Init(DER_VALID_OID, sizeof DER_VALID_OID));
+  InputBuffer buf(DER_VALID_OID);
+  Input input(buf);
 
   const uint8_t expectedOID[] = {
     0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
   };
 
   ASSERT_EQ(Success, OID(input, expectedOID));
 }
 
--- a/lib/mozpkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
+++ b/lib/mozpkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
@@ -31,55 +31,55 @@
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 class CreateEncodedOCSPRequestTrustDomain : public TrustDomain
 {
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
-                              const SECItem&, /*out*/ TrustLevel&)
+                              InputBuffer, /*out*/ TrustLevel&)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime)
+  virtual Result FindIssuer(InputBuffer, IssuerChecker&, PRTime)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
-                                 const SECItem*, const SECItem*)
+                                 const InputBuffer*, const InputBuffer*)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature&,
-                                  const SECItem&)
+                                  InputBuffer)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+  virtual Result DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen)
   {
     return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
   }
 
-  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 };
 
 class pkixocsp_CreateEncodedOCSPRequest : public NSSTest
 {
 protected:
@@ -108,66 +108,86 @@ protected:
                                     1 + 1 + LONGEST_REQUIRED_LEN);
     memset(longestRequiredSerialNumber->data, 0,
            longestRequiredSerialNumber->len);
     longestRequiredSerialNumber->data[0] = der::INTEGER;
     longestRequiredSerialNumber->data[1] = LONGEST_REQUIRED_LEN;
     longestRequiredSerialNumber->data[2] = 0x01; // value is 0x010000...00
   }
 
-  // The resultant issuerDER is owned by the arena.
+  // The resultant issuerDER and issuerSPKI are owned by the arena.
   SECStatus MakeIssuerCertIDComponents(const char* issuerASCII,
-                                       /*out*/ const SECItem*& issuerDER,
-                                       /*out*/ ScopedSECItem& issuerSPKI)
+                                       /*out*/ InputBuffer& issuerDER,
+                                       /*out*/ InputBuffer& issuerSPKI)
   {
-    issuerDER = ASCIIToDERName(arena.get(), issuerASCII);
-    if (!issuerDER) {
+    const SECItem* issuerDERSECItem = ASCIIToDERName(arena.get(), issuerASCII);
+    if (!issuerDERSECItem) {
       return SECFailure;
     }
+    if (issuerDER.Init(issuerDERSECItem->data, issuerDERSECItem->len)
+          != Success) {
+      return SECFailure;
+    }
+
     ScopedSECKEYPublicKey issuerPublicKey;
     ScopedSECKEYPrivateKey issuerPrivateKey;
     if (GenerateKeyPair(issuerPublicKey, issuerPrivateKey) != SECSuccess) {
       return SECFailure;
     }
-    issuerSPKI = SECKEY_EncodeDERSubjectPublicKeyInfo(issuerPublicKey.get());
-    if (!issuerSPKI) {
+    ScopedSECItem issuerSPKIOriginal(
+      SECKEY_EncodeDERSubjectPublicKeyInfo(issuerPublicKey.get()));
+    if (!issuerSPKIOriginal) {
+      return SECFailure;
+    }
+    SECItem issuerSPKICopy;
+    if (SECITEM_CopyItem(arena.get(), &issuerSPKICopy,
+                         issuerSPKIOriginal.get()) != SECSuccess) {
+      return SECFailure;
+    }
+    if (issuerSPKI.Init(issuerSPKICopy.data, issuerSPKICopy.len) != Success) {
       return SECFailure;
     }
 
     return SECSuccess;
   }
 
   CreateEncodedOCSPRequestTrustDomain trustDomain;
 };
 
 // Test that the large length of the child serial number causes
 // CreateEncodedOCSPRequest to fail.
 TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest)
 {
-  const SECItem* issuerDER;
-  ScopedSECItem issuerSPKI;
+  InputBuffer issuerDER;
+  InputBuffer issuerSPKI;
   ASSERT_EQ(SECSuccess,
             MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
+  InputBuffer serialNumber;
+  ASSERT_EQ(Success, serialNumber.Init(unsupportedLongSerialNumber->data,
+                                       unsupportedLongSerialNumber->len));
   uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
   size_t ocspRequestLength;
   ASSERT_EQ(Result::ERROR_BAD_DER,
             CreateEncodedOCSPRequest(trustDomain,
-                                     CertID(*issuerDER, *issuerSPKI,
-                                            *unsupportedLongSerialNumber),
+                                     CertID(issuerDER, issuerSPKI,
+                                            serialNumber),
                                      ocspRequest, ocspRequestLength));
 }
 
 // Test that CreateEncodedOCSPRequest handles the longest serial number that
 // it's required to support (i.e. 20 octets).
 TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest)
 {
-  const SECItem* issuerDER;
-  ScopedSECItem issuerSPKI;
+  InputBuffer issuerDER;
+  InputBuffer issuerSPKI;
   ASSERT_EQ(SECSuccess,
             MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
+  InputBuffer serialNumber;
+  ASSERT_EQ(Success, serialNumber.Init(longestRequiredSerialNumber->data,
+                                       longestRequiredSerialNumber->len));
   uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
   size_t ocspRequestLength;
   ASSERT_EQ(Success,
             CreateEncodedOCSPRequest(trustDomain,
-                                     CertID(*issuerDER, *issuerSPKI,
-                                            *longestRequiredSerialNumber),
+                                     CertID(issuerDER, issuerSPKI,
+                                            serialNumber),
                                      ocspRequest, ocspRequestLength));
 }
--- a/lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/lib/mozpkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -38,62 +38,61 @@ const uint16_t END_ENTITY_MAX_LIFETIME_I
 
 class OCSPTestTrustDomain : public TrustDomain
 {
 public:
   OCSPTestTrustDomain()
   {
   }
 
-  virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
-                              const SECItem& candidateCert,
-                              /*out*/ TrustLevel& trustLevel)
+  Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
+                      InputBuffer, /*out*/ TrustLevel& trustLevel)
   {
     EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
     trustLevel = TrustLevel::InheritsTrust;
     return Success;
   }
 
-  virtual Result FindIssuer(const SECItem&, IssuerChecker&, PRTime)
+  Result FindIssuer(InputBuffer, IssuerChecker&, PRTime)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&,
-                                 PRTime time, /*optional*/ const SECItem*,
-                                 /*optional*/ const SECItem*)
+                                 PRTime time, /*optional*/ const InputBuffer*,
+                                 /*optional*/ const InputBuffer*)
   {
     // TODO: I guess mozilla::pkix should support revocation of designated
     // OCSP responder eventually, but we don't now, so this function should
     // never get called.
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
-                                  const SECItem& subjectPublicKeyInfo)
+                                  InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
                                              nullptr);
   }
 
-  virtual Result DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf,
+  virtual Result DigestBuf(InputBuffer item, /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen)
   {
     return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
   }
 
-  virtual Result CheckPublicKey(const SECItem& subjectPublicKeyInfo)
+  virtual Result CheckPublicKey(InputBuffer subjectPublicKeyInfo)
   {
     return ::mozilla::pkix::CheckPublicKey(subjectPublicKeyInfo);
   }
 
 private:
   OCSPTestTrustDomain(const OCSPTestTrustDomain&) /*delete*/;
   void operator=(const OCSPTestTrustDomain&) /*delete*/;
 };
@@ -127,28 +126,37 @@ public:
       PR_Abort();
     }
   }
 
   void SetUp()
   {
     NSSTest::SetUp();
 
-    const SECItem* rootNameDER = ASCIIToDERName(arena.get(), rootName);
-    if (!rootNameDER) {
+    InputBuffer rootNameDER;
+    // The result of ASCIIToDERName is owned by the arena
+    if (InitInputBufferFromSECItem(ASCIIToDERName(arena.get(), rootName),
+                                   rootNameDER) != Success) {
       PR_Abort();
     }
-    const SECItem*
-      endEntitySerialNumber(CreateEncodedSerialNumber(arena.get(),
-                                                      ++rootIssuedCount));
-    if (!endEntitySerialNumber) {
+
+    InputBuffer serialNumberDER;
+    // The result of CreateEncodedSerialNumber is owned by the arena
+    if (InitInputBufferFromSECItem(
+          CreateEncodedSerialNumber(arena.get(), ++rootIssuedCount),
+          serialNumberDER) != Success) {
       PR_Abort();
     }
-    endEntityCertID = new (std::nothrow) CertID(*rootNameDER, *rootSPKI,
-                                                *endEntitySerialNumber);
+
+    InputBuffer rootSPKIDER;
+    if (InitInputBufferFromSECItem(rootSPKI.get(), rootSPKIDER) != Success) {
+      PR_Abort();
+    }
+    endEntityCertID = new (std::nothrow) CertID(rootNameDER, rootSPKIDER,
+                                                serialNumberDER);
     if (!endEntityCertID) {
       PR_Abort();
     }
   }
 
   static ScopedSECKEYPrivateKey rootPrivateKey;
   static ScopedSECItem rootSPKI;
   static long rootIssuedCount;
@@ -185,38 +193,43 @@ static const WithoutResponseBytes WITHOU
   },
 };
 
 class pkixocsp_VerifyEncodedResponse_WithoutResponseBytes
   : public pkixocsp_VerifyEncodedResponse
   , public ::testing::WithParamInterface<WithoutResponseBytes>
 {
 protected:
-  SECItem* CreateEncodedOCSPErrorResponse(uint8_t status)
+  // The result is owned by the arena
+  InputBuffer CreateEncodedOCSPErrorResponse(uint8_t status)
   {
-    static const SECItem EMPTY = { siBuffer, nullptr, 0 };
+    static const InputBuffer EMPTY;
     OCSPResponseContext context(arena.get(),
                                 CertID(EMPTY, EMPTY, EMPTY),
                                 oneDayBeforeNow);
     context.responseStatus = status;
     context.skipResponseBytes = true;
-    return CreateEncodedOCSPResponse(context);
+    SECItem* response = CreateEncodedOCSPResponse(context);
+    EXPECT_TRUE(response);
+    InputBuffer result;
+    EXPECT_EQ(Success, result.Init(response->data, response->len));
+    return result;
   }
 };
 
 TEST_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, CorrectErrorCode)
 {
-  SECItem* response(CreateEncodedOCSPErrorResponse(
-                      GetParam().responseStatus));
-  ASSERT_TRUE(response);
+  InputBuffer
+    response(CreateEncodedOCSPErrorResponse(GetParam().responseStatus));
+
   bool expired;
   ASSERT_EQ(GetParam().expectedError,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
 }
 
 INSTANTIATE_TEST_CASE_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes,
                         pkixocsp_VerifyEncodedResponse_WithoutResponseBytes,
                         testing::ValuesIn(WITHOUT_RESPONSEBYTES));
 
 ///////////////////////////////////////////////////////////////////////////////
 // "successful" responses
@@ -237,122 +250,118 @@ public:
     pkixocsp_VerifyEncodedResponse::SetUp();
   }
 
   static void SetUpTestCase()
   {
     pkixocsp_VerifyEncodedResponse::SetUpTestCase();
   }
 
-  SECItem* CreateEncodedOCSPSuccessfulResponse(
+  // The result is owned by the arena
+  InputBuffer CreateEncodedOCSPSuccessfulResponse(
                     OCSPResponseContext::CertStatus certStatus,
                     const CertID& certID,
                     /*optional*/ const char* signerName,
                     const ScopedSECKEYPrivateKey& signerPrivateKey,
                     PRTime producedAt, PRTime thisUpdate,
                     /*optional*/ const PRTime* nextUpdate,
                     /*optional*/ SECItem const* const* certs = nullptr)
   {
     OCSPResponseContext context(arena.get(), certID, producedAt);
     if (signerName) {
       context.signerNameDER = ASCIIToDERName(arena.get(), signerName);
-      if (!context.signerNameDER) {
-        return nullptr;
-      }
+      EXPECT_TRUE(context.signerNameDER);
     }
     context.signerPrivateKey = SECKEY_CopyPrivateKey(signerPrivateKey.get());
-    if (!context.signerPrivateKey) {
-      return nullptr;
-    }
+    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;
 
-    return CreateEncodedOCSPResponse(context);
+    SECItem* response = CreateEncodedOCSPResponse(context);
+    EXPECT_TRUE(response);
+    InputBuffer result;
+    EXPECT_EQ(Success, result.Init(response->data, response->len));
+    return result;
   }
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey)
 {
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, byKey,
-                      rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      &oneDayAfterNow));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, byKey,
+                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
+                         &oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
                                       now, END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName)
 {
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, rootName,
-                      rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      &oneDayAfterNow));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, rootName,
+                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
+                         &oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate)
 {
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, byKey,
-                      rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      nullptr));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, byKey,
+                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
+                         nullptr));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked)
 {
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::revoked, *endEntityCertID, byKey,
-                      rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      &oneDayAfterNow));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::revoked, *endEntityCertID, byKey,
+                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
+                         &oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown)
 {
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::unknown, *endEntityCertID, byKey,
-                      rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      &oneDayAfterNow));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::unknown, *endEntityCertID, byKey,
+                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
+                         &oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // indirect responses (signed by a delegated OCSP responder cert)
 
 class pkixocsp_VerifyEncodedResponse_DelegatedResponder
   : public pkixocsp_VerifyEncodedResponse_successful
@@ -369,22 +378,22 @@ protected:
   // ResponderID construction.
   //
   // If signerEKU is omitted, then the certificate will have the
   // id-kp-OCSPSigning EKU. If signerEKU is SEC_OID_UNKNOWN then it will not
   // have any EKU extension. Otherwise, the certificate will have the given
   // EKU.
   //
   // signerDEROut is owned by the arena
-  SECItem* CreateEncodedIndirectOCSPSuccessfulResponse(
+  InputBuffer CreateEncodedIndirectOCSPSuccessfulResponse(
               const char* certSubjectName,
               OCSPResponseContext::CertStatus certStatus,
               const char* signerName,
               SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER,
-              /*optional, out*/ const SECItem** signerDEROut = nullptr)
+              /*optional, out*/ InputBuffer* signerDEROut = nullptr)
   {
     PR_ASSERT(certSubjectName);
 
     const SECItem* extensions[] = {
       signerEKU != SEC_OID_UNKNOWN
         ? CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                                     ExtensionCriticality::NotCritical)
         : nullptr,
@@ -392,29 +401,25 @@ protected:
     };
     ScopedSECKEYPrivateKey signerPrivateKey;
     SECItem* signerDER(CreateEncodedCertificate(
                           arena.get(), ++rootIssuedCount, rootName,
                           oneDayBeforeNow, oneDayAfterNow, certSubjectName,
                           signerEKU != SEC_OID_UNKNOWN ? extensions : nullptr,
                           rootPrivateKey.get(), signerPrivateKey));
     EXPECT_TRUE(signerDER);
-    if (!signerDER) {
-      return nullptr;
+    if (signerDEROut) {
+      EXPECT_EQ(Success,
+                signerDEROut->Init(signerDER->data, signerDER->len));
     }
 
     const SECItem* signerNameDER = nullptr;
     if (signerName) {
       signerNameDER = ASCIIToDERName(arena.get(), signerName);
-      if (!signerNameDER) {
-        return nullptr;
-      }
-    }
-    if (signerDEROut) {
-      *signerDEROut = signerDER;
+      EXPECT_TRUE(signerNameDER);
     }
     SECItem const* const certs[] = { signerDER, nullptr };
     return CreateEncodedOCSPSuccessfulResponse(certStatus, *endEntityCertID,
                                                signerName, signerPrivateKey,
                                                oneDayBeforeNow, oneDayBeforeNow,
                                                &oneDayAfterNow, certs);
   }
 
@@ -447,79 +452,75 @@ protected:
                                     serialNumberDER, issuerDER, notBefore,
                                     notAfter, subjectDER, extensions,
                                     signerKey, SEC_OID_SHA256, privateKey);
   }
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey)
 {
-  SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
-                      "CN=good_indirect_byKey", OCSPResponseContext::good,
-                      byKey));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedIndirectOCSPSuccessfulResponse(
+                         "CN=good_indirect_byKey", OCSPResponseContext::good,
+                         byKey));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName)
 {
-  SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
-                      "CN=good_indirect_byName", OCSPResponseContext::good,
-                      "CN=good_indirect_byName"));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedIndirectOCSPSuccessfulResponse(
+                         "CN=good_indirect_byName", OCSPResponseContext::good,
+                         "CN=good_indirect_byName"));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_byKey_missing_signer)
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, byKey,
-                      missingSignerPrivateKey, oneDayBeforeNow,
-                      oneDayBeforeNow, nullptr));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, byKey,
+                         missingSignerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, nullptr));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_byName_missing_signer)
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, "CN=missing",
-                      missingSignerPrivateKey, oneDayBeforeNow,
-                      oneDayBeforeNow, nullptr));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         "CN=missing", missingSignerPrivateKey,
+                         oneDayBeforeNow, oneDayBeforeNow, nullptr));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      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";
 
@@ -534,28 +535,25 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                               now - (10 * ONE_DAY),
                                               now - (2 * ONE_DAY),
                                               signerName, extensions,
                                               rootPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   SECItem const* const certs[] = { signerDER, nullptr };
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID, signerName,
-                      signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                      &oneDayAfterNow,
-                      certs));
-  ASSERT_TRUE(response);
-
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         signerName, signerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      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[] = {
@@ -569,84 +567,81 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                               now + (2 * ONE_DAY),
                                               now + (10 * ONE_DAY),
                                               signerName, extensions,
                                               rootPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   SECItem const* const certs[] = { signerDER, nullptr };
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                      OCSPResponseContext::good, *endEntityCertID,
-                      signerName, signerPrivateKey, oneDayBeforeNow,
-                      oneDayBeforeNow, &oneDayAfterNow, certs));
-  ASSERT_TRUE(response);
-
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         signerName, signerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku)
 {
-  SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
-                      "CN=good_indirect_wrong_eku", OCSPResponseContext::good,
-                      byKey, SEC_OID_UNKNOWN));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedIndirectOCSPSuccessfulResponse(
+                         "CN=good_indirect_wrong_eku",
+                         OCSPResponseContext::good, byKey, SEC_OID_UNKNOWN));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_wrong_eku)
 {
-  SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
-                      "CN=good_indirect_wrong_eku", OCSPResponseContext::good,
-                      byKey, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
-  ASSERT_TRUE(response);
+  InputBuffer response(CreateEncodedIndirectOCSPSuccessfulResponse(
+                         "CN=good_indirect_wrong_eku",
+                         OCSPResponseContext::good, byKey,
+                         SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 // Test that signature of OCSP response signer cert is verified
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku)
 {
-  SECItem* response(CreateEncodedIndirectOCSPSuccessfulResponse(
-                      "CN=good_indirect_tampered_eku",
-                      OCSPResponseContext::good, byKey,
-                      SEC_OID_EXT_KEY_USAGE_SERVER_AUTH));
-  ASSERT_TRUE(response);
+  InputBuffer 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
-  ASSERT_SECSuccess(TamperOnce(*response,
+  SECItem responseSECItem = UnsafeMapInputBufferToSECItem(response);
+  ASSERT_SECSuccess(TamperOnce(responseSECItem,
                                EKU_SERVER_AUTH, PR_ARRAY_SIZE(EKU_SERVER_AUTH),
                                EKU_OCSP_SIGNER, PR_ARRAY_SIZE(EKU_OCSP_SIGNER)));
 
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, 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";
 
@@ -666,27 +661,25 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1,
                         subCAName, oneDayBeforeNow, oneDayAfterNow,
                         signerName, extensions, unknownPrivateKey.get(),
                         signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by that delegated responder
   SECItem const* const certs[] = { signerDER, nullptr };
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                        OCSPResponseContext::good, *endEntityCertID,
-                        signerName, signerPrivateKey, oneDayBeforeNow,
-                        oneDayBeforeNow, &oneDayAfterNow, certs));
-  ASSERT_TRUE(response);
-
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         signerName, signerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 // The CA that issued the OCSP responder cert is a sub-CA of the issuer of
 // the certificate that the OCSP response is for. That sub-CA cert is included
 // in the OCSP response before the OCSP responder cert.
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_subca_1_first)
@@ -722,28 +715,25 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   SECItem const* const certs[] = { subCADER, signerDER, nullptr };
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                        OCSPResponseContext::good, *endEntityCertID, signerName,
-                        signerPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                        &oneDayAfterNow,
-                        certs));
-  ASSERT_TRUE(response);
-
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         signerName, signerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 // The CA that issued the OCSP responder cert is a sub-CA of the issuer of
 // the certificate that the OCSP response is for. That sub-CA cert is included
 // in the OCSP response after the OCSP responder cert.
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_subca_1_second)
@@ -779,114 +769,112 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   SECItem const* const certs[] = { signerDER, subCADER, nullptr };
-  SECItem* response(CreateEncodedOCSPSuccessfulResponse(
-                        OCSPResponseContext::good, *endEntityCertID,
-                        signerName, signerPrivateKey, oneDayBeforeNow,
-                        oneDayBeforeNow, &oneDayAfterNow, certs));
-  ASSERT_TRUE(response);
-
+  InputBuffer response(CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID,
+                         signerName, signerPrivateKey, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 class pkixocsp_VerifyEncodedResponse_GetCertTrust
   : public pkixocsp_VerifyEncodedResponse_DelegatedResponder {
 public:
-  pkixocsp_VerifyEncodedResponse_GetCertTrust()
-    : signerCertDER(nullptr)
-    , response(nullptr)
-  {
-  }
-
   void SetUp()
   {
     pkixocsp_VerifyEncodedResponse_DelegatedResponder::SetUp();
-    response = CreateEncodedIndirectOCSPSuccessfulResponse(
-                          "CN=OCSPGetCertTrustTest Signer",
-                          OCSPResponseContext::good, byKey,
-                          SEC_OID_OCSP_RESPONDER, &signerCertDER);
-    if (!response || !signerCertDER) {
+
+    InputBuffer
+      createdResponse(
+        CreateEncodedIndirectOCSPSuccessfulResponse(
+          "CN=OCSPGetCertTrustTest Signer", OCSPResponseContext::good,
+          byKey, SEC_OID_OCSP_RESPONDER, &signerCertDER));
+    if (response.Init(createdResponse) != Success) {
+      PR_Abort();
+    }
+
+    if (response.GetLength() == 0 || signerCertDER.GetLength() == 0) {
       PR_Abort();
     }
   }
 
   class TrustDomain : public OCSPTestTrustDomain
   {
   public:
     TrustDomain()
       : certTrustLevel(TrustLevel::InheritsTrust)
     {
     }
 
-    bool SetCertTrust(const SECItem* certDER, TrustLevel certTrustLevel)
+    bool SetCertTrust(InputBuffer certDER, TrustLevel certTrustLevel)
     {
-      this->certDER = certDER;
+      EXPECT_EQ(Success, this->certDER.Init(certDER));
       this->certTrustLevel = certTrustLevel;
       return true;
     }
   private:
     virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
                                 const CertPolicyId&,
-                                const SECItem& candidateCert,
+                                InputBuffer candidateCert,
                                 /*out*/ TrustLevel& trustLevel)
     {
       EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
-      EXPECT_TRUE(certDER);
-      EXPECT_TRUE(SECITEM_ItemsAreEqual(certDER, &candidateCert));
+      EXPECT_NE(0, certDER.GetLength());
+      EXPECT_TRUE(InputBuffersAreEqual(certDER, candidateCert));
       trustLevel = certTrustLevel;
       return Success;
     }
 
-    const SECItem* certDER; // weak pointer
+    InputBuffer certDER;
     TrustLevel certTrustLevel;
   };
 
   TrustDomain trustDomain;
-  const SECItem* signerCertDER; // owned by arena
-  SECItem* response; // owned by arena
+  InputBuffer signerCertDER; // owned by arena
+  InputBuffer response; // owned by arena
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, InheritTrust)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::InheritsTrust));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, TrustAnchor)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::TrustAnchor));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, ActivelyDistrusted)
 {
   ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER,
                                        TrustLevel::ActivelyDistrusted));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
-                                      *response, expired));
+                                      response, expired));
   ASSERT_FALSE(expired);
 }
--- a/lib/mozpkix/test/lib/pkixtestutil.cpp
+++ b/lib/mozpkix/test/lib/pkixtestutil.cpp
@@ -26,17 +26,17 @@
 
 #include <cerrno>
 #include <limits>
 #include <new>
 
 #include "cryptohi.h"
 #include "hasht.h"
 #include "pk11pub.h"
-#include "pkixcheck.h"
+#include "pkix/pkixnss.h"
 #include "pkixder.h"
 #include "prerror.h"
 #include "prinit.h"
 #include "prprf.h"
 #include "secder.h"
 #include "secerr.h"
 
 using namespace std;
@@ -143,16 +143,26 @@ TamperOnce(SECItem& item,
       p = foundFirstByte + toLen;
     } else {
       p = foundFirstByte + 1;
       --remaining;
     }
   }
 }
 
+Result
+InitInputBufferFromSECItem(const SECItem* secItem,
+                           /*out*/ InputBuffer& inputBuffer)
+{
+  if (!secItem) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+  return inputBuffer.Init(secItem->data, secItem->len);
+}
+
 class Output
 {
 public:
   Output()
     : numItems(0)
     , length(0)
   {
   }
@@ -1322,40 +1332,44 @@ SingleResponse(OCSPResponseContext& cont
 SECItem*
 CertID(OCSPResponseContext& context)
 {
   SECItem* hashAlgorithm = AlgorithmIdentifier(context.arena,
                                                context.certIDHashAlg);
   if (!hashAlgorithm) {
     return nullptr;
   }
-  SECItem* issuerNameHash = HashedOctetString(context.arena,
-                                              context.certID.issuer,
+  SECItem issuerSECItem = UnsafeMapInputBufferToSECItem(context.certID.issuer);
+  SECItem* issuerNameHash = HashedOctetString(context.arena, issuerSECItem,
                                               context.certIDHashAlg);
   if (!issuerNameHash) {
     return nullptr;
   }
 
+  SECItem issuerSubjectPublicKeyInfoSECItem =
+    UnsafeMapInputBufferToSECItem(context.certID.issuerSubjectPublicKeyInfo);
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
     spki(SECKEY_DecodeDERSubjectPublicKeyInfo(
-          &context.certID.issuerSubjectPublicKeyInfo));
+           &issuerSubjectPublicKeyInfoSECItem));
   if (!spki) {
     return nullptr;
   }
   SECItem* issuerKeyHash(KeyHashHelper(context.arena, spki.get()));
   if (!issuerKeyHash) {
     return nullptr;
   }
 
   static const SEC_ASN1Template serialTemplate[] = {
     { SEC_ASN1_INTEGER, 0 },
     { 0 }
   };
+  SECItem serialNumberSECItem =
+    UnsafeMapInputBufferToSECItem(context.certID.serialNumber);
   SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr,
-                                             &context.certID.serialNumber,
+                                             &serialNumberSECItem,
                                              serialTemplate);
   if (!serialNumber) {
     return nullptr;
   }
 
   Output output;
   if (output.Add(hashAlgorithm) != Success) {
     return nullptr;
--- a/lib/mozpkix/test/lib/pkixtestutil.h
+++ b/lib/mozpkix/test/lib/pkixtestutil.h
@@ -32,16 +32,26 @@
 #include "keyhi.h"
 #include "pkix/enumclass.h"
 #include "pkix/pkixtypes.h"
 #include "pkix/ScopedPtr.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
+class TestInputBuffer : public InputBuffer
+{
+public:
+  template <size_t N>
+  explicit TestInputBuffer(const char (&valueString)[N])
+    : InputBuffer(reinterpret_cast<const uint8_t(&)[N-1]>(valueString))
+  {
+  }
+};
+
 namespace {
 
 inline void
 fclose_void(FILE* file) {
   (void) fclose(file);
 }
 
 inline void
@@ -80,16 +90,19 @@ const SECItem* ASCIIToDERName(PLArenaPoo
 // for avoiding making multiple changes at once.
 //
 // 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.
 SECStatus TamperOnce(SECItem& item, const uint8_t* from, size_t fromLen,
                      const uint8_t* to, size_t toLen);
 
+Result InitInputBufferFromSECItem(const SECItem* secItem,
+                                  /*out*/ InputBuffer& inputBuffer);
+
 ///////////////////////////////////////////////////////////////////////////////
 // Encode Certificates
 
 enum Version { v1 = 0, v2 = 1, v3 = 2 };
 
 // serialNumber is assumed to be the DER encoding of an INTEGER.
 //
 // If extensions is null, then no extensions will be encoded. Otherwise,