Chromium Code Reviews| Index: net/tools/transport_security_state_generator/trie/trie_writer_unittest.cc |
| diff --git a/net/tools/transport_security_state_generator/trie/trie_writer_unittest.cc b/net/tools/transport_security_state_generator/trie/trie_writer_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5f6f71667a890a4b80021b616065d10607b37797 |
| --- /dev/null |
| +++ b/net/tools/transport_security_state_generator/trie/trie_writer_unittest.cc |
| @@ -0,0 +1,396 @@ |
| +// Copyright 2017 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 <memory> |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "net/tools/transport_security_state_generator/bit_writer.h" |
| +#include "net/tools/transport_security_state_generator/huffman/huffman_builder.h" |
| +#include "net/tools/transport_security_state_generator/transport_security_state_entry.h" |
| +#include "net/tools/transport_security_state_generator/trie/trie_bit_buffer.h" |
| +#include "net/tools/transport_security_state_generator/trie/trie_writer.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace net { |
| + |
| +namespace transport_security_state { |
| + |
| +namespace { |
| + |
| +std::vector<uint8_t> StringToVector(const std::string& input) { |
| + std::vector<uint8_t> output; |
| + for (const auto& character : input) { |
| + output.push_back(character); |
| + } |
| + return output; |
| +} |
| + |
| +void appendEntry(const std::string& hostname, |
| + TransportSecurityStateEntries* entries) { |
| + std::unique_ptr<TransportSecurityStateEntry> entry = |
| + base::MakeUnique<TransportSecurityStateEntry>(); |
| + entry->hostname = hostname; |
| + entry->include_subdomains = true; |
| + entry->force_https = true; |
| + |
| + entries->push_back(std::move(entry)); |
| +} |
| + |
| +} // namespace |
| + |
| +class TrieWriterTest : public testing::Test { |
| + protected: |
| + void SetUp() override { |
| + // Create a Huffman representation that is 8bit ASCII. |
| + for (uint8_t i = 0; i <= 127; ++i) { |
| + HuffmanRepresentation representation; |
| + representation.bits = i; |
| + representation.number_of_bits = 8; |
| + huffman_table_[i] = representation; |
| + } |
| + |
| + domain_ids_map_["BADSSL_COM"] = 1; |
| + domain_ids_map_["EXAMPLE_ORG"] = 2; |
| + domain_ids_map_["OVERFLOW_TLD"] = 512; |
| + |
| + report_uris_map_["https://report.badssl.com/upload"] = 1; |
| + report_uris_map_["https://report.example.org/upload"] = 2; |
| + report_uris_map_["https://report.overflow.tld/upload"] = 16; |
| + |
| + pinsets_map_["pinset_a"] = 1; |
| + pinsets_map_["pinset_b"] = 2; |
| + pinsets_map_["pinset_overflow"] = 16; |
| + } |
| + |
| + std::unique_ptr<TrieWriter> GetTrieWriter() { |
| + return base::MakeUnique<TrieWriter>(huffman_table_, domain_ids_map_, |
| + report_uris_map_, report_uris_map_, |
| + pinsets_map_, nullptr); |
| + } |
| + |
| + bool TestWriteEntry(TrieWriter* trie_writer, |
| + const TransportSecurityStateEntry& entry, |
| + std::initializer_list<uint8_t> expected) { |
| + TrieBitBuffer buffer; |
| + if (!trie_writer->WriteEntry(&entry, &buffer)) { |
| + return false; |
| + } |
| + |
| + BitWriter bit_writer; |
| + buffer.WriteToBitWriter(&bit_writer); |
| + bit_writer.Flush(); |
| + |
| + EXPECT_EQ(std::vector<uint8_t>(expected), bit_writer.bytes()); |
| + return true; |
| + } |
| + |
| + private: |
| + HuffmanRepresentationTable huffman_table_; |
| + NameIDMap domain_ids_map_; |
| + NameIDMap report_uris_map_; |
| + NameIDMap pinsets_map_; |
| +}; |
| + |
| +TEST_F(TrieWriterTest, SingleEntryTrie) { |
| + TransportSecurityStateEntries entries; |
| + appendEntry("hsts.badssl.com", &entries); |
| + |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + |
| + uint32_t root_position; |
| + ASSERT_TRUE(trie_writer->WriteEntries(entries, &root_position)); |
| + |
| + EXPECT_EQ(0U, root_position); |
| + EXPECT_EQ(157U, trie_writer->position()); |
| + |
| + trie_writer->Flush(); |
| + EXPECT_EQ(20U, trie_writer->bytes().size()); |
| + |
| + // The expected bits are, starting at |root_position|: |
| + // 0xFF 0XFE: 15 ones followed by a zero indicating a prefix of 15 characters. |
| + // |
| + // The prefix follows immediately (8 bits per character): |
| + // 0x6D 0x6F 0x63 0x2E 0x6C 0x73 0x73 0x64 0x61 0x62 0x2E 0x73 0x74 0x73 0x68 |
| + // and decodes to "moc.lssdab.stsh". |
| + // |
| + // The prefix is followed by 0x0 (kTerminalValue) which indidicates the end |
| + // of the prefix. What follows is the encoded entry for "hsts.badssl.com": |
| + // 0xC3 0xF8. The entry (first 5 bits) is followed be 0x7F (kEndOfTabelValue). |
| + // The remaining bits are padding. |
| + EXPECT_THAT( |
| + trie_writer->bytes(), |
| + testing::ElementsAreArray({0xFF, 0xFE, 0x6D, 0x6F, 0x63, 0x2E, 0x6C, |
| + 0x73, 0x73, 0x64, 0x61, 0x62, 0x2E, 0x73, |
| + 0x74, 0x73, 0x68, 0x0, 0xC3, 0xF8})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, MultiPrefixTrie) { |
| + TransportSecurityStateEntries entries; |
| + appendEntry("hsts.badssl.com", &entries); |
| + appendEntry("expect-ct.badssl.com", &entries); |
| + appendEntry("expect-staple.badssl.com", &entries); |
| + |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + |
| + uint32_t root_position; |
| + ASSERT_TRUE(trie_writer->WriteEntries(entries, &root_position)); |
| + |
| + EXPECT_EQ(273U, root_position); |
| + EXPECT_EQ(440U, trie_writer->position()); |
| + |
| + trie_writer->Flush(); |
| + EXPECT_EQ(56U, trie_writer->bytes().size()); |
| + |
| + EXPECT_THAT(trie_writer->bytes(), |
|
martijnc
2017/02/08 20:58:21
I've verified that these expectations are correct
Ryan Sleevi
2017/02/08 21:45:29
That's a really tough question. You're effectively
|
| + testing::ElementsAreArray( |
| + {0xff, 0xf3, 0x63, 0x83, 0xb, 0xa3, 0x99, 0x6b, 0xa3, 0x1b, |
| + 0x2b, 0x83, 0xc3, 0x28, 0x6, 0x1f, 0xf9, 0xd1, 0xcd, 0xa0, |
| + 0x3, 0xf, 0xff, 0xe6, 0x32, 0xd7, 0x46, 0x36, 0x57, 0x7, |
| + 0x86, 0x50, 0xc, 0x3f, 0xff, 0xf3, 0x6b, 0x7b, 0x19, 0x73, |
| + 0x63, 0x9b, 0x9b, 0x23, 0xb, 0x11, 0x73, 0x2a, 0x62, 0x2e, |
| + 0x70, 0x82, 0x74, 0x31, 0x7f, 0x0})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, MultiMixedPrefixTrie) { |
| + TransportSecurityStateEntries entries; |
| + appendEntry("hsts.badssl.com", &entries); |
| + appendEntry("expect-ct.badssl.com", &entries); |
| + appendEntry("example.org", &entries); |
| + appendEntry("badssl.com", &entries); |
| + |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + |
| + uint32_t root_position; |
| + ASSERT_TRUE(trie_writer->WriteEntries(entries, &root_position)); |
| + |
| + EXPECT_EQ(423U, root_position); |
| + EXPECT_EQ(476U, trie_writer->position()); |
| + |
| + trie_writer->Flush(); |
| + EXPECT_EQ(60U, trie_writer->bytes().size()); |
| + |
| + EXPECT_THAT( |
| + trie_writer->bytes(), |
| + testing::ElementsAreArray( |
| + {0xff, 0xce, 0x4d, 0xe5, 0xcc, 0xad, 0x8e, 0xd, 0xac, 0x2f, |
| + 0xc, 0xa0, 0x18, 0x7f, 0xe7, 0x47, 0x36, 0x80, 0xc, 0x3f, |
| + 0xff, 0x98, 0xcb, 0x5d, 0x18, 0xd9, 0x5c, 0x1e, 0x19, 0x40, |
| + 0x30, 0xfe, 0x73, 0x44, 0x7b, 0xa1, 0x8b, 0xff, 0xfc, 0xde, |
| + 0xc6, 0x5c, 0xd8, 0xe6, 0xe6, 0xc8, 0xc2, 0xc4, 0x1, 0x82, |
| + 0xe3, 0x5c, 0xfe, 0x67, 0x4e, 0x9d, 0xb6, 0x32, 0xd7, 0xf0})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, WriteEntryHSTS) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + TransportSecurityStateEntry entry; |
| + entry.hostname = "example.org"; |
| + |
| + entry.force_https = true; |
| + entry.include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xC0})); |
| + |
| + entry.force_https = false; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x80})); |
| + |
| + entry.include_subdomains = false; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x0})); |
| + |
| + entry.force_https = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x40})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, WriteEntryHPKP) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + TransportSecurityStateEntry entry; |
| + entry.hostname = "example.org"; |
| + |
| + entry.pinset = "pinset_a"; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x22, 0x2, 0x0})); |
| + |
| + entry.hpkp_include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x22, 0x2, 0x80})); |
| + |
| + // Should ignore |entry.hpkp_include_subdomains|. |
| + entry.include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xA2, 0x2, 0x0})); |
| + |
| + // Should result in an "empty" HPKP state. |
| + entry.pinset = ""; |
| + entry.include_subdomains = false; |
| + entry.hpkp_include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x0})); |
| + |
| + // Unknown pinset. |
| + entry.pinset = "not_found"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| + |
| + // Overflow for the pinset ID. |
| + entry.pinset = "pinset_overflow"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| + |
| + // Set a valid pinset for the other tests. |
| + entry.pinset = "pinset_a"; |
| + |
| + // Overflow for the domain ID. |
| + entry.hostname = "overflow.tld"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| + |
| + // Unknown domain ID. |
| + entry.hostname = "unknown.tld"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, WriteEntryExpectStaple) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + TransportSecurityStateEntry entry; |
| + entry.hostname = "example.org"; |
| + |
| + entry.expect_staple = true; |
| + entry.expect_staple_report_uri = "https://report.example.org/upload"; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x08, 0x80})); |
| + |
| + entry.expect_staple_include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x0C, 0x80})); |
| + |
| + // This should result in an "empty" expect-staple state. |
| + entry.expect_staple = false; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x0})); |
| + |
| + // Enable expect-staple for the other tests. |
| + entry.expect_staple = true; |
| + |
| + // Invalid report URI ID. |
| + entry.expect_staple_report_uri = "https://report.overflow.tld/upload"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| + |
| + // Unknown report URI. |
| + entry.expect_staple_report_uri = "https://report.unknown.tld/upload"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, WriteEntryExpectCT) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + TransportSecurityStateEntry entry; |
| + entry.hostname = "example.org"; |
| + |
| + entry.expect_ct = true; |
| + entry.expect_ct_report_uri = "https://report.example.org/upload"; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x12, 0x0})); |
| + |
| + // This should result in an "empty" expect-staple state. |
| + entry.expect_ct = false; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x0})); |
| + |
| + // Enable expect-staple for the other tests. |
| + entry.expect_ct = true; |
| + |
| + // Invalid report URI ID. |
| + entry.expect_ct_report_uri = "https://report.overflow.tld/upload"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| + |
| + // Unknown report URI. |
| + entry.expect_ct_report_uri = "https://report.unknown.tld/upload"; |
| + EXPECT_FALSE(TestWriteEntry(trie_writer.get(), entry, {})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, WriteEntryCombination) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + TransportSecurityStateEntry entry; |
| + entry.hostname = "example.org"; |
| + |
| + entry.force_https = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0x40})); |
| + |
| + entry.include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xC0})); |
| + |
| + entry.pinset = "pinset_a"; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xE2, 0x2, 0x0})); |
| + |
| + // Should have no effect. |
| + entry.hpkp_include_subdomains = true; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xE2, 0x2, 0x0})); |
| + |
| + entry.expect_ct = true; |
| + entry.expect_ct_report_uri = "https://report.example.org/upload"; |
| + EXPECT_TRUE(TestWriteEntry(trie_writer.get(), entry, {0xE2, 0x2, 0x90})); |
| + |
| + entry.expect_staple = true; |
| + entry.expect_staple_report_uri = "https://report.example.org/upload"; |
| + EXPECT_TRUE( |
| + TestWriteEntry(trie_writer.get(), entry, {0xE2, 0x2, 0x94, 0x40})); |
| + |
| + entry.expect_staple_include_subdomains = true; |
| + EXPECT_TRUE( |
| + TestWriteEntry(trie_writer.get(), entry, {0xE2, 0x2, 0x96, 0x40})); |
| +} |
| + |
| +TEST_F(TrieWriterTest, ReverseName) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + |
| + EXPECT_EQ(StringToVector(std::string("moc.elpmaxe\0", 12)), |
| + trie_writer->ReverseName("example.com")); |
| + EXPECT_EQ(StringToVector(std::string("moc.lssdab.stsh\0", 16)), |
| + trie_writer->ReverseName("hsts.badssl.com")); |
| +} |
| + |
| +TEST_F(TrieWriterTest, LongestCommonPrefix) { |
| + std::unique_ptr<TrieWriter> trie_writer = GetTrieWriter(); |
| + |
| + TransportSecurityStateEntry entry1; |
| + entry1.hostname = "sub.sub.example.com"; |
| + TransportSecurityStateEntry entry2; |
| + entry2.hostname = "sub.example.com"; |
| + TransportSecurityStateEntry entry3; |
| + entry3.hostname = "www.sub.example.com"; |
| + TransportSecurityStateEntry entry4; |
| + entry4.hostname = "example.com"; |
| + TransportSecurityStateEntry entry5; |
| + entry5.hostname = "badssl.com"; |
| + TransportSecurityStateEntry entry6; |
| + entry6.hostname = "example.org"; |
| + |
| + ReversedEntries reversed_entries; |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry1.hostname), &entry1)); |
| + |
| + EXPECT_EQ(StringToVector("moc.elpmaxe.bus.bus"), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| + |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry2.hostname), &entry2)); |
| + EXPECT_EQ(StringToVector("moc.elpmaxe.bus"), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| + |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry3.hostname), &entry3)); |
| + EXPECT_EQ(StringToVector("moc.elpmaxe.bus"), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| + |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry4.hostname), &entry4)); |
| + EXPECT_EQ(StringToVector("moc.elpmaxe"), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| + |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry5.hostname), &entry5)); |
| + EXPECT_EQ(StringToVector("moc."), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| + |
| + reversed_entries.push_back(base::MakeUnique<ReversedEntry>( |
| + trie_writer->ReverseName(entry6.hostname), &entry6)); |
| + EXPECT_EQ(StringToVector(""), |
| + trie_writer->LongestCommonPrefix(reversed_entries.cbegin(), |
| + reversed_entries.cend())); |
| +} |
| + |
| +} // transport_security_state |
| + |
| +} // namespace net |