Bug 968490: Add mozilla::pkix::der unit tests (r=cviecco)
authorStefan Arentz <sarentz@mozilla.com>
Wed, 26 Mar 2014 16:00:03 -0700
changeset 180344 aad09525748b45199216520e703fd73d2104523c
parent 180343 496007a2b3aeed32e03fc9223ecfd93f2355233c
child 180345 be6f50f0f58325cb55abac511e95e87513cc5419
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewerscviecco
bugs968490
milestone31.0a1
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