/* -*- 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 <limits>
#include <stdint.h>
#include <vector>

#include "pkixgtest.h"

#include "mozpkix/pkixder.h"

using namespace mozilla::pkix;
using namespace mozilla::pkix::der;
using namespace mozilla::pkix::test;
using namespace std;

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

TEST_F(pkixder_universal_types_tests, BooleanTrue01)
{
  const uint8_t DER_BOOLEAN_TRUE_01[] = {
    0x01,                       // BOOLEAN
    0x01,                       // length
    0x01                        // invalid
  };
  Input input(DER_BOOLEAN_TRUE_01);
  Reader reader(input);
  bool value = false;
  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(reader, value));
}

TEST_F(pkixder_universal_types_tests, BooleanTrue42)
{
  const uint8_t DER_BOOLEAN_TRUE_42[] = {
    0x01,                       // BOOLEAN
    0x01,                       // length
    0x42                        // invalid
  };
  Input input(DER_BOOLEAN_TRUE_42);
  Reader reader(input);
  bool value = false;
  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(reader, value));
}

static const uint8_t DER_BOOLEAN_TRUE[] = {
  0x01,                       // BOOLEAN
  0x01,                       // length
  0xff                        // true
};

TEST_F(pkixder_universal_types_tests, BooleanTrueFF)
{
  Input input(DER_BOOLEAN_TRUE);
  Reader reader(input);
  bool value = false;
  ASSERT_EQ(Success, Boolean(reader, value));
  ASSERT_TRUE(value);
}

TEST_F(pkixder_universal_types_tests, BooleanFalse)
{
  const uint8_t DER_BOOLEAN_FALSE[] = {
    0x01,                       // BOOLEAN
    0x01,                       // length
    0x00                        // false
  };
  Input input(DER_BOOLEAN_FALSE);
  Reader reader(input);

  bool value = true;
  ASSERT_EQ(Success, Boolean(reader, value));
  ASSERT_FALSE(value);
}

TEST_F(pkixder_universal_types_tests, BooleanInvalidLength)
{
  const uint8_t DER_BOOLEAN_INVALID_LENGTH[] = {
    0x01,                       // BOOLEAN
    0x02,                       // length
    0x42, 0x42                  // invalid
  };
  Input input(DER_BOOLEAN_INVALID_LENGTH);
  Reader reader(input);

  bool value = true;
  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(reader, value));
}

TEST_F(pkixder_universal_types_tests, BooleanInvalidZeroLength)
{
  const uint8_t DER_BOOLEAN_INVALID_ZERO_LENGTH[] = {
    0x01,                       // BOOLEAN
    0x00                        // length
  };
  Input input(DER_BOOLEAN_INVALID_ZERO_LENGTH);
  Reader reader(input);

  bool value = true;
  ASSERT_EQ(Result::ERROR_BAD_DER, Boolean(reader, value));
}

// OptionalBoolean implements decoding of OPTIONAL BOOLEAN DEFAULT FALSE.
// If the field is present, it must be a valid encoding of a BOOLEAN with
// value TRUE. If the field is not present, it defaults to FALSE. For
// compatibility reasons, OptionalBoolean also accepts encodings where the field
// is present with value FALSE (this is technically not a valid DER encoding).
TEST_F(pkixder_universal_types_tests, OptionalBooleanValidEncodings)
{
  {
    const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_TRUE[] = {
      0x01,                       // BOOLEAN
      0x01,                       // length
      0xff                        // true
    };
    Input input(DER_OPTIONAL_BOOLEAN_PRESENT_TRUE);
    Reader reader(input);
    bool value = false;
    ASSERT_EQ(Success, OptionalBoolean(reader, value)) <<
      "Should accept the only valid encoding of a present OPTIONAL BOOLEAN";
    ASSERT_TRUE(value);
    ASSERT_TRUE(reader.AtEnd());
  }

  {
    // The OPTIONAL BOOLEAN is omitted in this data.
    const uint8_t DER_INTEGER_05[] = {
      0x02,                       // INTEGER
      0x01,                       // length
      0x05
    };
    Input input(DER_INTEGER_05);
    Reader reader(input);
    bool value = true;
    ASSERT_EQ(Success, OptionalBoolean(reader, value)) <<
      "Should accept a valid encoding of an omitted OPTIONAL BOOLEAN";
    ASSERT_FALSE(value);
    ASSERT_FALSE(reader.AtEnd());
  }

  {
    Input input;
    ASSERT_EQ(Success, input.Init(reinterpret_cast<const uint8_t*>(""), 0));
    Reader reader(input);
    bool value = true;
    ASSERT_EQ(Success, OptionalBoolean(reader, value)) <<
      "Should accept another valid encoding of an omitted OPTIONAL BOOLEAN";
    ASSERT_FALSE(value);
    ASSERT_TRUE(reader.AtEnd());
  }
}

TEST_F(pkixder_universal_types_tests, OptionalBooleanInvalidEncodings)
{
  const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_FALSE[] = {
    0x01,                       // BOOLEAN
    0x01,                       // length
    0x00                        // false
  };

  {
    Input input(DER_OPTIONAL_BOOLEAN_PRESENT_FALSE);
    Reader reader(input);
    bool value = true;
    ASSERT_EQ(Success, OptionalBoolean(reader, value)) <<
      "Should accept an invalid, default-value encoding of OPTIONAL BOOLEAN";
    ASSERT_FALSE(value);
    ASSERT_TRUE(reader.AtEnd());
  }

  const uint8_t DER_OPTIONAL_BOOLEAN_PRESENT_42[] = {
    0x01,                       // BOOLEAN
    0x01,                       // length
    0x42                        // (invalid value for a BOOLEAN)
  };

  {
    Input input(DER_OPTIONAL_BOOLEAN_PRESENT_42);
    Reader reader(input);
    bool value;
    ASSERT_EQ(Result::ERROR_BAD_DER, OptionalBoolean(reader, value)) <<
      "Should reject an invalid-valued encoding of OPTIONAL BOOLEAN";
  }
}

TEST_F(pkixder_universal_types_tests, Enumerated)
{
  const uint8_t DER_ENUMERATED[] = {
    0x0a,                       // ENUMERATED
    0x01,                       // length
    0x42                        // value
  };
  Input input(DER_ENUMERATED);
  Reader reader(input);

  uint8_t value = 0;
  ASSERT_EQ(Success, Enumerated(reader, value));
  ASSERT_EQ(0x42, value);
}

TEST_F(pkixder_universal_types_tests, EnumeratedNotShortestPossibleDER)
{
  const uint8_t DER_ENUMERATED[] = {
    0x0a,                       // ENUMERATED
    0x02,                       // length
    0x00, 0x01                  // value
  };
  Input input(DER_ENUMERATED);
  Reader reader(input);

  uint8_t value = 0;
  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
}

TEST_F(pkixder_universal_types_tests, EnumeratedOutOfAcceptedRange)
{
  // Although this is a valid ENUMERATED value according to ASN.1, we
  // intentionally don't support these large values because there are no
  // ENUMERATED values in X.509 certs or OCSP this large, and we're trying to
  // keep the parser simple and fast.
  const uint8_t DER_ENUMERATED_INVALID_LENGTH[] = {
    0x0a,                       // ENUMERATED
    0x02,                       // length
    0x12, 0x34                  // value
  };
  Input input(DER_ENUMERATED_INVALID_LENGTH);
  Reader reader(input);

  uint8_t value = 0;
  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
}

TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
{
  const uint8_t DER_ENUMERATED_INVALID_ZERO_LENGTH[] = {
    0x0a,                       // ENUMERATED
    0x00                        // length
  };
  Input input(DER_ENUMERATED_INVALID_ZERO_LENGTH);
  Reader reader(input);

  uint8_t value = 0;
  ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING, Enumerated(reader, value));
}

////////////////////////////////////////
// GeneralizedTime and TimeChoice
//
// From RFC 5280 section 4.1.2.5.2
//
//   For the purposes of this profile, GeneralizedTime values MUST be
//   expressed in Greenwich Mean Time (Zulu) and MUST include seconds
//   (i.e., times are YYYYMMDDHHMMSSZ), even where the number of seconds
//   is zero.  GeneralizedTime values MUST NOT include fractional seconds.
//
// And from from RFC 6960 (OCSP) section 4.2.2.1:
//
//   Responses can contain four times -- thisUpdate, nextUpdate,
//   producedAt, and revocationTime.  The semantics of these fields are
//   defined in Section 2.4.  The format for GeneralizedTime is as
//   specified in Section 4.1.2.5.2 of [RFC5280].
//
// So while we can could accept other ASN1 (ITU-T X.680) encodings for
// GeneralizedTime we should not accept them, and breaking reading of these
// other encodings is actually encouraged.

// e.g. TWO_CHARS(53) => '5', '3'
#define TWO_CHARS(t) \
  static_cast<uint8_t>('0' + (static_cast<uint8_t>(t) / 10u)), \
  static_cast<uint8_t>('0' + (static_cast<uint8_t>(t) % 10u))

// Calls TimeChoice on the UTCTime variant of the given generalized time.
template <uint16_t LENGTH>
Result
TimeChoiceForEquivalentUTCTime(const uint8_t (&generalizedTimeDER)[LENGTH],
                               /*out*/ Time& value)
{
  static_assert(LENGTH >= 4,
                "TimeChoiceForEquivalentUTCTime input too small");
  uint8_t utcTimeDER[LENGTH - 2];
  utcTimeDER[0] = 0x17; // tag UTCTime
  utcTimeDER[1] = LENGTH - 1/*tag*/ - 1/*value*/ - 2/*century*/;
  // Copy the value except for the first two digits of the year
  for (size_t i = 2; i < LENGTH - 2; ++i) {
    utcTimeDER[i] = generalizedTimeDER[i + 2];
  }

  Input input(utcTimeDER);
  Reader reader(input);
  return TimeChoice(reader, value);
}

