// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>

#include "extended_key_usage.h"
#include "input.h"
#include <gtest/gtest.h>

namespace bssl {

namespace {

// Helper method to check if an EKU is present in a std::vector of EKUs.
bool HasEKU(const std::vector<der::Input>& list, const der::Input& eku) {
  for (const auto& oid : list) {
    if (oid == eku)
      return true;
  }
  return false;
}

// Check that we can read multiple EKUs from an extension.
TEST(ExtendedKeyUsageTest, ParseEKUExtension) {
  // clang-format off
  const uint8_t raw_extension_value[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02  // 1.3.6.1.5.5.7.3.2
      // end of SEQUENCE
  };
  // clang-format on
  der::Input extension_value(raw_extension_value);

  std::vector<der::Input> ekus;
  EXPECT_TRUE(ParseEKUExtension(extension_value, &ekus));

  EXPECT_EQ(2u, ekus.size());
  EXPECT_TRUE(HasEKU(ekus, der::Input(kServerAuth)));
  EXPECT_TRUE(HasEKU(ekus, der::Input(kClientAuth)));
}

// Check that an extension with the same OID present multiple times doesn't
// cause an error.
TEST(ExtendedKeyUsageTest, RepeatedOid) {
  // clang-format off
  const uint8_t extension_bytes[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01  // 1.3.6.1.5.5.7.3.1
  };
  // clang-format on
  der::Input extension(extension_bytes);

  std::vector<der::Input> ekus;
  EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
  EXPECT_EQ(2u, ekus.size());
  for (const auto& eku : ekus) {
    EXPECT_EQ(der::Input(kServerAuth), eku);
  }
}

// Check that parsing an EKU extension which contains a private OID doesn't
// cause an error.
TEST(ExtendedKeyUsageTest, ParseEKUExtensionGracefullyHandlesPrivateOids) {
  // clang-format off
  const uint8_t extension_bytes[] = {
    0x30, 0x13,  // SEQUENCE (19 bytes)
    0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
    0x06, 0x07,  // OBJECT IDENTIFIER (7 bytes)
    0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79  // 1.3.6.1.4.1.11129
  };
  // clang-format on
  der::Input extension(extension_bytes);

  std::vector<der::Input> ekus;
  EXPECT_TRUE(ParseEKUExtension(extension, &ekus));
  EXPECT_EQ(2u, ekus.size());
  EXPECT_TRUE(HasEKU(ekus, der::Input(kServerAuth)));

  const uint8_t google_oid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79};
  der::Input google(google_oid);
  EXPECT_TRUE(HasEKU(ekus, google));
}

// Test a variety of bad inputs.

// If the extension value has data following the sequence of oids, parsing it
// should fail.
TEST(ExtendedKeyUsageTest, ExtraData) {
  // clang-format off
  const uint8_t extra_data[] = {
      0x30, 0x14,  // SEQUENCE (20 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,  // 1.3.6.1.5.5.7.3.2
      // end of SEQUENCE
      0x02, 0x01,  // INTEGER (1 byte)
      0x01  // 1
  };
  // clang-format on

  std::vector<der::Input> ekus;
  EXPECT_FALSE(ParseEKUExtension(der::Input(extra_data), &ekus));
}

// Check that ParseEKUExtension only accepts a sequence containing only oids.
// This test case has an integer in the sequence (which should fail). A key
// difference between this test case and ExtendedKeyUsageTest.ExtraData is where
// the sequence ends - in this test case the integer is still part of the
// sequence, while in ExtendedKeyUsageTest.ExtraData the integer is after the
// sequence.
TEST(ExtendedKeyUsageTest, NotAnOid) {
  // clang-format off
  const uint8_t not_an_oid[] = {
      0x30, 0x0d,  // SEQUENCE (13 bytes)
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,  // 1.3.6.1.5.5.7.3.1
      0x02, 0x01,  // INTEGER (1 byte)
      0x01  // 1
      // end of SEQUENCE
  };
  // clang-format on

  std::vector<der::Input> ekus;
  EXPECT_FALSE(ParseEKUExtension(der::Input(not_an_oid), &ekus));
}

// Checks that the list of oids passed to ParseEKUExtension are in a sequence,
// instead of one or more oid tag-length-values concatenated together.
TEST(ExtendedKeyUsageTest, NotASequence) {
  // clang-format off
  const uint8_t not_a_sequence[] = {
      0x06, 0x08,  // OBJECT IDENTIFIER (8 bytes)
      0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01  // 1.3.6.1.5.5.7.3.1
  };
  // clang-format on

  std::vector<der::Input> ekus;
  EXPECT_FALSE(ParseEKUExtension(der::Input(not_a_sequence), &ekus));
}

// A sequence passed into ParseEKUExtension must have at least one oid in it.
TEST(ExtendedKeyUsageTest, EmptySequence) {
  const uint8_t empty_sequence[] = {0x30, 0x00};  // SEQUENCE (0 bytes)

  std::vector<der::Input> ekus;
  EXPECT_FALSE(ParseEKUExtension(der::Input(empty_sequence), &ekus));
}

// The extension value must not be empty.
TEST(ExtendedKeyUsageTest, EmptyExtension) {
  std::vector<der::Input> ekus;
  EXPECT_FALSE(ParseEKUExtension(der::Input(), &ekus));
}

}  // namespace

}  // namespace net
