Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: net/tools/domain_security_preload_generator/preloaded_state_generator.cc

Issue 2551153003: Add static domain security state generator tool. (Closed)
Patch Set: Fix iOS? Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698