template <uint16_t LENGTH>
void
ExpectGoodTime(Time expectedValue,
               const uint8_t (&generalizedTimeDER)[LENGTH])
{
  // GeneralizedTime
  {
    Input input(generalizedTimeDER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Success, GeneralizedTime(reader, value));
    EXPECT_EQ(expectedValue, value);
  }

  // TimeChoice: GeneralizedTime
  {
    Input input(generalizedTimeDER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Success, TimeChoice(reader, value));
    EXPECT_EQ(expectedValue, value);
  }

  // TimeChoice: UTCTime
  {
    Time value(Time::uninitialized);
    ASSERT_EQ(Success,
              TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
    EXPECT_EQ(expectedValue, value);
  }
}

template <uint16_t LENGTH>
void
ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
{
  // GeneralizedTime
  {
    Input input(generalizedTimeDER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, GeneralizedTime(reader, value));
  }

  // TimeChoice: GeneralizedTime
  {
    Input input(generalizedTimeDER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, TimeChoice(reader, value));
  }

  // TimeChoice: UTCTime
  {
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME,
              TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
  }
}

// Control value: a valid time
TEST_F(pkixder_universal_types_tests, ValidControl)
{
  const uint8_t GT_DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', 'Z'
  };
  ExpectGoodTime(YMDHMS(1991, 5, 6, 16, 45, 40), GT_DER);
}

