| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 
|  | 2 // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 // found in the LICENSE file. | 
|  | 4 | 
|  | 5 #include "net/tools/domain_security_preload_generator/preloaded_state_generator.
     h" | 
|  | 6 | 
|  | 7 #include <iostream> | 
|  | 8 | 
|  | 9 #include <string> | 
|  | 10 | 
|  | 11 #include "base/strings/string_util.h" | 
|  | 12 #include "base/strings/stringprintf.h" | 
|  | 13 #include "net/tools/domain_security_preload_generator/cert_util.h" | 
|  | 14 #include "net/tools/domain_security_preload_generator/huffman/huffman_frequency_
     tracker.h" | 
|  | 15 #include "net/tools/domain_security_preload_generator/spki_hash.h" | 
|  | 16 | 
|  | 17 namespace net { | 
|  | 18 | 
|  | 19 namespace transport_security_state { | 
|  | 20 | 
|  | 21 namespace { | 
|  | 22 | 
|  | 23 static const char kNewLine[] = "\n"; | 
|  | 24 static const char kIndent[] = "  "; | 
|  | 25 | 
|  | 26 std::string FormatSPKIName(const std::string& name) { | 
|  | 27   return "kSPKIHash_" + name; | 
|  | 28 } | 
|  | 29 | 
|  | 30 std::string FormatAcceptedKeyName(const std::string& name) { | 
|  | 31   return "k" + name + "AcceptableCerts"; | 
|  | 32 } | 
|  | 33 | 
|  | 34 std::string FormatRejectedKeyName(const std::string& name) { | 
|  | 35   return "k" + name + "RejectedCerts"; | 
|  | 36 } | 
|  | 37 | 
|  | 38 std::string FormatReportURIName(const std::string& name) { | 
|  | 39   return "k" + name + "ReportURI"; | 
|  | 40 } | 
|  | 41 | 
|  | 42 // Replaces the first occurrence of "[[" + name + "]]" in |*tpl| with | 
|  | 43 // |value|. | 
|  | 44 bool ReplaceTag(const std::string& name, | 
|  | 45                 const std::string& value, | 
|  | 46                 std::string* tpl) { | 
|  | 47   std::string tag = "[[" + name + "]]"; | 
|  | 48 | 
|  | 49   size_t start_pos = tpl->find(tag); | 
|  | 50   if (start_pos == std::string::npos) { | 
|  | 51     return false; | 
|  | 52   } | 
|  | 53 | 
|  | 54   tpl->replace(start_pos, tag.length(), value); | 
|  | 55   return true; | 
|  | 56 } | 
|  | 57 | 
|  | 58 // Formats the bytes in |bytes| as an C++ array initializer and returns the | 
|  | 59 // resulting string. | 
|  | 60 std::string FormatVectorAsArray(const std::vector<uint8_t>& bytes) { | 
|  | 61   std::string output = "{"; | 
|  | 62   output.append(kNewLine); | 
|  | 63   output.append(kIndent); | 
|  | 64   output.append(kIndent); | 
|  | 65 | 
|  | 66   size_t bytes_on_current_line = 0; | 
|  | 67 | 
|  | 68   for (size_t i = 0; i < bytes.size(); ++i) { | 
|  | 69     base::StringAppendF(&output, "0x%02x,", bytes[i]); | 
|  | 70 | 
|  | 71     bytes_on_current_line++; | 
|  | 72     if (bytes_on_current_line >= 12 && i + 1 < bytes.size()) { | 
|  | 73       output.append(kNewLine); | 
|  | 74       output.append(kIndent); | 
|  | 75       output.append(kIndent); | 
|  | 76 | 
|  | 77       bytes_on_current_line = 0; | 
|  | 78     } else if (i + 1 < bytes.size()) { | 
|  | 79       output.append(" "); | 
|  | 80     } | 
|  | 81   } | 
|  | 82 | 
|  | 83   output.append(kNewLine); | 
|  | 84   output.append("}"); | 
|  | 85 | 
|  | 86   return output; | 
|  | 87 } | 
|  | 88 | 
|  | 89 std::string WritePinsetList(const std::string& name, | 
|  | 90                             const std::vector<std::string>& pins) { | 
|  | 91   std::string output = "static const char* const " + name + "[] = {"; | 
|  | 92   output.append(kNewLine); | 
|  | 93 | 
|  | 94   for (const auto& pin_name : pins) { | 
|  | 95     output.append(kIndent); | 
|  | 96     output.append(kIndent); | 
|  | 97     output.append(FormatSPKIName(pin_name)); | 
|  | 98     output.append(","); | 
|  | 99     output.append(kNewLine); | 
|  | 100   } | 
|  | 101 | 
|  | 102   output.append(kIndent); | 
|  | 103   output.append(kIndent); | 
|  | 104   output.append("NULL,"); | 
|  | 105   output.append(kNewLine); | 
|  | 106   output.append("};"); | 
|  | 107 | 
|  | 108   return output; | 
|  | 109 } | 
|  | 110 | 
|  | 111 HuffmanRepresentationTable ApproximateHuffman( | 
|  | 112     const DomainSecurityEntries& entries) { | 
|  | 113   HuffmanFrequencyTracker tracker; | 
|  | 114   for (const auto& entry : entries) { | 
|  | 115     for (const auto& c : entry->hostname) { | 
|  | 116       tracker.RecordUsage(c); | 
|  | 117     } | 
|  | 118 | 
|  | 119     tracker.RecordUsage(TrieWriter::kTerminalValue); | 
|  | 120     tracker.RecordUsage(TrieWriter::kEndOfTableValue); | 
|  | 121   } | 
|  | 122 | 
|  | 123   return tracker.ToTable(); | 
|  | 124 } | 
|  | 125 | 
|  | 126 }  // namespace | 
|  | 127 | 
|  | 128 PreloadedStateGenerator::PreloadedStateGenerator() {} | 
|  | 129 | 
|  | 130 PreloadedStateGenerator::~PreloadedStateGenerator() {} | 
|  | 131 | 
|  | 132 std::string PreloadedStateGenerator::Generate( | 
|  | 133     const std::string& preload_template, | 
|  | 134     const DomainSecurityEntries& entries, | 
|  | 135     const DomainIDList& domain_ids, | 
|  | 136     const Pinsets& pinsets, | 
|  | 137     bool verbose) { | 
|  | 138   std::string output = preload_template; | 
|  | 139 | 
|  | 140   NameIDMap domain_ids_map; | 
|  | 141   ProcessDomainIds(domain_ids, &domain_ids_map, &output); | 
|  | 142 | 
|  | 143   ProcessSPKIHashes(pinsets, &output); | 
|  | 144 | 
|  | 145   NameIDMap expect_ct_report_uri_map; | 
|  | 146   ProcessExpectCTURIs(entries, &expect_ct_report_uri_map, &output); | 
|  | 147 | 
|  | 148   NameIDMap expect_staple_report_uri_map; | 
|  | 149   ProcessExpectStapleURIs(entries, &expect_staple_report_uri_map, &output); | 
|  | 150 | 
|  | 151   NameIDMap pinsets_map; | 
|  | 152   ProcessPinsets(pinsets, &pinsets_map, &output); | 
|  | 153 | 
|  | 154   // The trie generation process is ran twice, the first time using an | 
|  | 155   // approximate Huffman table. During this first run, the correct character | 
|  | 156   // frequencies are collected which are then used to calculate the most space | 
|  | 157   // efficient Huffman table for the given inputs. This table is used for the | 
|  | 158   // second run. | 
|  | 159   HuffmanRepresentationTable table = ApproximateHuffman(entries); | 
|  | 160   HuffmanFrequencyTracker tracker; | 
|  | 161   TrieWriter writer(table, domain_ids_map, expect_ct_report_uri_map, | 
|  | 162                     expect_staple_report_uri_map, pinsets_map, &tracker); | 
|  | 163   writer.WriteEntries(entries); | 
|  | 164   uint32_t initial_length = writer.position(); | 
|  | 165 | 
|  | 166   HuffmanRepresentationTable optimal_table = tracker.ToTable(); | 
|  | 167   TrieWriter new_writer(optimal_table, domain_ids_map, expect_ct_report_uri_map, | 
|  | 168                         expect_staple_report_uri_map, pinsets_map, nullptr); | 
|  | 169 | 
|  | 170   uint32_t root_position = new_writer.WriteEntries(entries); | 
|  | 171   uint32_t new_length = new_writer.position(); | 
|  | 172 | 
|  | 173   std::vector<uint8_t> huffman_tree = tracker.ToVector(); | 
|  | 174 | 
|  | 175   new_writer.Flush(); | 
|  | 176 | 
|  | 177   ReplaceTag("HUFFMAN_TREE", FormatVectorAsArray(huffman_tree), &output); | 
|  | 178   ReplaceTag("HSTS_TRIE", FormatVectorAsArray(new_writer.bytes()), &output); | 
|  | 179 | 
|  | 180   ReplaceTag("HSTS_TRIE_BITS", std::to_string(new_length), &output); | 
|  | 181   ReplaceTag("HSTS_TRIE_ROOT", std::to_string(root_position), &output); | 
|  | 182 | 
|  | 183   if (verbose) { | 
|  | 184     std::cout << "Saved " << std::to_string(initial_length - new_length) | 
|  | 185               << " bits by using accurate Huffman counts." << std::endl; | 
|  | 186     std::cout << "Bit length " << std::to_string(new_length) << std::endl; | 
|  | 187     std::cout << "Root position " << std::to_string(root_position) << std::endl; | 
|  | 188   } | 
|  | 189 | 
|  | 190   return output; | 
|  | 191 } | 
|  | 192 | 
|  | 193 void PreloadedStateGenerator::ProcessDomainIds(const DomainIDList& domain_ids, | 
|  | 194                                                NameIDMap* map, | 
|  | 195                                                std::string* tpl) { | 
|  | 196   std::string output = "{"; | 
|  | 197   output.append(kNewLine); | 
|  | 198 | 
|  | 199   for (size_t i = 0; i < domain_ids.size(); ++i) { | 
|  | 200     const std::string& current = domain_ids.at(i); | 
|  | 201     output.append(kIndent); | 
|  | 202     output.append("DOMAIN_" + current + ","); | 
|  | 203     output.append(kNewLine); | 
|  | 204 | 
|  | 205     map->insert(NameIDPair(current, static_cast<uint32_t>(i))); | 
|  | 206   } | 
|  | 207 | 
|  | 208   output.append(kIndent); | 
|  | 209   output.append("// Boundary value for UMA_HISTOGRAM_ENUMERATION."); | 
|  | 210   output.append(kNewLine); | 
|  | 211   output.append(kIndent); | 
|  | 212   output.append("DOMAIN_NUM_EVENTS,"); | 
|  | 213   output.append(kNewLine); | 
|  | 214   output.append("}"); | 
|  | 215 | 
|  | 216   ReplaceTag("DOMAIN_IDS", output, tpl); | 
|  | 217 } | 
|  | 218 | 
|  | 219 void PreloadedStateGenerator::ProcessSPKIHashes(const Pinsets& pinset, | 
|  | 220                                                 std::string* tpl) { | 
|  | 221   std::string output; | 
|  | 222 | 
|  | 223   const SPKIHashMap& hashes = pinset.spki_hashes(); | 
|  | 224   for (const auto& current : hashes) { | 
|  | 225     const std::string& name = current.first; | 
|  | 226     const SPKIHash& hash = current.second; | 
|  | 227 | 
|  | 228     output.append("static const char " + FormatSPKIName(name) + "[] ="); | 
|  | 229     output.append(kNewLine); | 
|  | 230 | 
|  | 231     for (size_t i = 0; i < hash.size() / 16; ++i) { | 
|  | 232       output.append(kIndent); | 
|  | 233       output.append(kIndent); | 
|  | 234       output.append("\""); | 
|  | 235 | 
|  | 236       for (size_t j = i * 16; j < ((i + 1) * 16); ++j) { | 
|  | 237         base::StringAppendF(&output, "\\x%02x", hash.data()[j]); | 
|  | 238       } | 
|  | 239 | 
|  | 240       output.append("\""); | 
|  | 241       if (i + 1 == hash.size() / 16) { | 
|  | 242         output.append(";"); | 
|  | 243       } | 
|  | 244       output.append(kNewLine); | 
|  | 245     } | 
|  | 246 | 
|  | 247     output.append(kNewLine); | 
|  | 248   } | 
|  | 249 | 
|  | 250   base::TrimString(output, kNewLine, &output); | 
|  | 251   ReplaceTag("SPKI_HASHES", output, tpl); | 
|  | 252 } | 
|  | 253 | 
|  | 254 void PreloadedStateGenerator::ProcessExpectCTURIs( | 
|  | 255     const DomainSecurityEntries& entries, | 
|  | 256     NameIDMap* expect_ct_report_uri_map, | 
|  | 257     std::string* tpl) { | 
|  | 258   std::string output = "{"; | 
|  | 259   output.append(kNewLine); | 
|  | 260 | 
|  | 261   for (const auto& entry : entries) { | 
|  | 262     const std::string& url = entry->expect_ct_report_uri; | 
|  | 263     if (entry->expect_ct && url.size() && | 
|  | 264         expect_ct_report_uri_map->find(url) == | 
|  | 265             expect_ct_report_uri_map->cend()) { | 
|  | 266       output.append(kIndent); | 
|  | 267       output.append(kIndent); | 
|  | 268       output.append("\"" + entry->expect_ct_report_uri + "\","); | 
|  | 269       output.append(kNewLine); | 
|  | 270 | 
|  | 271       expect_ct_report_uri_map->insert( | 
|  | 272           NameIDPair(entry->expect_ct_report_uri, | 
|  | 273                      static_cast<uint32_t>(expect_ct_report_uri_map->size()))); | 
|  | 274     } | 
|  | 275   } | 
|  | 276 | 
|  | 277   output.append("}"); | 
|  | 278   ReplaceTag("EXPECT_CT_REPORT_URIS", output, tpl); | 
|  | 279 } | 
|  | 280 | 
|  | 281 void PreloadedStateGenerator::ProcessExpectStapleURIs( | 
|  | 282     const DomainSecurityEntries& entries, | 
|  | 283     NameIDMap* expect_staple_report_uri_map, | 
|  | 284     std::string* tpl) { | 
|  | 285   std::string output = "{"; | 
|  | 286   output.append(kNewLine); | 
|  | 287 | 
|  | 288   for (const auto& entry : entries) { | 
|  | 289     const std::string& url = entry->expect_staple_report_uri; | 
|  | 290     if (entry->expect_staple && url.size() && | 
|  | 291         expect_staple_report_uri_map->find(url) == | 
|  | 292             expect_staple_report_uri_map->cend()) { | 
|  | 293       output.append(kIndent); | 
|  | 294       output.append(kIndent); | 
|  | 295       output.append("\"" + entry->expect_staple_report_uri + "\","); | 
|  | 296       output.append(kNewLine); | 
|  | 297 | 
|  | 298       expect_staple_report_uri_map->insert(NameIDPair( | 
|  | 299           entry->expect_staple_report_uri, | 
|  | 300           static_cast<uint32_t>(expect_staple_report_uri_map->size()))); | 
|  | 301     } | 
|  | 302   } | 
|  | 303 | 
|  | 304   output.append("}"); | 
|  | 305   ReplaceTag("EXPECT_STAPLE_REPORT_URIS", output, tpl); | 
|  | 306 } | 
|  | 307 | 
|  | 308 void PreloadedStateGenerator::ProcessPinsets(const Pinsets& pinset, | 
|  | 309                                              NameIDMap* pinset_map, | 
|  | 310                                              std::string* tpl) { | 
|  | 311   std::string certs_output; | 
|  | 312   std::string pinsets_output = "{"; | 
|  | 313   pinsets_output.append(kNewLine); | 
|  | 314 | 
|  | 315   const PinsetMap& pinsets = pinset.pinsets(); | 
|  | 316   for (const auto& current : pinsets) { | 
|  | 317     const std::unique_ptr<Pinset>& pinset = current.second; | 
|  | 318     std::string uppercased_name = pinset->name(); | 
|  | 319     uppercased_name[0] = base::ToUpperASCII(uppercased_name[0]); | 
|  | 320 | 
|  | 321     const std::string& accepted_pins_names = | 
|  | 322         FormatAcceptedKeyName(uppercased_name); | 
|  | 323     certs_output.append( | 
|  | 324         WritePinsetList(accepted_pins_names, pinset->static_spki_hashes())); | 
|  | 325     certs_output.append(kNewLine); | 
|  | 326 | 
|  | 327     std::string rejected_pins_names = "kNoRejectedPublicKeys"; | 
|  | 328     if (pinset->bad_static_spki_hashes().size()) { | 
|  | 329       rejected_pins_names = FormatRejectedKeyName(uppercased_name); | 
|  | 330       certs_output.append(WritePinsetList(rejected_pins_names, | 
|  | 331                                           pinset->bad_static_spki_hashes())); | 
|  | 332       certs_output.append(kNewLine); | 
|  | 333     } | 
|  | 334 | 
|  | 335     std::string report_uri = "kNoReportURI"; | 
|  | 336     if (pinset->report_uri().size()) { | 
|  | 337       report_uri = FormatReportURIName(uppercased_name); | 
|  | 338       certs_output.append("static const char " + report_uri + "[] = "); | 
|  | 339       certs_output.append("\""); | 
|  | 340       certs_output.append(pinset->report_uri()); | 
|  | 341       certs_output.append("\";"); | 
|  | 342       certs_output.append(kNewLine); | 
|  | 343     } | 
|  | 344     certs_output.append(kNewLine); | 
|  | 345 | 
|  | 346     pinsets_output.append(kIndent); | 
|  | 347     pinsets_output.append(kIndent); | 
|  | 348     pinsets_output.append("{" + accepted_pins_names + ", " + | 
|  | 349                           rejected_pins_names + ", " + report_uri + "},"); | 
|  | 350     pinsets_output.append(kNewLine); | 
|  | 351 | 
|  | 352     pinset_map->insert( | 
|  | 353         NameIDPair(pinset->name(), static_cast<uint32_t>(pinset_map->size()))); | 
|  | 354   } | 
|  | 355 | 
|  | 356   pinsets_output.append("}"); | 
|  | 357 | 
|  | 358   base::TrimString(certs_output, kNewLine, &certs_output); | 
|  | 359 | 
|  | 360   ReplaceTag("ACCEPTABLE_CERTS", certs_output, tpl); | 
|  | 361   ReplaceTag("PINSETS", pinsets_output, tpl); | 
|  | 362 } | 
|  | 363 | 
|  | 364 }  // namespace transport_security_state | 
|  | 365 | 
|  | 366 }  // namespace net | 
| OLD | NEW | 
|---|