/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
 * of licensing terms:
 */
/* 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/.
 */
/* Copyright 2013 Mozilla Contributors
 *
 * 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 "pkixgtest.h"

#include "mozpkix/pkixder.h"

using namespace mozilla::pkix;
using namespace mozilla::pkix::der;

namespace {

class pkixder_input_tests : public ::testing::Test { };

static const uint8_t DER_SEQUENCE_EMPTY[] = {
  0x30,                       // SEQUENCE
  0x00,                       // length
};

static const uint8_t DER_SEQUENCE_NOT_EMPTY[] = {
  0x30,                       // SEQUENCE
  0x01,                       // length
  'X',                        // value
};

static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE[] = {
  'X',                        // value
};

static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED[] = {
  0x30,                       // SEQUENCE
  0x01,                       // length
};

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
};

static const Input EMPTY_INPUT;

TEST_F(pkixder_input_tests, InputInit)
{
  Input buf;
  ASSERT_EQ(Success,
            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}

TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
{
  Input buf;
  ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 0));

  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 Reader class was intended to protect against
  // buffer overflows, and there's no risk with the current behavior. See bug
  // 1000354.
  ASSERT_EQ(Success, buf.Init((const uint8_t*) "hello", 0));
  ASSERT_TRUE(buf.GetLength() == 0);
}

TEST_F(pkixder_input_tests, InputInitWithLargeData)
{
  Input 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, buf.Init((const uint8_t*) "", 0xffff));
}

TEST_F(pkixder_input_tests, InputInitMultipleTimes)
{
  Input buf;

  ASSERT_EQ(Success,
            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));

  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
            buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}

TEST_F(pkixder_input_tests, PeekWithinBounds)
{
  const uint8_t der[] = { 0x11, 0x11 };
  Input buf(der);
  Reader input(buf);
  ASSERT_TRUE(input.Peek(0x11));
  ASSERT_FALSE(input.Peek(0x22));
}

TEST_F(pkixder_input_tests, PeekPastBounds)
{
  const uint8_t der[] = { 0x11, 0x22 };
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 1));
  Reader 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)
{
  const uint8_t der[] = { 0x11, 0x22 };
  Input buf(der);
  Reader 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)
{
  const uint8_t der[] = { 0x11, 0x22 };
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 1));
  Reader 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);
}

TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer)
{
  // 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.
  uintptr_t derint = -1;
  auto der = reinterpret_cast<const uint8_t*>(derint);
  ASSERT_EQ(sizeof(der), sizeof(derint))
      << "underflow of pointer might not work";
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 0));
  Reader input(buf);

  uint8_t b;
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}

TEST_F(pkixder_input_tests, ReadWord)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader 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)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 2)); // Initialize with too-short length
  Reader 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)
{
  const uint8_t der[] = { 0x11, 0x22 };
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 1));
  Reader input(buf);

  uint16_t readWord1 = 0;
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1));
  ASSERT_NE(0x1122, readWord1);
}

static void UNSANITIZED_ReadWordWrapAroundPointer()
#if defined(__clang__)
    /* Use "undefined" instead of more specific "pointer-overflow" for
     * clang 4.0.0 backward compatability. */
    __attribute__((no_sanitize("undefined")))
#endif
{
  const uint8_t* der = nullptr;
  --der;
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 0));
  Reader input(buf);
  uint16_t b;
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}

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.
  UNSANITIZED_ReadWordWrapAroundPointer();
}

TEST_F(pkixder_input_tests, Skip)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader 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, Skip_ToEnd)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);
  ASSERT_EQ(Success, input.Skip(sizeof der));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, Skip_PastEnd)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1));
}

TEST_F(pkixder_input_tests, Skip_ToNewInput)
{
  const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
  Input buf(der);
  Reader input(buf);

  Reader 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());

  // Reader 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, Skip_ToNewInputPastEnd)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  Reader skippedInput;
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput));
}

TEST_F(pkixder_input_tests, Skip_ToInput)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };

  Input item;
  ASSERT_EQ(Success, input.Skip(sizeof expectedItemData, item));

  Input expected(expectedItemData);
  ASSERT_TRUE(InputsAreEqual(expected, item));
}

static void UNSANITIZED_Skip_WrapAroundPointer()
#if defined(__clang__)
    /* Use "undefined" instead of more specific "pointer-overflow" for
     * clang 4.0.0 backward compatability. */
    __attribute__((no_sanitize("undefined")))
#endif
{
  const uint8_t* der = nullptr;
  // coverity[FORWARD_NULL]
  --der;
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 0));
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1));
}

TEST_F(pkixder_input_tests, Skip_WrapAroundPointer) {
  // 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.
  UNSANITIZED_Skip_WrapAroundPointer();
}

TEST_F(pkixder_input_tests, Skip_ToInputPastEnd)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  Input skipped;
  ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skipped));
}

TEST_F(pkixder_input_tests, SkipToEnd_ToInput)
{
  static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  Input skipped;
  ASSERT_EQ(Success, input.SkipToEnd(skipped));
}