TEST_F(pkixder_universal_types_tests, TimeTimeZoneOffset)
{
  const uint8_t DER_GENERALIZED_TIME_OFFSET[] = {
    0x18,                           // Generalized Time
    19,                             // Length = 19
    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0', '-',
    '0', '7', '0', '0'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_OFFSET);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidZeroLength)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH[] = {
    0x18,                           // GeneralizedTime
    0x00                            // Length = 0
  };

  Time value(Time::uninitialized);

  // GeneralizedTime
  Input gtBuf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
  Reader gt(gtBuf);
  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, GeneralizedTime(gt, value));

  // TimeChoice: GeneralizedTime
  Input tc_gt_buf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
  Reader tc_gt(tc_gt_buf);
  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, TimeChoice(tc_gt, value));

  // TimeChoice: UTCTime
  const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = {
    0x17, // UTCTime
    0x00  // Length = 0
  };
  Input tc_utc_buf(DER_UTCTIME_INVALID_ZERO_LENGTH);
  Reader tc_utc(tc_utc_buf);
  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, TimeChoice(tc_utc, value));
}

// A non zulu time should fail
TEST_F(pkixder_universal_types_tests, TimeInvalidLocal)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_LOCAL[] = {
    0x18,                           // Generalized Time
    14,                             // Length = 14
    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', '4', '0'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_LOCAL);
}

// A time missing seconds and zulu should fail
TEST_F(pkixder_universal_types_tests, TimeInvalidTruncated)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_TRUNCATED[] = {
    0x18,                           // Generalized Time
    12,                             // Length = 12
    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_TRUNCATED);
}

