Bug 968490: Add mozilla::pkix::der unit tests (r=cviecco)
authorStefan Arentz <sarentz@mozilla.com>
Wed, 26 Mar 2014 16:00:03 -0700
changeset 199608 aad09525748b45199216520e703fd73d2104523c
parent 199607 496007a2b3aeed32e03fc9223ecfd93f2355233c
child 199609 be6f50f0f58325cb55abac511e95e87513cc5419
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscviecco
bugs968490
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 968490: Add mozilla::pkix::der unit tests (r=cviecco)
security/pkix/lib/pkixder.h
security/pkix/moz.build
security/pkix/test/gtest/moz.build
security/pkix/test/gtest/pkixder_input_tests.cpp
security/pkix/test/gtest/pkixder_pki_types_tests.cpp
security/pkix/test/gtest/pkixder_universal_types_tests.cpp
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -184,23 +184,24 @@ public:
     friend class Input;
     explicit Mark(const uint8_t* mark) : mMark(mark) { }
     const uint8_t* const mMark;
     void operator=(const Mark&) /* = delete */;
   };
 
   Mark GetMark() const { return Mark(input); }
 
-  void GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item)
+  bool GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item)
   {
     PR_ASSERT(mark.mMark < input);
     item.type = type;
     item.data = const_cast<uint8_t*>(mark.mMark);
-    // TODO: bounds check
+    // TODO: Return false if bounds check fails
     item.len = input - mark.mMark;
+    return true;
   }
 
 private:
   const uint8_t* input;
   const uint8_t* end;
 
   Input(const Input&) /* = delete */;
   void operator=(const Input&) /* = delete */;
--- a/security/pkix/moz.build
+++ b/security/pkix/moz.build
@@ -12,17 +12,18 @@ UNIFIED_SOURCES += [
     'lib/pkixkey.cpp',
     'lib/pkixocsp.cpp',
 ]
 
 LOCAL_INCLUDES += [
     'include',
 ]
 