TEST_F(pkixder_input_tests, SkipToEnd_ToInput_InputAlreadyInited)
{
  static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  static const uint8_t initialValue[] = { 0x01, 0x02, 0x03 };
  Input x(initialValue);
  // Fails because skipped was already initialized once, and Inputs are not
  // allowed to be Init()d multiple times.
  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.SkipToEnd(x));
  ASSERT_TRUE(InputsAreEqual(x, Input(initialValue)));
}

TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
{
  Input buf(DER_SEQUENCE_OF_INT8);
  Reader input(buf);

  ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
  ASSERT_EQ(Success, End(input));
}

TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);

  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE));
}

TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
{
  Input buf(DER_OVERRUN_SEQUENCE_OF_INT8);
  Reader input(buf);
  ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}

TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput)
{
  Reader input;
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, AtEndAtBeginning)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);
  ASSERT_FALSE(input.AtEnd());
}

TEST_F(pkixder_input_tests, AtEndAtEnd)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);
  ASSERT_EQ(Success, input.Skip(sizeof der));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, MarkAndGetInput)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  Reader::Mark mark = input.GetMark();

  const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };

  ASSERT_EQ(Success, input.Skip(sizeof expectedItemData));

  Input item;
  ASSERT_EQ(Success, input.GetInput(mark, item));
  Input expected(expectedItemData);
  ASSERT_TRUE(InputsAreEqual(expected, item));
}

// Cannot run this test on debug builds because of the NotReached
#ifdef NDEBUG
TEST_F(pkixder_input_tests, MarkAndGetInputDifferentInput)
{
  const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
  Input buf(der);
  Reader input(buf);

  Reader another;
  Reader::Mark mark = another.GetMark();

  ASSERT_EQ(Success, input.Skip(3));

  Input item;
  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.GetInput(mark, item));
}
#endif

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_AtEnd)
{
  Reader input(EMPTY_INPUT);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_TruncatedAfterTag)
{
  static const uint8_t DER[] = { SEQUENCE };
  Input buf(DER);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidEmpty)
{
  Input buf(DER_SEQUENCE_EMPTY);
  Reader input(buf);
  uint8_t tag = 0;
  Input value;
  ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
  ASSERT_EQ(SEQUENCE, tag);
  ASSERT_EQ(0u, value.GetLength());
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidNotEmpty)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  uint8_t tag = 0;
  Input value;
  ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
  ASSERT_EQ(SEQUENCE, tag);
  Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
  ASSERT_TRUE(InputsAreEqual(expected, value));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests,
       ReadTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidWrongLength)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm1)
{
  // High tag number form is not allowed (illegal 1 byte tag)
  //
  // If the decoder treats 0x1F as a valid low tag number tag, then it will
  // treat the actual tag (1) as a length, and then it will return Success
  // with value == { 0x00 } and tag == 0x1f.
  //
  // It is illegal to encode tag 1 in the high tag number form because it isn't
  // the shortest encoding (the low tag number form is).
  static const uint8_t DER[] = {
    0x1F, // high tag number form indicator
    1,    // tag 1 (not legal!)
    0     // length zero
  };
  Input buf(DER);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm2)
{
  // High tag number form is not allowed (legal 1 byte tag).
  //
  // ReadTagAndGetValue's check to prohibit the high tag number form has no
  // effect on whether this test passes or fails, because ReadTagAndGetValue
  // will interpret the second byte (31) as a length, and the input doesn't
  // have 31 bytes following it. This test is here to guard against the case
  // where somebody actually implements high tag number form parsing, to remind
  // that person that they need to add tests here, including in particular
  // tests for overly-long encodings.
  static const uint8_t DER[] = {
    0x1F, // high tag number form indicator
    31,   // tag 31
    0     // length zero
  };
  Input buf(DER);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm3)
{
  // High tag number form is not allowed (2 byte legal tag)
  //
  // ReadTagAndGetValue's check to prohibit the high tag number form has no
  // effect on whether this test passes or fails, because ReadTagAndGetValue
  // will interpret the second byte as a length, and the input doesn't have
  // that many bytes following it. This test is here to guard against the case
  // where somebody actually implements high tag number form parsing, to remind
  // that person that they need to add tests here, including in particular
  // tests for overly-long encodings.
  static const uint8_t DER[] = {
    0x1F,              // high tag number form indicator
    0x80 | 0x01, 0x00, // tag 0x100 (256)
    0                  // length zero
  };
  Input buf(DER);
  Reader input(buf);
  uint8_t tag;
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ReadTagAndGetValue(input, tag, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidEmpty)
{
  Input buf(DER_SEQUENCE_EMPTY);
  Reader input(buf);
  Reader value;
  ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
  ASSERT_TRUE(value.AtEnd());
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidNotEmpty)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Reader 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_Reader_InvalidNotEmptyValueTruncated)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
  Reader input(buf);
  Reader value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, SEQUENCE, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongLength)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);
  Reader value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, SEQUENCE, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongTag)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Reader value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, INTEGER, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty)
{
  Input buf(DER_SEQUENCE_EMPTY);
  Reader input(buf);
  Input value;
  ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
  ASSERT_EQ(0u, value.GetLength());
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidNotEmpty)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Input value;
  ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
  Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
  ASSERT_TRUE(InputsAreEqual(expected, value));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests,
       ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
  Reader input(buf);
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, SEQUENCE, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, SEQUENCE, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongTag)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Input value;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            ExpectTagAndGetValue(input, INTEGER, value));
}

TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_ValidEmpty)
{
  Input buf(DER_SEQUENCE_EMPTY);
  Reader input(buf);
  ASSERT_EQ(Success, ExpectTagAndEmptyValue(input, SEQUENCE));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InValidNotEmpty)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}

TEST_F(pkixder_input_tests,
       ExpectTagAndEmptyValue_Input_InvalidNotEmptyValueTruncated)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}

TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongLength)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}

TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongTag)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, INTEGER));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidEmpty)
{
  Input buf(DER_SEQUENCE_EMPTY);
  Reader input(buf);
  Input tlv;
  ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
  Input expected(DER_SEQUENCE_EMPTY);
  ASSERT_TRUE(InputsAreEqual(expected, tlv));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidNotEmpty)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Input tlv;
  ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
  Input expected(DER_SEQUENCE_NOT_EMPTY);
  ASSERT_TRUE(InputsAreEqual(expected, tlv));
  ASSERT_TRUE(input.AtEnd());
}

TEST_F(pkixder_input_tests,
       ExpectTagAndGetTLV_Input_InvalidNotEmptyValueTruncated)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
  Reader input(buf);
  Input tlv;
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongLength)
{
  Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);
  Input tlv;
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}

TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongTag)
{
  Input buf(DER_SEQUENCE_NOT_EMPTY);
  Reader input(buf);
  Input tlv;
  ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv));
}

TEST_F(pkixder_input_tests, EndAtEnd)
{
  Input buf(DER_INT16);
  Reader input(buf);
  ASSERT_EQ(Success, input.Skip(4));
  ASSERT_EQ(Success, End(input));
}

TEST_F(pkixder_input_tests, EndBeforeEnd)
{
  Input buf(DER_INT16);
  Reader input(buf);
  ASSERT_EQ(Success, input.Skip(2));
  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}

TEST_F(pkixder_input_tests, EndAtBeginning)
{
  Input buf(DER_INT16);
  Reader input(buf);
  ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}

// TODO: Need tests for Nested too?

Result NestedOfHelper(Reader& input, std::vector<uint8_t>& readValues)
{
  uint8_t value = 0;
  Result rv = input.Read(value);
  EXPECT_EQ(Success, rv);
  if (rv != Success) {
    return rv;
  }
  readValues.push_back(value);
  return Success;
}

TEST_F(pkixder_input_tests, NestedOf)
{
  Input buf(DER_SEQUENCE_OF_INT8);
  Reader input(buf);

  std::vector<uint8_t> readValues;
  ASSERT_EQ(Success,
            NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
                     [&readValues](Reader& r) {
                       return NestedOfHelper(r, readValues);
                     }));
  ASSERT_EQ(3u, 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 buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
  Reader input(buf);

  std::vector<uint8_t> readValues;
  ASSERT_EQ(Result::ERROR_BAD_DER,
            NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
                     [&readValues](Reader& r) {
                       return NestedOfHelper(r, readValues);
                     }));
  ASSERT_EQ(0u, readValues.size());
}

TEST_F(pkixder_input_tests, MatchRestAtEnd)
{
  static const uint8_t der[1] = { };
  Input buf;
  ASSERT_EQ(Success, buf.Init(der, 0));
  Reader input(buf);
  ASSERT_TRUE(input.AtEnd());
  static const uint8_t toMatch[] = { 1 };
  ASSERT_FALSE(input.MatchRest(toMatch));
}

TEST_F(pkixder_input_tests, MatchRest1Match)
{
  static const uint8_t der[] = { 1 };
  Input buf(der);
  Reader input(buf);
  ASSERT_FALSE(input.AtEnd());
  ASSERT_TRUE(input.MatchRest(der));
}

TEST_F(pkixder_input_tests, MatchRest1Mismatch)
{
  static const uint8_t der[] = { 1 };
  Input buf(der);
  Reader input(buf);
  static const uint8_t toMatch[] = { 2 };
  ASSERT_FALSE(input.MatchRest(toMatch));
  ASSERT_FALSE(input.AtEnd());
}

TEST_F(pkixder_input_tests, MatchRest2WithTrailingByte)
{
  static const uint8_t der[] = { 1, 2, 3 };
  Input buf(der);
  Reader input(buf);
  static const uint8_t toMatch[] = { 1, 2 };
  ASSERT_FALSE(input.MatchRest(toMatch));
}

TEST_F(pkixder_input_tests, MatchRest2Mismatch)
{
  static const uint8_t der[] = { 1, 2, 3 };
  Input buf(der);
  Reader input(buf);
  static const uint8_t toMatchMismatch[] = { 1, 3 };
  ASSERT_FALSE(input.MatchRest(toMatchMismatch));
  ASSERT_TRUE(input.MatchRest(der));
}

} // namespace