TEST_F(pkixder_universal_types_tests, TimeNoSeconds)
{
  const uint8_t DER_GENERALIZED_TIME_NO_SECONDS[] = {
    0x18,                           // Generalized Time
    13,                             // Length = 13
    '1', '9', '9', '1', '0', '5', '0', '6', '1', '6', '4', '5', 'Z'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_NO_SECONDS);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidPrefixedYear)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_PREFIXED_YEAR[] = {
    0x18,                           // Generalized Time
    16,                             // Length = 16
    ' ', '1', '9', '9', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', 'Z'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_PREFIXED_YEAR);
}

TEST_F(pkixder_universal_types_tests, TimeTooManyDigits)
{
  const uint8_t DER_GENERALIZED_TIME_TOO_MANY_DIGITS[] = {
    0x18,                           // Generalized Time
    16,                             // Length = 16
    '1', '1', '1', '1', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', 'Z'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_TOO_MANY_DIGITS);
}

// In order to ensure we we don't run into any trouble with conversions to and
// from time_t we only accept times from 1970 onwards.
TEST_F(pkixder_universal_types_tests, GeneralizedTimeYearValidRange)
{
  // Note that by using the last second of the last day of the year, we're also
  // effectively testing all the accumulated conversions from Gregorian to to
  // Julian time, including in particular the effects of leap years.

  for (uint16_t i = 1970; i <= 9999; ++i) {
    const uint8_t DER[] = {
      0x18,                           // Generalized Time
      15,                             // Length = 15
      TWO_CHARS(i / 100), TWO_CHARS(i % 100), // YYYY
      '1', '2', '3', '1', // 12-31
      '2', '3', '5', '9', '5', '9', 'Z' // 23:59:59Z
    };

    Time expectedValue = YMDHMS(i, 12, 31, 23, 59, 59);

    // We have to test GeneralizedTime separately from UTCTime instead of using
    // ExpectGooDtime because the range of UTCTime is less than the range of
    // GeneralizedTime.

    // GeneralizedTime
    {
      Input input(DER);
      Reader reader(input);
      Time value(Time::uninitialized);
      ASSERT_EQ(Success, GeneralizedTime(reader, value));
      EXPECT_EQ(expectedValue, value);
    }

    // TimeChoice: GeneralizedTime
    {
      Input input(DER);
      Reader reader(input);
      Time value(Time::uninitialized);
      ASSERT_EQ(Success, TimeChoice(reader, value));
      EXPECT_EQ(expectedValue, value);
    }

    // TimeChoice: UTCTime, which is limited to years less than 2049.
    if (i <= 2049) {
      Time value(Time::uninitialized);
      ASSERT_EQ(Success, TimeChoiceForEquivalentUTCTime(DER, value));
      EXPECT_EQ(expectedValue, value);
    }
  }
}

// In order to ensure we we don't run into any trouble with conversions to and
// from time_t we only accept times from 1970 onwards.
TEST_F(pkixder_universal_types_tests, TimeYearInvalid1969)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '6', '9', '1', '2', '3', '1', // !!!1969!!!-12-31
    '2', '3', '5', '9', '5', '9', 'Z' // 23:59:59Z
  };
  ExpectBadTime(DER);
}

static const uint8_t DAYS_IN_MONTH[] = {
  0,  // unused
  31, // January
  28, // February (leap years tested separately)
  31, // March
  30, // April
  31, // May
  30, // Jun
  31, // July
  31, // August
  30, // September
  31, // October
  30, // November
  31, // December
};

TEST_F(pkixder_universal_types_tests, TimeMonthDaysValidRange)
{
  for (uint16_t month = 1; month <= 12; ++month) {
    for (uint8_t day = 1; day <= DAYS_IN_MONTH[month]; ++day) {
      const uint8_t DER[] = {
        0x18,                           // Generalized Time
        15,                             // Length = 15
        '2', '0', '1', '5', TWO_CHARS(month), TWO_CHARS(day), // (2015-mm-dd)
        '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
      };
      ExpectGoodTime(YMDHMS(2015, month, day, 16, 45, 40), DER);
    }
  }
}

