| 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 { |
| 20 |
| 21 static const char* kNewLine = "\n"; |
| 22 static const char* kIndent = " "; |
| 23 |
| 24 std::string FormatSPKIName(const std::string& name) { |
| 25 return "kSPKIHash_" + name; |
| 26 } |
| 27 |
| 28 std::string FormatAcceptedKeyName(const std::string& name) { |
| 29 return "k" + name + "AcceptableCerts"; |
| 30 } |
| 31 |
| 32 std::string FormatRejectedKeyName(const std::string& name) { |
| 33 return "k" + name + "RejectedCerts"; |
| 34 } |
| 35 |
| 36 std::string FormatReportURIName(const std::string& name) { |
| 37 return "k" + name + "ReportURI"; |
| 38 } |
| 39 |
| 40 std::string FormatVectorAsArray(const std::vector<uint8_t>& bytes) { |
| 41 std::string output = "{"; |
| 42 output.append(kNewLine); |
| 43 output.append(kIndent); |
| 44 output.append(kIndent); |
| 45 |
| 46 size_t bytes_on_current_line = 0; |
| 47 |
| 48 for (size_t i = 0; i < bytes.size(); ++i) { |
| 49 base::StringAppendF(&output, "0x%02x,", bytes[i]); |
| 50 |
| 51 bytes_on_current_line++; |
| 52 if (bytes_on_current_line >= 12 && (i + 1) < bytes.size()) { |
| 53 output.append(kNewLine); |
| 54 output.append(kIndent); |
| 55 output.append(kIndent); |
| 56 |
| 57 bytes_on_current_line = 0; |
| 58 } else if ((i + 1) < bytes.size()) { |
| 59 output.append(" "); |
| 60 } |
| 61 } |
| 62 |
| 63 output.append(kNewLine); |
| 64 output.append("}"); |
| 65 |
| 66 return output; |
| 67 } |
| 68 |
| 69 std::string WritePinsetList(const std::string& name, |
| 70 const std::vector<std::string>& pins) { |
| 71 std::string output = "static const char* const " + name + "[] = {"; |
| 72 output.append(kNewLine); |
| 73 |
| 74 for (const auto& pin_name : pins) { |
| 75 output.append(kIndent); |
| 76 output.append(kIndent); |
| 77 output.append(FormatSPKIName(pin_name)); |
| 78 output.append(","); |
| 79 output.append(kNewLine); |
| 80 } |
| 81 |
| 82 output.append(kIndent); |
| 83 output.append(kIndent); |
| 84 output.append("NULL,"); |
| 85 output.append(kNewLine); |
| 86 output.append("};"); |
| 87 |
| 88 return output; |
| 89 } |
| 90 |
| 91 HuffmanRepresentationTable ApproximateHuffman( |
| 92 const DomainSecurityEntries& entries) { |
| 93 HuffmanFrequencyTracker tracker; |
| 94 for (const auto& entry : entries) { |
| 95 for (const auto& c : entry->hostname()) { |
| 96 tracker.RecordUsage(c); |
| 97 } |
| 98 |
| 99 tracker.RecordUsage(TrieWriter::kTerminalValue); |
| 100 tracker.RecordUsage(TrieWriter::kEndOfTableValue); |
| 101 } |
| 102 |
| 103 return tracker.ToHuffmanRepresentationTable(); |
| 104 } |
| 105 |
| 106 } // namespace |
| 107 |
| 108 PreloadedStateGenerator::PreloadedStateGenerator() {} |
| 109 |
| 110 PreloadedStateGenerator::~PreloadedStateGenerator() {} |
| 111 |
| 112 std::string PreloadedStateGenerator::Generate( |
| 113 const std::string& preload_template, |
| 114 const DomainSecurityEntries& entries, |
| 115 const DomainIDList& domain_ids, |
| 116 const Pinsets& pinsets, |
| 117 bool verbose) { |
| 118 template_.reset(new Template(preload_template)); |
| 119 |
| 120 NameIDMap domain_ids_map; |
| 121 ProcessDomainIds(domain_ids, &domain_ids_map); |
| 122 |
| 123 ProcessSPKIHashes(pinsets); |
| 124 |
| 125 NameIDMap expect_ct_report_uri_map; |
| 126 ProcessExpectCTURIs(entries, &expect_ct_report_uri_map); |
| 127 |
| 128 NameIDMap expect_staple_report_uri_map; |
| 129 ProcessExpectStapleURIs(entries, &expect_staple_report_uri_map); |
| 130 |
| 131 NameIDMap pinsets_map; |
| 132 ProcessPinsets(pinsets, &pinsets_map); |
| 133 |
| 134 HuffmanRepresentationTable table = ApproximateHuffman(entries); |
| 135 HuffmanFrequencyTracker tracker; |
| 136 TrieWriter writer(table, domain_ids_map, expect_ct_report_uri_map, |
| 137 expect_staple_report_uri_map, pinsets_map, &tracker); |
| 138 writer.WriteEntries(entries); |
| 139 uint32_t initial_length = writer.position(); |
| 140 |
| 141 HuffmanRepresentationTable optimal_table = |
| 142 tracker.ToHuffmanRepresentationTable(); |
| 143 TrieWriter new_writer(optimal_table, domain_ids_map, expect_ct_report_uri_map, |
| 144 expect_staple_report_uri_map, pinsets_map, nullptr); |
| 145 |
| 146 int root_position = new_writer.WriteEntries(entries); |
| 147 uint32_t new_length = new_writer.position(); |
| 148 |
| 149 std::vector<uint8_t> huffman_tree = tracker.AsVector(); |
| 150 |
| 151 new_writer.close(); |
| 152 |
| 153 template_->ReplaceTag("HUFFMAN_TREE", FormatVectorAsArray(huffman_tree)); |
| 154 template_->ReplaceTag("HSTS_TRIE", FormatVectorAsArray(new_writer.bytes())); |
| 155 |
| 156 template_->ReplaceTag("HSTS_TRIE_BITS", std::to_string(new_length)); |
| 157 template_->ReplaceTag("HSTS_TRIE_ROOT", std::to_string(root_position)); |
| 158 |
| 159 if (verbose) { |
| 160 std::cout << "Saved " << uint(initial_length - new_length) |
| 161 << " bits by using accurate Huffman counts." << std::endl; |
| 162 std::cout << "Bit length " << uint(new_length) << std::endl; |
| 163 std::cout << "Root position " << uint(root_position) << std::endl; |
| 164 } |
| 165 |
| 166 return template_->GetOutput(); |
| 167 } |
| 168 |
| 169 void PreloadedStateGenerator::ProcessDomainIds(const DomainIDList& domain_ids, |
| 170 NameIDMap* map) { |
| 171 DCHECK(template_.get()); |
| 172 |
| 173 std::string output = "{"; |
| 174 output.append(kNewLine); |
| 175 |
| 176 for (size_t i = 0; i < domain_ids.size(); ++i) { |
| 177 const std::string& current = domain_ids.at(i); |
| 178 output.append(kIndent); |
| 179 output.append("DOMAIN_" + current + ","); |
| 180 output.append(kNewLine); |
| 181 |
| 182 map->insert(NameIDPair(current, i)); |
| 183 } |
| 184 |
| 185 output.append(kIndent); |
| 186 output.append("// Boundary value for UMA_HISTOGRAM_ENUMERATION."); |
| 187 output.append(kNewLine); |
| 188 output.append(kIndent); |
| 189 output.append("DOMAIN_NUM_EVENTS,"); |
| 190 output.append(kNewLine); |
| 191 output.append("}"); |
| 192 |
| 193 template_->ReplaceTag("DOMAIN_IDS", output); |
| 194 } |
| 195 |
| 196 void PreloadedStateGenerator::ProcessSPKIHashes(const Pinsets& pinset) { |
| 197 DCHECK(template_.get()); |
| 198 |
| 199 std::string output; |
| 200 |
| 201 const SPKIHashMap& hashes = pinset.spki_hashes(); |
| 202 for (const auto& current : hashes) { |
| 203 const std::string& name = current.first; |
| 204 const SPKIHash& hash = current.second; |
| 205 |
| 206 output.append("static const char " + FormatSPKIName(name) + "[] ="); |
| 207 output.append(kNewLine); |
| 208 |
| 209 for (size_t i = 0; i < hash.size() / 16; ++i) { |
| 210 output.append(kIndent); |
| 211 output.append(kIndent); |
| 212 output.append("\""); |
| 213 |
| 214 for (size_t j = i * 16; j < ((i + 1) * 16); ++j) { |
| 215 base::StringAppendF(&output, "\\x%02x", hash.data()[j]); |
| 216 } |
| 217 |
| 218 output.append("\""); |
| 219 if (i + 1 == hash.size() / 16) { |
| 220 output.append(";"); |
| 221 } |
| 222 output.append(kNewLine); |
| 223 } |
| 224 |
| 225 output.append(kNewLine); |
| 226 } |
| 227 |
| 228 base::TrimString(output, kNewLine, &output); |
| 229 template_->ReplaceTag("SPKI_HASHES", output); |
| 230 } |
| 231 |
| 232 void PreloadedStateGenerator::ProcessExpectCTURIs( |
| 233 const DomainSecurityEntries& entries, |
| 234 NameIDMap* expect_ct_report_uri_map) { |
| 235 std::string output = "{"; |
| 236 output.append(kNewLine); |
| 237 |
| 238 for (const auto& entry : entries) { |
| 239 const std::string& url = entry->expect_ct_report_uri(); |
| 240 if (entry->expect_ct() && url.size() && |
| 241 expect_ct_report_uri_map->find(url) == |
| 242 expect_ct_report_uri_map->cend()) { |
| 243 output.append(kIndent); |
| 244 output.append(kIndent); |
| 245 output.append("\"" + entry->expect_ct_report_uri() + "\","); |
| 246 output.append(kNewLine); |
| 247 |
| 248 expect_ct_report_uri_map->insert(NameIDPair( |
| 249 entry->expect_ct_report_uri(), expect_ct_report_uri_map->size())); |
| 250 } |
| 251 } |
| 252 |
| 253 output.append("}"); |
| 254 template_->ReplaceTag("EXPECT_CT_REPORT_URIS", output); |
| 255 } |
| 256 |
| 257 void PreloadedStateGenerator::ProcessExpectStapleURIs( |
| 258 const DomainSecurityEntries& entries, |
| 259 NameIDMap* expect_staple_report_uri_map) { |
| 260 std::string output = "{"; |
| 261 output.append(kNewLine); |
| 262 |
| 263 for (const auto& entry : entries) { |
| 264 const std::string& url = entry->expect_staple_report_uri(); |
| 265 if (entry->expect_staple() && url.size() && |
| 266 expect_staple_report_uri_map->find(url) == |
| 267 expect_staple_report_uri_map->cend()) { |
| 268 output.append(kIndent); |
| 269 output.append(kIndent); |
| 270 output.append("\"" + entry->expect_staple_report_uri() + "\","); |
| 271 output.append(kNewLine); |
| 272 |
| 273 expect_staple_report_uri_map->insert( |
| 274 NameIDPair(entry->expect_staple_report_uri(), |
| 275 expect_staple_report_uri_map->size())); |
| 276 } |
| 277 } |
| 278 |
| 279 output.append("}"); |
| 280 template_->ReplaceTag("EXPECT_STAPLE_REPORT_URIS", output); |
| 281 } |
| 282 |
| 283 void PreloadedStateGenerator::ProcessPinsets(const Pinsets& pinset, |
| 284 NameIDMap* pinset_map) { |
| 285 std::string certs_output; |
| 286 std::string pinsets_output = "{"; |
| 287 pinsets_output.append(kNewLine); |
| 288 |
| 289 const PinsetMap& pinsets = pinset.pinsets(); |
| 290 for (const auto& current : pinsets) { |
| 291 const std::unique_ptr<Pinset>& pinset = current.second; |
| 292 std::string uppercased_name = pinset->name(); |
| 293 uppercased_name[0] = base::ToUpperASCII(uppercased_name[0]); |
| 294 |
| 295 const std::string& accepted_pins_names = |
| 296 FormatAcceptedKeyName(uppercased_name); |
| 297 certs_output.append( |
| 298 WritePinsetList(accepted_pins_names, pinset->static_spki_hashes())); |
| 299 certs_output.append(kNewLine); |
| 300 |
| 301 std::string rejected_pins_names = "kNoRejectedPublicKeys"; |
| 302 if (pinset->bad_static_spki_hashes().size()) { |
| 303 rejected_pins_names = FormatRejectedKeyName(uppercased_name); |
| 304 certs_output.append(WritePinsetList(rejected_pins_names, |
| 305 pinset->bad_static_spki_hashes())); |
| 306 certs_output.append(kNewLine); |
| 307 } |
| 308 |
| 309 std::string report_uri = "kNoReportURI"; |
| 310 if (pinset->report_uri().size()) { |
| 311 report_uri = FormatReportURIName(uppercased_name); |
| 312 certs_output.append("static const char " + report_uri + "[] = "); |
| 313 certs_output.append("\""); |
| 314 certs_output.append(pinset->report_uri()); |
| 315 certs_output.append("\";"); |
| 316 certs_output.append(kNewLine); |
| 317 } |
| 318 certs_output.append(kNewLine); |
| 319 |
| 320 pinsets_output.append(kIndent); |
| 321 pinsets_output.append(kIndent); |
| 322 pinsets_output.append("{" + accepted_pins_names + ", " + |
| 323 rejected_pins_names + ", " + report_uri + "},"); |
| 324 pinsets_output.append(kNewLine); |
| 325 |
| 326 pinset_map->insert(NameIDPair(pinset->name(), pinset_map->size())); |
| 327 } |
| 328 |
| 329 pinsets_output.append("}"); |
| 330 |
| 331 base::TrimString(certs_output, kNewLine, &certs_output); |
| 332 |
| 333 template_->ReplaceTag("ACCEPTABLE_CERTS", certs_output); |
| 334 template_->ReplaceTag("PINSETS", pinsets_output); |
| 335 } |
| 336 |
| 337 } // namespace net |
| OLD | NEW |