OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/quic/core/crypto/cert_compressor.h" | 5 #include "net/quic/core/crypto/cert_compressor.h" |
6 | 6 |
7 #include <cstdint> | 7 #include <cstdint> |
8 #include <memory> | 8 #include <memory> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "net/quic/core/quic_utils.h" | 11 #include "net/quic/core/quic_utils.h" |
12 #include "third_party/zlib/zlib.h" | 12 #include "third_party/zlib/zlib.h" |
13 | 13 |
14 using base::StringPiece; | 14 using base::StringPiece; |
15 using std::string; | 15 using std::string; |
16 using std::vector; | |
17 | 16 |
18 namespace net { | 17 namespace net { |
19 | 18 |
20 namespace { | 19 namespace { |
21 | 20 |
22 // kCommonCertSubstrings contains ~1500 bytes of common certificate substrings | 21 // kCommonCertSubstrings contains ~1500 bytes of common certificate substrings |
23 // in order to help zlib. This was generated via a fairly dumb algorithm from | 22 // in order to help zlib. This was generated via a fairly dumb algorithm from |
24 // the Alexa Top 5000 set - we could probably do better. | 23 // the Alexa Top 5000 set - we could probably do better. |
25 static const unsigned char kCommonCertSubstrings[] = { | 24 static const unsigned char kCommonCertSubstrings[] = { |
26 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, | 25 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 Type type; | 169 Type type; |
171 uint64_t hash; | 170 uint64_t hash; |
172 uint64_t set_hash; | 171 uint64_t set_hash; |
173 uint32_t index; | 172 uint32_t index; |
174 }; | 173 }; |
175 | 174 |
176 // MatchCerts returns a vector of CertEntries describing how to most | 175 // MatchCerts returns a vector of CertEntries describing how to most |
177 // efficiently represent |certs| to a peer who has the common sets identified | 176 // efficiently represent |certs| to a peer who has the common sets identified |
178 // by |client_common_set_hashes| and who has cached the certificates with the | 177 // by |client_common_set_hashes| and who has cached the certificates with the |
179 // 64-bit, FNV-1a hashes in |client_cached_cert_hashes|. | 178 // 64-bit, FNV-1a hashes in |client_cached_cert_hashes|. |
180 vector<CertEntry> MatchCerts(const vector<string>& certs, | 179 std::vector<CertEntry> MatchCerts(const std::vector<string>& certs, |
181 StringPiece client_common_set_hashes, | 180 StringPiece client_common_set_hashes, |
182 StringPiece client_cached_cert_hashes, | 181 StringPiece client_cached_cert_hashes, |
183 const CommonCertSets* common_sets) { | 182 const CommonCertSets* common_sets) { |
184 vector<CertEntry> entries; | 183 std::vector<CertEntry> entries; |
185 entries.reserve(certs.size()); | 184 entries.reserve(certs.size()); |
186 | 185 |
187 const bool cached_valid = | 186 const bool cached_valid = |
188 client_cached_cert_hashes.size() % sizeof(uint64_t) == 0 && | 187 client_cached_cert_hashes.size() % sizeof(uint64_t) == 0 && |
189 !client_cached_cert_hashes.empty(); | 188 !client_cached_cert_hashes.empty(); |
190 | 189 |
191 for (vector<string>::const_iterator i = certs.begin(); i != certs.end(); | 190 for (std::vector<string>::const_iterator i = certs.begin(); i != certs.end(); |
192 ++i) { | 191 ++i) { |
193 CertEntry entry; | 192 CertEntry entry; |
194 | 193 |
195 if (cached_valid) { | 194 if (cached_valid) { |
196 bool cached = false; | 195 bool cached = false; |
197 | 196 |
198 uint64_t hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size()); | 197 uint64_t hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size()); |
199 // This assumes that the machine is little-endian. | 198 // This assumes that the machine is little-endian. |
200 for (size_t j = 0; j < client_cached_cert_hashes.size(); | 199 for (size_t j = 0; j < client_cached_cert_hashes.size(); |
201 j += sizeof(uint64_t)) { | 200 j += sizeof(uint64_t)) { |
(...skipping 26 matching lines...) Expand all Loading... |
228 | 227 |
229 entry.type = CertEntry::COMPRESSED; | 228 entry.type = CertEntry::COMPRESSED; |
230 entries.push_back(entry); | 229 entries.push_back(entry); |
231 } | 230 } |
232 | 231 |
233 return entries; | 232 return entries; |
234 } | 233 } |
235 | 234 |
236 // CertEntriesSize returns the size, in bytes, of the serialised form of | 235 // CertEntriesSize returns the size, in bytes, of the serialised form of |
237 // |entries|. | 236 // |entries|. |
238 size_t CertEntriesSize(const vector<CertEntry>& entries) { | 237 size_t CertEntriesSize(const std::vector<CertEntry>& entries) { |
239 size_t entries_size = 0; | 238 size_t entries_size = 0; |
240 | 239 |
241 for (vector<CertEntry>::const_iterator i = entries.begin(); | 240 for (std::vector<CertEntry>::const_iterator i = entries.begin(); |
242 i != entries.end(); ++i) { | 241 i != entries.end(); ++i) { |
243 entries_size++; | 242 entries_size++; |
244 switch (i->type) { | 243 switch (i->type) { |
245 case CertEntry::COMPRESSED: | 244 case CertEntry::COMPRESSED: |
246 break; | 245 break; |
247 case CertEntry::CACHED: | 246 case CertEntry::CACHED: |
248 entries_size += sizeof(uint64_t); | 247 entries_size += sizeof(uint64_t); |
249 break; | 248 break; |
250 case CertEntry::COMMON: | 249 case CertEntry::COMMON: |
251 entries_size += sizeof(uint64_t) + sizeof(uint32_t); | 250 entries_size += sizeof(uint64_t) + sizeof(uint32_t); |
252 break; | 251 break; |
253 } | 252 } |
254 } | 253 } |
255 | 254 |
256 entries_size++; // for end marker | 255 entries_size++; // for end marker |
257 | 256 |
258 return entries_size; | 257 return entries_size; |
259 } | 258 } |
260 | 259 |
261 // SerializeCertEntries serialises |entries| to |out|, which must have enough | 260 // SerializeCertEntries serialises |entries| to |out|, which must have enough |
262 // space to contain them. | 261 // space to contain them. |
263 void SerializeCertEntries(uint8_t* out, const vector<CertEntry>& entries) { | 262 void SerializeCertEntries(uint8_t* out, const std::vector<CertEntry>& entries) { |
264 for (vector<CertEntry>::const_iterator i = entries.begin(); | 263 for (std::vector<CertEntry>::const_iterator i = entries.begin(); |
265 i != entries.end(); ++i) { | 264 i != entries.end(); ++i) { |
266 *out++ = static_cast<uint8_t>(i->type); | 265 *out++ = static_cast<uint8_t>(i->type); |
267 switch (i->type) { | 266 switch (i->type) { |
268 case CertEntry::COMPRESSED: | 267 case CertEntry::COMPRESSED: |
269 break; | 268 break; |
270 case CertEntry::CACHED: | 269 case CertEntry::CACHED: |
271 memcpy(out, &i->hash, sizeof(i->hash)); | 270 memcpy(out, &i->hash, sizeof(i->hash)); |
272 out += sizeof(uint64_t); | 271 out += sizeof(uint64_t); |
273 break; | 272 break; |
274 case CertEntry::COMMON: | 273 case CertEntry::COMMON: |
275 // Assumes a little-endian machine. | 274 // Assumes a little-endian machine. |
276 memcpy(out, &i->set_hash, sizeof(i->set_hash)); | 275 memcpy(out, &i->set_hash, sizeof(i->set_hash)); |
277 out += sizeof(i->set_hash); | 276 out += sizeof(i->set_hash); |
278 memcpy(out, &i->index, sizeof(uint32_t)); | 277 memcpy(out, &i->index, sizeof(uint32_t)); |
279 out += sizeof(uint32_t); | 278 out += sizeof(uint32_t); |
280 break; | 279 break; |
281 } | 280 } |
282 } | 281 } |
283 | 282 |
284 *out++ = 0; // end marker | 283 *out++ = 0; // end marker |
285 } | 284 } |
286 | 285 |
287 // ZlibDictForEntries returns a string that contains the zlib pre-shared | 286 // ZlibDictForEntries returns a string that contains the zlib pre-shared |
288 // dictionary to use in order to decompress a zlib block following |entries|. | 287 // dictionary to use in order to decompress a zlib block following |entries|. |
289 // |certs| is one-to-one with |entries| and contains the certificates for those | 288 // |certs| is one-to-one with |entries| and contains the certificates for those |
290 // entries that are CACHED or COMMON. | 289 // entries that are CACHED or COMMON. |
291 string ZlibDictForEntries(const vector<CertEntry>& entries, | 290 string ZlibDictForEntries(const std::vector<CertEntry>& entries, |
292 const vector<string>& certs) { | 291 const std::vector<string>& certs) { |
293 string zlib_dict; | 292 string zlib_dict; |
294 | 293 |
295 // The dictionary starts with the common and cached certs in reverse order. | 294 // The dictionary starts with the common and cached certs in reverse order. |
296 size_t zlib_dict_size = 0; | 295 size_t zlib_dict_size = 0; |
297 for (size_t i = certs.size() - 1; i < certs.size(); i--) { | 296 for (size_t i = certs.size() - 1; i < certs.size(); i--) { |
298 if (entries[i].type != CertEntry::COMPRESSED) { | 297 if (entries[i].type != CertEntry::COMPRESSED) { |
299 zlib_dict_size += certs[i].size(); | 298 zlib_dict_size += certs[i].size(); |
300 } | 299 } |
301 } | 300 } |
302 | 301 |
(...skipping 10 matching lines...) Expand all Loading... |
313 | 312 |
314 zlib_dict += string(reinterpret_cast<const char*>(kCommonCertSubstrings), | 313 zlib_dict += string(reinterpret_cast<const char*>(kCommonCertSubstrings), |
315 sizeof(kCommonCertSubstrings)); | 314 sizeof(kCommonCertSubstrings)); |
316 | 315 |
317 DCHECK_EQ(zlib_dict.size(), zlib_dict_size); | 316 DCHECK_EQ(zlib_dict.size(), zlib_dict_size); |
318 | 317 |
319 return zlib_dict; | 318 return zlib_dict; |
320 } | 319 } |
321 | 320 |
322 // HashCerts returns the FNV-1a hashes of |certs|. | 321 // HashCerts returns the FNV-1a hashes of |certs|. |
323 vector<uint64_t> HashCerts(const vector<string>& certs) { | 322 std::vector<uint64_t> HashCerts(const std::vector<string>& certs) { |
324 vector<uint64_t> ret; | 323 std::vector<uint64_t> ret; |
325 ret.reserve(certs.size()); | 324 ret.reserve(certs.size()); |
326 | 325 |
327 for (vector<string>::const_iterator i = certs.begin(); i != certs.end(); | 326 for (std::vector<string>::const_iterator i = certs.begin(); i != certs.end(); |
328 ++i) { | 327 ++i) { |
329 ret.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); | 328 ret.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); |
330 } | 329 } |
331 | 330 |
332 return ret; | 331 return ret; |
333 } | 332 } |
334 | 333 |
335 // ParseEntries parses the serialised form of a vector of CertEntries from | 334 // ParseEntries parses the serialised form of a vector of CertEntries from |
336 // |in_out| and writes them to |out_entries|. CACHED and COMMON entries are | 335 // |in_out| and writes them to |out_entries|. CACHED and COMMON entries are |
337 // resolved using |cached_certs| and |common_sets| and written to |out_certs|. | 336 // resolved using |cached_certs| and |common_sets| and written to |out_certs|. |
338 // |in_out| is updated to contain the trailing data. | 337 // |in_out| is updated to contain the trailing data. |
339 bool ParseEntries(StringPiece* in_out, | 338 bool ParseEntries(StringPiece* in_out, |
340 const vector<string>& cached_certs, | 339 const std::vector<string>& cached_certs, |
341 const CommonCertSets* common_sets, | 340 const CommonCertSets* common_sets, |
342 vector<CertEntry>* out_entries, | 341 std::vector<CertEntry>* out_entries, |
343 vector<string>* out_certs) { | 342 std::vector<string>* out_certs) { |
344 StringPiece in = *in_out; | 343 StringPiece in = *in_out; |
345 vector<uint64_t> cached_hashes; | 344 std::vector<uint64_t> cached_hashes; |
346 | 345 |
347 out_entries->clear(); | 346 out_entries->clear(); |
348 out_certs->clear(); | 347 out_certs->clear(); |
349 | 348 |
350 for (;;) { | 349 for (;;) { |
351 if (in.empty()) { | 350 if (in.empty()) { |
352 return false; | 351 return false; |
353 } | 352 } |
354 CertEntry entry; | 353 CertEntry entry; |
355 const uint8_t type_byte = in[0]; | 354 const uint8_t type_byte = in[0]; |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
448 z_ = nullptr; | 447 z_ = nullptr; |
449 } | 448 } |
450 | 449 |
451 z_stream* z_; | 450 z_stream* z_; |
452 const Type type_; | 451 const Type type_; |
453 }; | 452 }; |
454 | 453 |
455 } // anonymous namespace | 454 } // anonymous namespace |
456 | 455 |
457 // static | 456 // static |
458 string CertCompressor::CompressChain(const vector<string>& certs, | 457 string CertCompressor::CompressChain(const std::vector<string>& certs, |
459 StringPiece client_common_set_hashes, | 458 StringPiece client_common_set_hashes, |
460 StringPiece client_cached_cert_hashes, | 459 StringPiece client_cached_cert_hashes, |
461 const CommonCertSets* common_sets) { | 460 const CommonCertSets* common_sets) { |
462 const vector<CertEntry> entries = MatchCerts( | 461 const std::vector<CertEntry> entries = MatchCerts( |
463 certs, client_common_set_hashes, client_cached_cert_hashes, common_sets); | 462 certs, client_common_set_hashes, client_cached_cert_hashes, common_sets); |
464 DCHECK_EQ(entries.size(), certs.size()); | 463 DCHECK_EQ(entries.size(), certs.size()); |
465 | 464 |
466 size_t uncompressed_size = 0; | 465 size_t uncompressed_size = 0; |
467 for (size_t i = 0; i < entries.size(); i++) { | 466 for (size_t i = 0; i < entries.size(); i++) { |
468 if (entries[i].type == CertEntry::COMPRESSED) { | 467 if (entries[i].type == CertEntry::COMPRESSED) { |
469 uncompressed_size += 4 /* uint32_t length */ + certs[i].size(); | 468 uncompressed_size += 4 /* uint32_t length */ + certs[i].size(); |
470 } | 469 } |
471 } | 470 } |
472 | 471 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
550 if (rv != Z_STREAM_END) { | 549 if (rv != Z_STREAM_END) { |
551 return ""; | 550 return ""; |
552 } | 551 } |
553 | 552 |
554 result.resize(result.size() - z.avail_out); | 553 result.resize(result.size() - z.avail_out); |
555 return result; | 554 return result; |
556 } | 555 } |
557 | 556 |
558 // static | 557 // static |
559 bool CertCompressor::DecompressChain(StringPiece in, | 558 bool CertCompressor::DecompressChain(StringPiece in, |
560 const vector<string>& cached_certs, | 559 const std::vector<string>& cached_certs, |
561 const CommonCertSets* common_sets, | 560 const CommonCertSets* common_sets, |
562 vector<string>* out_certs) { | 561 std::vector<string>* out_certs) { |
563 vector<CertEntry> entries; | 562 std::vector<CertEntry> entries; |
564 if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) { | 563 if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) { |
565 return false; | 564 return false; |
566 } | 565 } |
567 DCHECK_EQ(entries.size(), out_certs->size()); | 566 DCHECK_EQ(entries.size(), out_certs->size()); |
568 | 567 |
569 std::unique_ptr<uint8_t[]> uncompressed_data; | 568 std::unique_ptr<uint8_t[]> uncompressed_data; |
570 StringPiece uncompressed; | 569 StringPiece uncompressed; |
571 | 570 |
572 if (!in.empty()) { | 571 if (!in.empty()) { |
573 if (in.size() < sizeof(uint32_t)) { | 572 if (in.size() < sizeof(uint32_t)) { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 } | 637 } |
639 | 638 |
640 if (!uncompressed.empty()) { | 639 if (!uncompressed.empty()) { |
641 return false; | 640 return false; |
642 } | 641 } |
643 | 642 |
644 return true; | 643 return true; |
645 } | 644 } |
646 | 645 |
647 } // namespace net | 646 } // namespace net |
OLD | NEW |