TEST_F(pkixder_universal_types_tests, TimeMonthInvalid0)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '5', '0', '0', '1', '5', // 2015-!!!00!!!-15
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeMonthInvalid13)
{
  const uint8_t DER_GENERALIZED_TIME_13TH_MONTH[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '9', '1', //YYYY (1991)
    '1', '3', //MM 13th month of the year
    '0', '6', '1', '6', '4', '5', '4', '0', 'Z'
  };
  ExpectBadTime(DER_GENERALIZED_TIME_13TH_MONTH);
}

TEST_F(pkixder_universal_types_tests, TimeDayInvalid0)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '5', '0', '1', '0', '0', // 2015-01-!!!00!!!
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeMonthDayInvalidPastEndOfMonth)
{
  for (int16_t month = 1; month <= 12; ++month) {
    const uint8_t DER[] = {
      0x18,                           // Generalized Time
      15,                             // Length = 15
      '1', '9', '9', '1', // YYYY 1991
      TWO_CHARS(month), // MM
      TWO_CHARS(1 + (month == 2 ? 29 : DAYS_IN_MONTH[month])), // !!!DD!!!
      '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
    };
    ExpectBadTime(DER);
  }
}

TEST_F(pkixder_universal_types_tests, TimeMonthFebLeapYear2016)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '6', '0', '2', '2', '9', // 2016-02-29
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };
  ExpectGoodTime(YMDHMS(2016, 2, 29, 16, 45, 40), DER);
}

TEST_F(pkixder_universal_types_tests, TimeMonthFebLeapYear2000)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '0', '0', '0', '2', '2', '9', // 2000-02-29
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };
  ExpectGoodTime(YMDHMS(2000, 2, 29, 16, 45, 40), DER);
}

TEST_F(pkixder_universal_types_tests, TimeMonthFebLeapYear2400)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '4', '0', '0', '0', '2', '2', '9', // 2400-02-29
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };

  // We don't use ExpectGoodTime here because UTCTime can't represent 2400.

  Time expectedValue = YMDHMS(2400, 2, 29, 16, 45, 40);

  // GeneralizedTime
  {
    Input input(DER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Success, GeneralizedTime(reader, value));
    EXPECT_EQ(expectedValue, value);
  }

  // TimeChoice: GeneralizedTime
  {
    Input input(DER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Success, TimeChoice(reader, value));
    EXPECT_EQ(expectedValue, value);
  }
}

TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2014)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '4', '0', '2', '2', '9', // 2014-02-29
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2100)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '1', '0', '0', '0', '2', '2', '9', // 2100-02-29
    '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
  };

  // We don't use ExpectBadTime here because UTCTime can't represent 2100.

  // GeneralizedTime
  {
    Input input(DER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, GeneralizedTime(reader, value));
  }

  // TimeChoice: GeneralizedTime
  {
    Input input(DER);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, TimeChoice(reader, value));
  }
}

TEST_F(pkixder_universal_types_tests, TimeHoursValidRange)
{
  for (uint8_t i = 0; i <= 23; ++i) {
    const uint8_t DER[] = {
      0x18,                           // Generalized Time
      15,                             // Length = 15
      '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
      TWO_CHARS(i), '5', '9', '0', '1', 'Z' // HHMMSSZ (!!!!ii!!!!:59:01 Zulu)
    };
    ExpectGoodTime(YMDHMS(2012, 6, 30, i, 59, 1), DER);
  }
}

TEST_F(pkixder_universal_types_tests, TimeHoursInvalid_24_00_00)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '4', '0', '0', '0', '0', 'Z' // HHMMSSZ (!!24!!:00:00 Zulu)
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeMinutesValidRange)
{
  for (uint8_t i = 0; i <= 59; ++i) {
    const uint8_t DER[] = {
      0x18,                           // Generalized Time
      15,                             // Length = 15
      '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
      '2', '3', TWO_CHARS(i), '0', '1', 'Z' // HHMMSSZ (23:!!!!ii!!!!:01 Zulu)
    };
    ExpectGoodTime(YMDHMS(2012, 6, 30, 23, i, 1), DER);
  }
}

TEST_F(pkixder_universal_types_tests, TimeMinutesInvalid60)
{
  const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '3', '6', '0', '5', '9', 'Z' // HHMMSSZ (23:!!!60!!!:01 Zulu)
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeSecondsValidRange)
{
  for (uint8_t i = 0; i <= 59; ++i) {
    const uint8_t DER[] = {
      0x18,                           // Generalized Time
      15,                             // Length = 15
      '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
      '2', '3', '5', '9', TWO_CHARS(i), 'Z' // HHMMSSZ (23:59:!!!!ii!!!! Zulu)
    };
    ExpectGoodTime(YMDHMS(2012, 6, 30, 23, 59, i), DER);
  }
}

