| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| |
| #include "base/macros.h" |
| #include "net/cert/internal/extended_key_usage.h" |
| #include "net/der/input.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| 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, ServerAuth())); |
| EXPECT_TRUE(HasEKU(ekus, ClientAuth())); |
| } |
| |
| // 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(ServerAuth(), 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, ServerAuth())); |
| |
| 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 |