-DIRS += [
+TEST_DIRS += [
+    'test/gtest',
     'test/lib',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBRARY_NAME = 'mozillapkix'
 
 FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+LIBRARY_NAME = 'mozillapkix_gtest'
+
+SOURCES += [
+    'pkixder_input_tests.cpp',
+    'pkixder_pki_types_tests.cpp',
+    'pkixder_universal_types_tests.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '../../include',
+    '../../lib',
+]
+
+FINAL_LIBRARY='xul-gtest'
+
+include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixder_input_tests.cpp
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Copyright 2013 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "pkix/bind.h"
+#include "pkixder.h"
+
+using namespace mozilla::pkix::der;
+
+namespace {
+
+class pkixder_input_tests : public ::testing::Test
+{
+protected:
+  virtual void SetUp()
+  {
+    PR_SetError(0, 0);
+  }
+};
+
+const uint8_t DER_SEQUENCE_OF_INT8[] = {
+  0x30,                       // SEQUENCE
+  0x09,                       // length
+  0x02, 0x01, 0x01,           // INTEGER length 1 value 0x01
+  0x02, 0x01, 0x02,           // INTEGER length 1 value 0x02
+  0x02, 0x01, 0x03            // INTEGER length 1 value 0x03
+};
+
+const uint8_t DER_TRUNCATED_SEQUENCE_OF_INT8[] = {
+  0x30,                       // SEQUENCE
+  0x09,                       // length
+  0x02, 0x01, 0x01,           // INTEGER length 1 value 0x01
+  0x02, 0x01, 0x02            // INTEGER length 1 value 0x02
+  // MISSING DATA HERE ON PURPOSE
+};
+
+const uint8_t DER_OVERRUN_SEQUENCE_OF_INT8[] = {
+  0x30,                       // SEQUENCE
+  0x09,                       // length
+  0x02, 0x01, 0x01,           // INTEGER length 1 value 0x01
+  0x02, 0x01, 0x02,           // INTEGER length 1 value 0x02
+  0x02, 0x02, 0xFF, 0x03      // INTEGER length 2 value 0xFF03
+};
+
+const uint8_t DER_INT16[] = {
+  0x02,                       // INTEGER
+  0x02,                       // length
+  0x12, 0x34                  // 0x1234
+};
+
+TEST_F(pkixder_input_tests, FailWithError)
+{
+  ASSERT_EQ(Failure, Fail(SEC_ERROR_BAD_DER));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+
+  ASSERT_EQ(Failure, Fail(SEC_ERROR_INVALID_ARGS));
+  ASSERT_EQ(SEC_ERROR_INVALID_ARGS, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, InputInit)
+{
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+}
+
+TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
+{
+  Input input;
+  ASSERT_EQ(Failure, input.Init(nullptr, 0));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+
+  ASSERT_EQ(Failure, input.Init(nullptr, 100));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+
+  // Is this a bug?
+  ASSERT_EQ(Success, input.Init((const uint8_t*) "hello", 0));
+}
+
+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(Failure, input.Init((const uint8_t*) "", 0xffff+1));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+
+  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(Failure,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+  ASSERT_EQ(SEC_ERROR_INVALID_ARGS, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, ExpectSuccess)
+{
+  Input input;
+
+  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());
+}
+
+TEST_F(pkixder_input_tests, ExpectMismatch)
+{
+  Input input;
+
+  ASSERT_EQ(Success,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+
+  const uint8_t expected[] = { 0x11, 0x22 };
+  ASSERT_EQ(Failure, input.Expect(expected, sizeof expected));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+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(Failure, input.Expect(expected, sizeof expected));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, PeekWithinBounds)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x11 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+  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));
+
+  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));
+
+  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));
+
+  uint8_t readByte1 = 0;
+  ASSERT_EQ(Success, input.Read(readByte1));
+  ASSERT_EQ(0x11, readByte1);
+
+  uint8_t readByte2 = 0;
+  ASSERT_EQ(Failure, input.Read(readByte2));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+  ASSERT_NE(0x22, readByte2);
+}
+
+TEST_F(pkixder_input_tests, ReadWord)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  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));
+
+  uint16_t readWord1 = 0;
+  ASSERT_EQ(Success, input.Read(readWord1));
+  ASSERT_EQ(0x1122, readWord1);
+
+  uint16_t readWord2 = 0;
+  ASSERT_EQ(Failure, input.Read(readWord2));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+  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));
+
+  uint16_t readWord1 = 0;
+  ASSERT_EQ(Failure, input.Read(readWord1));
+  ASSERT_NE(0x1122, readWord1);
+}
+
+TEST_F(pkixder_input_tests, InputSkip)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  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));
+  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));
+
+  ASSERT_EQ(Failure, input.Skip(sizeof der + 1));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, InputSkipToNewInput)
+{
+  Input input;
+  const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  Input skippedInput;
+  ASSERT_EQ(Success, input.Skip(3, skippedInput));
+
+  uint8_t readByte1 = 0;
+  ASSERT_EQ(Success, input.Read(readByte1));
+  ASSERT_EQ(0x04, readByte1);
+
+  ASSERT_TRUE(input.AtEnd());
+
+  // Input has no Remaining() or Length() so we simply read the bytes
+  // and then expect to be at the end.
+
+  for (uint8_t i = 1; i <= 3; ++i) {
+    uint8_t readByte = 0;
+    ASSERT_EQ(Success, skippedInput.Read(readByte));
+    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));
+
+  Input skippedInput;
+  ASSERT_EQ(Failure, input.Skip(sizeof der * 2, skippedInput));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, InputSkipToSECItem)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
+
+  SECItem 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));
+}
+
+TEST_F(pkixder_input_tests, SkipToSECItemPastEnd)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  SECItem skippedSECItem;
+  ASSERT_EQ(Failure, input.Skip(sizeof der + 1, skippedSECItem));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, Skip)
+{
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+
+  ASSERT_EQ(Success, Skip(input, SEQUENCE));
+  ASSERT_EQ(Success, End(input));
+}
+
+TEST_F(pkixder_input_tests, SkipWithTruncatedData)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
+                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+
+  ASSERT_EQ(Failure, Skip(input, SEQUENCE));
+}
+
+TEST_F(pkixder_input_tests, SkipWithOverrunData)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_OVERRUN_SEQUENCE_OF_INT8,
+                                sizeof DER_OVERRUN_SEQUENCE_OF_INT8));
+  ASSERT_EQ(Success, Skip(input, SEQUENCE));
+  ASSERT_EQ(Failure, 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));
+  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));
+  ASSERT_EQ(Success, input.Skip(sizeof der));
+  ASSERT_TRUE(input.AtEnd());
+}
+
+TEST_F(pkixder_input_tests, MarkAndGetSECItem)
+{
+  Input input;
+  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
+  ASSERT_EQ(Success, input.Init(der, sizeof der));
+
+  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_TRUE(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));
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndLength)
+{
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+
+  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));
+
+  // Wrong length
+  ASSERT_EQ(Failure, ExpectTagAndLength(input, INTEGER, 4));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndLengthWithWrongTag)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+
+  // Wrong type
+  ASSERT_EQ(Failure, ExpectTagAndLength(input, OCTET_STRING, 2));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndGetLength)
+{
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
+
+  uint16_t length = 0;
+  ASSERT_EQ(Success, 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));
+
+  uint16_t length = 0;
+  ASSERT_EQ(Failure, ExpectTagAndGetLength(input, INTEGER, length));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndGetLengthWithWrongLength)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_TRUNCATED_SEQUENCE_OF_INT8,
+                                sizeof DER_TRUNCATED_SEQUENCE_OF_INT8));
+
+  uint16_t length = 0;
+  ASSERT_EQ(Failure, ExpectTagAndGetLength(input, SEQUENCE, length));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndIgnoreLength)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  ASSERT_EQ(Success, ExpectTagAndIgnoreLength(input, INTEGER));
+}
+
+TEST_F(pkixder_input_tests, ExpectTagAndIgnoreLengthWithWrongTag)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+
+  ASSERT_EQ(Failure, ExpectTagAndIgnoreLength(input, OCTET_STRING));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, EndAtEnd)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  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));
+  ASSERT_EQ(Success, input.Skip(2));
+  ASSERT_EQ(Failure, End(input));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_input_tests, EndAtBeginning)
+{
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INT16, sizeof DER_INT16));
+  ASSERT_EQ(Failure, End(input));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+// TODO: Need tests for Nested too?
+
+Result NestedOfHelper(Input& input, std::vector<uint8_t>& readValues)
+{
+  uint8_t value = 0;
+  if (input.Read(value) != Success) {
+    return Failure;
+  }
+  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));
+
+  std::vector<uint8_t> readValues;
+  ASSERT_EQ(Success,
+    NestedOf(input, SEQUENCE, INTEGER, MustNotBeEmpty,
+             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));
+
+  std::vector<uint8_t> readValues;
+  ASSERT_EQ(Failure,
+    NestedOf(input, SEQUENCE, INTEGER, MustNotBeEmpty,
+             mozilla::pkix::bind(NestedOfHelper, mozilla::pkix::_1,
+                                 mozilla::pkix::ref(readValues))));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+  ASSERT_EQ((size_t) 0, readValues.size());
+}
+} // unnamed namespace
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Copyright 2013 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "pkix/bind.h"
+#include "pkixder.h"
+
+using namespace mozilla::pkix::der;
+
+namespace {
+
+class pkixder_pki_types_tests : public ::testing::Test
+{
+protected:
+  virtual void SetUp()
+  {
+    PR_SetError(0, 0);
+  }
+};
+
+TEST_F(pkixder_pki_types_tests, AlgorithmIdentifierNoParams)
+{
+  const uint8_t DER_ALGORITHM_IDENTIFIER_NO_PARAMS[] = {
+    0x06, 0x04, 0xde, 0xad, 0xbe, 0xef   // OID
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ALGORITHM_IDENTIFIER_NO_PARAMS,
+                                sizeof DER_ALGORITHM_IDENTIFIER_NO_PARAMS));
+
+  const uint8_t expectedAlgorithmID[] = {
+    0xde, 0xad, 0xbe, 0xef
+  };
+
+  SECAlgorithmID algorithmID;
+  ASSERT_EQ(Success, AlgorithmIdentifier(input, algorithmID));
+
+  ASSERT_EQ(sizeof expectedAlgorithmID, algorithmID.algorithm.len);
+  ASSERT_EQ(0, memcmp(algorithmID.algorithm.data, expectedAlgorithmID,
+                      sizeof expectedAlgorithmID));
+
+  ASSERT_EQ(0, algorithmID.parameters.len);
+  ASSERT_FALSE(algorithmID.parameters.data);
+}
+
+TEST_F(pkixder_pki_types_tests, AlgorithmIdentifierNullParams)
+{
+  const uint8_t DER_ALGORITHM_IDENTIFIER_NULL_PARAMS[] = {
+    0x30, 0x08,                         // SEQUENCE
+    0x06, 0x04, 0xde, 0xad, 0xbe, 0xef, // OID
+    0x05, 0x00                          // NULL
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ALGORITHM_IDENTIFIER_NULL_PARAMS,
+                                sizeof DER_ALGORITHM_IDENTIFIER_NULL_PARAMS));
+
+  uint16_t length;
+  ASSERT_EQ(Success, ExpectTagAndGetLength(input, SEQUENCE, length));
+
+  Input nested;
+  ASSERT_EQ(Success, input.Skip(length, nested));
+
+  const uint8_t expectedAlgorithmID[] = {
+    0xde, 0xad, 0xbe, 0xef
+  };
+
+  SECAlgorithmID algorithmID;
+  ASSERT_EQ(Success, AlgorithmIdentifier(nested, algorithmID));
+
+  ASSERT_EQ(sizeof expectedAlgorithmID, algorithmID.algorithm.len);
+  ASSERT_TRUE(memcmp(algorithmID.algorithm.data, expectedAlgorithmID,
+                     sizeof expectedAlgorithmID) == 0);
+
+  ASSERT_EQ((size_t) 0, algorithmID.parameters.len);
+  ASSERT_EQ(NULL, algorithmID.parameters.data);
+}
+
+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
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL, sizeof DER_CERT_SERIAL));
+
+  SECItem 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);
+}
+
+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
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_LONGEST,
+                                sizeof DER_CERT_SERIAL_LONGEST));
+
+  SECItem 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);
+}
+
+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
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_CRAZY_LONG,
+                                sizeof DER_CERT_SERIAL_CRAZY_LONG));
+
+  SECItem 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
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_CERT_SERIAL_ZERO_LENGTH,
+                                sizeof DER_CERT_SERIAL_ZERO_LENGTH));
+
+  SECItem item;
+  ASSERT_EQ(Failure, CertificateSerialNumber(input, item));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_pki_types_tests, OptionalVersionV1)
+{
+  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));
+
+  uint8_t version = 99;
+  // TODO(bug 982783): An explicit value of 1 is not allowed, because it is not
+  // the shortest possible encoding!
+  ASSERT_EQ(Success, OptionalVersion(input, version));
+  ASSERT_EQ(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));
+
+  uint8_t version = 99;
+  ASSERT_EQ(Success, OptionalVersion(input, version));
+  ASSERT_EQ(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));
+
+  uint8_t version = 99;
+  ASSERT_EQ(Success, OptionalVersion(input, version));
+  ASSERT_EQ(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));
+
+  uint8_t version = 99;
+  ASSERT_EQ(Success, OptionalVersion(input, version));
+  ASSERT_EQ(0x42, 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));
+
+  uint8_t version = 99;
+  ASSERT_EQ(Failure, OptionalVersion(input, version));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+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));
+
+  uint8_t version = 99;
+  ASSERT_EQ(Success, OptionalVersion(input, version));
+  ASSERT_EQ(v1, version);
+}
+} // unnamed namespace
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Copyright 2013 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "pkix/bind.h"
+#include "pkixder.h"
+
+using namespace mozilla::pkix::der;
+
+namespace {
+
+class pkixder_universal_types_tests : public ::testing::Test
+{
+protected:
+  virtual void SetUp()
+  {
+    PR_SetError(0, 0);
+  }
+};
+
+TEST_F(pkixder_universal_types_tests, BooleanTrue01)
+{
+  const uint8_t DER_BOOLEAN_TRUE_01[] = {
+    0x01,                       // INTEGER
+    0x01,                       // length
+    0x01                        // invalid
+  };
+
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_BOOLEAN_TRUE_01, sizeof DER_BOOLEAN_TRUE_01));
+
+  bool value = false;
+  ASSERT_EQ(Failure, Boolean(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, BooleanTrue42)
+{
+  const uint8_t DER_BOOLEAN_TRUE_42[] = {
+    0x01,                       // INTEGER
+    0x01,                       // length
+    0x42                        // invalid
+  };
+
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_BOOLEAN_TRUE_42, sizeof DER_BOOLEAN_TRUE_42));
+
+  bool value = false;
+  ASSERT_EQ(Failure, Boolean(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, BooleanTrueFF)
+{
+  const uint8_t DER_BOOLEAN_TRUE_FF[] = {
+    0x01,                       // INTEGER
+    0x01,                       // length
+    0xff                        // true
+  };
+
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_BOOLEAN_TRUE_FF, sizeof DER_BOOLEAN_TRUE_FF));
+
+  bool value = false;
+  ASSERT_EQ(Success, Boolean(input, value));
+  ASSERT_EQ(true, value);
+}
+
+TEST_F(pkixder_universal_types_tests, BooleanFalse)
+{
+  const uint8_t DER_BOOLEAN_FALSE[] = {
+    0x01,                       // INTEGER
+    0x01,                       // length
+    0x00                        // false
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_FALSE, sizeof DER_BOOLEAN_FALSE));
+
+  bool value = true;
+  ASSERT_EQ(Success, Boolean(input, value));
+  ASSERT_EQ(false, value);
+}
+
+TEST_F(pkixder_universal_types_tests, BooleanInvalidLength)
+{
+  const uint8_t DER_BOOLEAN_INVALID_LENGTH[] = {
+    0x01,                       // INTEGER
+    0x02,                       // length
+    0x42, 0x42                  // invalid
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_LENGTH,
+                                sizeof DER_BOOLEAN_INVALID_LENGTH));
+
+  bool value = true;
+  ASSERT_EQ(Failure, Boolean(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, BooleanInvalidZeroLength)
+{
+  const uint8_t DER_BOOLEAN_INVALID_ZERO_LENGTH[] = {
+    0x01,                       // INTEGER
+    0x00                        // length
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_BOOLEAN_INVALID_ZERO_LENGTH,
+                                sizeof DER_BOOLEAN_INVALID_ZERO_LENGTH));
+
+  bool value = true;
+  ASSERT_EQ(Failure, Boolean(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, Enumerated)
+{
+  const uint8_t DER_ENUMERATED[] = {
+    0x0a,                       // INTEGER
+    0x01,                       // length
+    0x42                        // value
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED));
+
+  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,                       // INTEGER
+    0x02,                       // length
+    0x00, 0x01                  // value
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ENUMERATED, sizeof DER_ENUMERATED));
+  uint8_t value = 0;
+  ASSERT_EQ(Failure, 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,                       // INTEGER
+    0x02,                       // length
+    0x12, 0x34                  // value
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_LENGTH,
+                                sizeof DER_ENUMERATED_INVALID_LENGTH));
+
+  uint8_t value = 0;
+  ASSERT_EQ(Failure, Enumerated(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
+{
+  const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = {
+    0x0a,                       // INTEGER
+    0x00                        // length
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_ENUMERATED_INVALID_ZERO_LENGTH,
+                                sizeof DER_ENUMERATED_INVALID_ZERO_LENGTH));
+
+  uint8_t value = 0;
+  ASSERT_EQ(Failure, Enumerated(input, value));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+// TODO: Test all acceptable timestamp formats. Find out what formats
+// are being used by looking at large collection of certs.
+TEST_F(pkixder_universal_types_tests, GeneralizedTimeOffset)
+{
+  const uint8_t DER_GENERALIZED_TIME_OFFSET[] = {
+    0x18,
+    19,
+    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', '-',
+    '0', '7', '0', '0'
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_GENERALIZED_TIME_OFFSET,
+                                sizeof DER_GENERALIZED_TIME_OFFSET));
+
+  PRTime value = 0;
+  ASSERT_EQ(Success, GeneralizedTime(input, value));
+  ASSERT_EQ(673573540000000, value);
+}
+
+TEST_F(pkixder_universal_types_tests, GeneralizedTimeGMT)
+{
+  const uint8_t DER_GENERALIZED_TIME_GMT[] = {
+    0x18,
+    15,
+    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'
+  };
+
+  Input input;
+  Result rv1 = input.Init(DER_GENERALIZED_TIME_GMT,
+                          sizeof DER_GENERALIZED_TIME_GMT);
+  ASSERT_EQ(Success, rv1);
+
+  PRTime value = 0;
+  Result rv2 = GeneralizedTime(input, value);
+  ASSERT_EQ(Success, rv2);
+  ASSERT_EQ(673548340000000, value);
+}
+
+TEST_F(pkixder_universal_types_tests, GeneralizedTimeInvalidZeroLength)
+{
+  const uint8_t DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH[] = {
+    0x18,
+    0x00
+  };
+
+  Input input;
+  ASSERT_EQ(Success,
+            input.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
+                       sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
+
+  PRTime value = 0;
+  ASSERT_EQ(Failure, GeneralizedTime(input, value));
+  ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, Integer)
+{
+  const uint8_t DER_INTEGUR[] = {
+    0x02,                       // INTEGER
+    0x04,                       // length
+    0x11, 0x22, 0x33, 0x44      // 0x11223344
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INTEGUR, sizeof DER_INTEGUR));
+
+  const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33, 0x44 };
+
+  SECItem item;
+  memset(&item, 0x00, sizeof item);
+
+  ASSERT_EQ(Success, Integer(input, item));
+
+  ASSERT_EQ(siBuffer, item.type);
+  ASSERT_EQ((size_t) 4, item.len);
+  ASSERT_TRUE(item.data);
+  ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
+}
+
+TEST_F(pkixder_universal_types_tests, OneByte)
+{
+  const uint8_t DER_INTEGUR[] = {
+    0x02,                       // INTEGER
+    0x01,                       // length
+    0x11                        // 0x11
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INTEGUR, sizeof DER_INTEGUR));
+
+  const uint8_t expectedItemData[] = { 0x11 };
+
+  SECItem item;
+  memset(&item, 0x00, sizeof item);
+
+  ASSERT_EQ(Success, Integer(input, item));
+
+  ASSERT_EQ(siBuffer, item.type);
+  ASSERT_EQ((size_t) 1, item.len);
+  ASSERT_TRUE(item.data);
+  ASSERT_EQ(0, memcmp(item.data, expectedItemData, sizeof expectedItemData));
+}
+
+TEST_F(pkixder_universal_types_tests, IntegerTruncated)
+{
+  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));
+
+  SECItem item;
+  memset(&item, 0x00, sizeof item);
+
+  ASSERT_EQ(Failure, Integer(input, item));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+
+  ASSERT_EQ(0, item.type);
+  ASSERT_EQ(0, item.len);
+}
+
+TEST_F(pkixder_universal_types_tests, IntegerZeroLength)
+{
+  const uint8_t DER_INTEGER_ZERO_LENGTH[] = {
+    0x02,                       // INTEGER
+    0x00                        // length
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INTEGER_ZERO_LENGTH,
+                                sizeof DER_INTEGER_ZERO_LENGTH));
+
+  SECItem item;
+  ASSERT_EQ(Failure, Integer(input, item));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+}
+
+TEST_F(pkixder_universal_types_tests, IntegerOverlyLong1)
+{
+  const uint8_t DER_INTEGER_OVERLY_LONG1[] = {
+    0x02,                       // INTEGER
+    0x02,                       // length
+    0x00, 0x01                  //
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG1,
+                                sizeof DER_INTEGER_OVERLY_LONG1));
+
+  SECItem item;
+  ASSERT_EQ(Failure, Integer(input, item));
+}
+
+TEST_F(pkixder_universal_types_tests, IntegerOverlyLong2)
+{
+  const uint8_t DER_INTEGER_OVERLY_LONG2[] = {
+    0x02,                       // INTEGER
+    0x02,                       // length
+    0xff, 0x80                  //
+  };
+
+  Input input;
+  ASSERT_EQ(Success, input.Init(DER_INTEGER_OVERLY_LONG2,
+                                sizeof DER_INTEGER_OVERLY_LONG2));
+
+  SECItem item;
+  ASSERT_EQ(Failure, Integer(input, item));
+}
+
+TEST_F(pkixder_universal_types_tests, Null)
+{
+  const uint8_t DER_NUL[] = {
+    0x05,
+    0x00
+  };
+
+  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));
+
+  ASSERT_EQ(Failure, 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));
+
+  const uint8_t expectedOID[] = {
+    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
+  };
+
+  ASSERT_EQ(Success, OID(input, expectedOID));
+}
+
+} // unnamed namespace