// No Leap Seconds (60)
TEST_F(pkixder_universal_types_tests, TimeSecondsInvalid60)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '3', '5', '9', '6', '0', 'Z' // HHMMSSZ (23:59:!!!!60!!!! Zulu)
  };
  ExpectBadTime(DER);
}

// No Leap Seconds (61)
TEST_F(pkixder_universal_types_tests, TimeSecondsInvalid61)
{
  static const uint8_t DER[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '3', '5', '9', '6', '1', 'Z' // HHMMSSZ (23:59:!!!!61!!!! Zulu)
  };
  ExpectBadTime(DER);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidZulu)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_ZULU[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '3', '5', '9', '5', '9', 'z' // HHMMSSZ (23:59:59 !!!z!!!) should be Z
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_ZULU);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidExtraData)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_EXTRA_DATA[] = {
    0x18,                           // Generalized Time
    16,                             // Length = 16
    '2', '0', '1', '2', '0', '6', '3', '0', // YYYYMMDD (2012-06-30)
    '2', '3', '5', '9', '5', '9', 'Z', // HHMMSSZ (23:59:59Z)
    0 // Extra null character
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_EXTRA_DATA);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidCenturyChar)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    'X', '9', '9', '1', '1', '2', '0', '6', // YYYYMMDD (X991-12-06)
    '1', '6', '4', '5', '4', '0', 'Z' // HHMMSSZ (16:45:40Z)
  };

  // We can't use ExpectBadTime here, because ExpectBadTime requires
  // consistent results for GeneralizedTime and UTCTime, but the results
  // for this input are different.

  // GeneralizedTime
  {
    Input input(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, GeneralizedTime(reader, value));
  }

  // TimeChoice: GeneralizedTime
  {
    Input input(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
    Reader reader(input);
    Time value(Time::uninitialized);
    ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, TimeChoice(reader, value));
  }

  // This test is not applicable to TimeChoice: UTCTime
}

TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_YEAR_CHAR[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '9', 'I', '0', '1', '0', '6', // YYYYMMDD (199I-12-06)
    '1', '6', '4', '5', '4', '0', 'Z' // HHMMSSZ (16:45:40Z)
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_YEAR_CHAR);
}

TEST_F(pkixder_universal_types_tests, GeneralizedTimeInvalidMonthChar)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_MONTH_CHAR[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '9', '1', '0', 'I', '0', '6', // YYYYMMDD (1991-0I-06)
    '1', '6', '4', '5', '4', '0', 'Z' // HHMMSSZ (16:45:40Z)
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_MONTH_CHAR);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidDayChar)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_DAY_CHAR[] = {
    0x18,                           // Generalized Time
    15,                             // Length = 15
    '1', '9', '9', '1', '0', '1', '0', 'S', // YYYYMMDD (1991-01-0S)
    '1', '6', '4', '5', '4', '0', 'Z' // HHMMSSZ (16:45:40Z)
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_DAY_CHAR);
}

TEST_F(pkixder_universal_types_tests, TimeInvalidFractionalSeconds)
{
  const uint8_t DER_GENERALIZED_TIME_INVALID_FRACTIONAL_SECONDS[] = {
    0x18,                           // Generalized Time
    17,                             // Length = 17
    '1', '9', '9', '1', '0', '1', '0', '1', // YYYYMMDD (1991-01-01)
    '1', '6', '4', '5', '4', '0', '.', '3', 'Z' // HHMMSS.FFF (16:45:40.3Z)
  };
  ExpectBadTime(DER_GENERALIZED_TIME_INVALID_FRACTIONAL_SECONDS);
}

struct IntegerTestParams
{
  ByteString encoded;
  struct PositiveIntegerParams
  {
    Result expectedResult;
    Input::size_type significantBytesIfValid;
  } positiveInteger;
  struct SmallNonnegativeIntegerParams
  {
    Result expectedResult;
    uint8_t valueIfValid;
  } smallNonnegativeInteger;
};

class pkixder_universal_types_tests_Integer
  : public ::testing::Test
  , public ::testing::WithParamInterface<IntegerTestParams>
{
};

::std::ostream& operator<<(::std::ostream& os, const IntegerTestParams&)
{
  return os << "TODO (bug 1318770)";
}

#define INVALID 0xFF

static const IntegerTestParams INTEGER_TEST_PARAMS[] =
{
  // Zero is encoded with one value byte of 0x00.
  { TLV(2, ByteString()),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x00"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Success, 0 } },

  // Positive single-byte values
  { TLV(2, "\x01"), { Success, 1 }, { Success, 1} },
  { TLV(2, "\x02"), { Success, 1 }, { Success, 2} },
  { TLV(2, "\x7e"), { Success, 1 }, { Success, 0x7e} },
  { TLV(2, "\x7f"), { Success, 1 }, { Success, 0x7f} },

  // Negative single-byte values
  { TLV(2, "\x80"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x81"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\xFE"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\xFF"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // Positive two-byte values not starting with 0x00
  { TLV(2, "\x7F\x00"),
    { Success, 2 },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x01\x00"),
    { Success, 2 },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x01\x02"),
    { Success, 2 },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // Negative two-byte values not starting with 0xFF
  { TLV(2, "\x80\x00"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x80\x7F"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x80\x80"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x80\xFF"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // The leading zero is necessary.
  { TLV(2, "\x00\x80"),
    { Success, 1},
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x00\x81"),
    { Success, 1},
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x00\xFF"),
    { Success, 1},
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // The leading zero is unnecessary.
  { TLV(2, "\x00\x01"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\x00\x7F"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // The leading 0xFF is necessary.
  { TLV(2, "\xFF\x00"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\xFF\x7F"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // The leading 0xFF is unnecessary.
  { TLV(2, "\xFF\x80"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2, "\xFF\xFF"),
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },

  // Truncated values
  { TLV(2, 1, ByteString(/*missing value*/)),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 3, "\x11\x22" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 4, "\x11\x22" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 2, "\x00" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 2, "\xFF" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 3, "\x00\x80" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },
  { TLV(2, 3, "\xFF\x00" /*truncated*/),
    { Result::ERROR_BAD_DER, INVALID },
    { Result::ERROR_BAD_DER, INVALID } },

  // Misc. larger values
  { TLV(2, 4, "\x11\x22\x33\x44"),
    { Success, 4 },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
  { TLV(2,
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"

        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"),
    { Success, 256 },
    { Result::ERROR_INVALID_INTEGER_ENCODING, INVALID } },
};

TEST_P(pkixder_universal_types_tests_Integer, Integer)
{
  const IntegerTestParams& params(GetParam());
  Input input;
  ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                params.encoded.length()));
  Reader reader(input);
  Result expectedResult = params.smallNonnegativeInteger.expectedResult;
  uint8_t value;
  ASSERT_EQ(expectedResult, der::Integer(reader, value));
  if (expectedResult == Success) {
    ASSERT_EQ(params.smallNonnegativeInteger.valueIfValid, value);
    ASSERT_TRUE(reader.AtEnd());
  }
}

TEST_P(pkixder_universal_types_tests_Integer,
       PositiveInteger_without_significantBytes)
{
  const IntegerTestParams& params(GetParam());
  Input input;
  ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                params.encoded.length()));
  Reader reader(input);
  Result expectedResult = params.positiveInteger.expectedResult;
  Input value;
  ASSERT_EQ(expectedResult, der::PositiveInteger(reader, value));
  if (expectedResult == Success) {
    Reader anotherReader(input);
    Input expectedValue;
    ASSERT_EQ(Success, ExpectTagAndGetValue(anotherReader,
                                            der::INTEGER, expectedValue));
    ASSERT_TRUE(InputsAreEqual(expectedValue, value));
    ASSERT_TRUE(reader.AtEnd());
  }
}

TEST_P(pkixder_universal_types_tests_Integer,
       PositiveInteger_with_significantBytes)
{
  const IntegerTestParams& params(GetParam());
  Input input;
  ASSERT_EQ(Success, input.Init(params.encoded.data(),
                                params.encoded.length()));
  Reader reader(input);
  Result expectedResult = params.positiveInteger.expectedResult;
  Input value;
  Input::size_type significantBytes = INVALID;
  ASSERT_EQ(expectedResult, der::PositiveInteger(reader, value,
                                                 &significantBytes));
  if (expectedResult == Success) {
    ASSERT_NE(INVALID, params.positiveInteger.significantBytesIfValid);
    ASSERT_EQ(params.positiveInteger.significantBytesIfValid,
              significantBytes);

    Reader anotherReader(input);
    Input expectedValue;
    ASSERT_EQ(Success, ExpectTagAndGetValue(anotherReader,
                                            der::INTEGER, expectedValue));
    ASSERT_TRUE(InputsAreEqual(expectedValue, value));
    ASSERT_TRUE(reader.AtEnd());
  }
}

#undef INVALID

INSTANTIATE_TEST_CASE_P(pkixder_universal_types_tests_Integer,
                        pkixder_universal_types_tests_Integer,
                        testing::ValuesIn(INTEGER_TEST_PARAMS));

TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefault)
{
  // The input is a BOOLEAN and not INTEGER for the input so we'll not parse
  // anything and instead use the default value.
  Input input(DER_BOOLEAN_TRUE);
  Reader reader(input);

  long value = 1;
  ASSERT_EQ(Success, OptionalInteger(reader, -1, value));
  ASSERT_EQ(-1, value);
  bool boolValue;
  ASSERT_EQ(Success, Boolean(reader, boolValue));
}

TEST_F(pkixder_universal_types_tests, OptionalIntegerUnsupportedDefault)
{
  // The same as the previous test, except with an unsupported default value
  // passed in.
  Input input(DER_BOOLEAN_TRUE);
  Reader reader(input);

  long value;
  ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, OptionalInteger(reader, 0, value));
}

TEST_F(pkixder_universal_types_tests, OptionalIntegerSupportedDefaultAtEnd)
{
  static const uint8_t dummy = 1;
  Input input;
  ASSERT_EQ(Success, input.Init(&dummy, 0));
  Reader reader(input);

  long value = 1;
  ASSERT_EQ(Success, OptionalInteger(reader, -1, value));
  ASSERT_EQ(-1, value);
}

TEST_F(pkixder_universal_types_tests, OptionalIntegerNonDefaultValue)
{
  static const uint8_t DER[] = {
    0x02, // INTEGER
    0x01, // length
    0x00
  };
  Input input(DER);
  Reader reader(input);

  long value = 2;
  ASSERT_EQ(Success, OptionalInteger(reader, -1, value));
  ASSERT_EQ(0, value);
  ASSERT_TRUE(reader.AtEnd());
}

TEST_F(pkixder_universal_types_tests, Null)
{
  const uint8_t DER_NUL[] = {
    0x05,
    0x00
  };
  Input input(DER_NUL);
  Reader reader(input);

  ASSERT_EQ(Success, Null(reader));
}

TEST_F(pkixder_universal_types_tests, NullWithBadLength)
{
  const uint8_t DER_NULL_BAD_LENGTH[] = {
    0x05,
    0x01,
    0x00
  };
  Input input(DER_NULL_BAD_LENGTH);
  Reader reader(input);

  ASSERT_EQ(Result::ERROR_BAD_DER, Null(reader));
}

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

  const uint8_t expectedOID[] = {
    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
  };

  ASSERT_EQ(Success, OID(reader, expectedOID));
}

TEST_F(pkixder_universal_types_tests, SkipOptionalImplicitPrimitiveTag)
{
  const uint8_t DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1[] = {
    0x81,
    0x04,
      0x00,
      0x0A,
      0x0B,
      0x0C,
  };
  Input input(DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1);
  Reader reader(input);

  ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 1));
  ASSERT_TRUE(reader.AtEnd());
}

TEST_F(pkixder_universal_types_tests, SkipOptionalImplicitPrimitiveTagMismatch)
{
  const uint8_t DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1[] = {
    0x81,
    0x04,
      0x00,
      0x0A,
      0x0B,
      0x0C,
  };
  Input input(DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1);
  Reader reader(input);

  ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 2));
  ASSERT_FALSE(reader.AtEnd());
}

TEST_F(pkixder_universal_types_tests, NoSkipOptionalImplicitConstructedTag)
{
  const uint8_t DER_IMPLICIT_SEQUENCE_WITH_CLASS_NUMBER_1[] = {
    0xA1,
    0x03,
      0x05,
      0x01,
      0x00,
  };
  Input input(DER_IMPLICIT_SEQUENCE_WITH_CLASS_NUMBER_1);
  Reader reader(input);

  ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 1));
  ASSERT_FALSE(reader.AtEnd());
}
