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/crypto/cert_compressor.h" | 5 #include "net/quic/crypto/cert_compressor.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "net/quic/quic_utils.h" | 9 #include "net/quic/quic_utils.h" |
10 #include "third_party/zlib/zlib.h" | 10 #include "third_party/zlib/zlib.h" |
11 | 11 |
12 using base::StringPiece; | 12 using base::StringPiece; |
13 using std::string; | 13 using std::string; |
14 using std::vector; | 14 using std::vector; |
15 | 15 |
16 namespace net { | 16 namespace net { |
17 | 17 |
18 namespace { | 18 namespace { |
19 | 19 |
20 // kCommonCertSubstrings contains ~1500 bytes of common certificate substrings | 20 // kCommonCertSubstrings contains ~1500 bytes of common certificate substrings |
21 // in order to help zlib. | 21 // in order to help zlib. This was generated via a fairly dumb algorithm from |
| 22 // the Alexa Top 5000 set - we could probably do better. |
22 static unsigned char kCommonCertSubstrings[] = { | 23 static unsigned char kCommonCertSubstrings[] = { |
23 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, | 24 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, |
24 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, | 25 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, |
25 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, | 26 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, |
26 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, | 27 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, |
27 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07, | 28 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07, |
28 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, | 29 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, |
29 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, | 30 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, |
30 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34, | 31 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34, |
31 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, | 32 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 struct CertEntry { | 152 struct CertEntry { |
152 public: | 153 public: |
153 enum Type { | 154 enum Type { |
154 // COMPRESSED means that the certificate is included in the trailing zlib | 155 // COMPRESSED means that the certificate is included in the trailing zlib |
155 // data. | 156 // data. |
156 COMPRESSED = 1, | 157 COMPRESSED = 1, |
157 // CACHED means that the certificate is already known to the peer and will | 158 // CACHED means that the certificate is already known to the peer and will |
158 // be replaced by its 64-bit hash (in |hash|). | 159 // be replaced by its 64-bit hash (in |hash|). |
159 CACHED = 2, | 160 CACHED = 2, |
160 // COMMON means that the certificate is in a common certificate set known | 161 // COMMON means that the certificate is in a common certificate set known |
161 // to the peer with hash |set_hash| and index |index|. | 162 // to the peer with hash |set_hash| and certificate index |index|. |
162 COMMON = 3, | 163 COMMON = 3, |
163 }; | 164 }; |
164 | 165 |
165 Type type; | 166 Type type; |
166 uint64 hash; | 167 uint64 hash; |
167 uint64 set_hash; | 168 uint64 set_hash; |
168 uint32 index; | 169 uint32 index; |
169 }; | 170 }; |
170 | 171 |
171 // MatchCerts returns a vector of CertEntries describing how to most | 172 // MatchCerts returns a vector of CertEntries describing how to most |
172 // efficiently represent |certs| to a peer who has the common sets identified | 173 // efficiently represent |certs| to a peer who has the common sets identified |
173 // by |client_common_set_hashes| and who has cached the certificates with the | 174 // by |client_common_set_hashes| and who has cached the certificates with the |
174 // 64-bit, FNV-1a hashes in |client_cached|. | 175 // 64-bit, FNV-1a hashes in |client_cached_cert_hashes|. |
175 vector<CertEntry> MatchCerts(const vector<string>& certs, | 176 vector<CertEntry> MatchCerts(const vector<string>& certs, |
176 StringPiece client_common_set_hashes, | 177 StringPiece client_common_set_hashes, |
177 StringPiece client_cached, | 178 StringPiece client_cached_cert_hashes, |
178 CommonCertSet* common_set) { | 179 const CommonCertSets* common_set) { |
179 vector<CertEntry> entries; | 180 vector<CertEntry> entries; |
180 entries.reserve(certs.size()); | 181 entries.reserve(certs.size()); |
181 | 182 |
182 const bool cached_valid = client_cached.size() % sizeof(uint64) == 0 && | 183 const bool cached_valid = |
183 !client_cached.empty(); | 184 client_cached_cert_hashes.size() % sizeof(uint64) == 0 && |
| 185 !client_cached_cert_hashes.empty(); |
184 | 186 |
185 for (vector<string>::const_iterator i = certs.begin(); | 187 for (vector<string>::const_iterator i = certs.begin(); |
186 i != certs.end(); ++i) { | 188 i != certs.end(); ++i) { |
187 CertEntry entry; | 189 CertEntry entry; |
188 | 190 |
189 if (cached_valid) { | 191 if (cached_valid) { |
190 bool cached = false; | 192 bool cached = false; |
191 | 193 |
192 uint64 hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size()); | 194 uint64 hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size()); |
193 // This assumes that the machine is little-endian. | 195 // This assumes that the machine is little-endian. |
194 for (size_t i = 0; i < client_cached.size(); i += sizeof(uint64)) { | 196 for (size_t i = 0; i < client_cached_cert_hashes.size(); |
| 197 i += sizeof(uint64)) { |
195 uint64 cached_hash; | 198 uint64 cached_hash; |
196 memcpy(&cached_hash, client_cached.data() + i, sizeof(uint64)); | 199 memcpy(&cached_hash, client_cached_cert_hashes.data() + i, |
| 200 sizeof(uint64)); |
197 if (hash != cached_hash) { | 201 if (hash != cached_hash) { |
198 continue; | 202 continue; |
199 } | 203 } |
200 | 204 |
201 entry.type = CertEntry::CACHED; | 205 entry.type = CertEntry::CACHED; |
202 entry.hash = hash; | 206 entry.hash = hash; |
203 entries.push_back(entry); | 207 entries.push_back(entry); |
204 cached = true; | 208 cached = true; |
205 break; | 209 break; |
206 } | 210 } |
207 | 211 |
208 if (cached) { | 212 if (cached) { |
209 continue; | 213 continue; |
210 } | 214 } |
211 } | 215 } |
212 | 216 |
213 if (common_set && | 217 if (common_set && common_set->MatchCert(*i, client_common_set_hashes, |
214 common_set->MatchCert(*i, client_common_set_hashes, &entry.set_hash, | 218 &entry.set_hash, &entry.index)) { |
215 &entry.index)) { | |
216 entry.type = CertEntry::COMMON; | 219 entry.type = CertEntry::COMMON; |
217 entries.push_back(entry); | 220 entries.push_back(entry); |
218 continue; | 221 continue; |
219 } | 222 } |
220 | 223 |
221 entry.type = CertEntry::COMPRESSED; | 224 entry.type = CertEntry::COMPRESSED; |
222 entries.push_back(entry); | 225 entries.push_back(entry); |
223 } | 226 } |
224 | 227 |
225 return entries; | 228 return entries; |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 | 325 |
323 return ret; | 326 return ret; |
324 } | 327 } |
325 | 328 |
326 // ParseEntries parses the serialised form of a vector of CertEntries from | 329 // ParseEntries parses the serialised form of a vector of CertEntries from |
327 // |in_out| and writes them to |out_entries|. CACHED and COMMON entries are | 330 // |in_out| and writes them to |out_entries|. CACHED and COMMON entries are |
328 // resolved using |cached_certs| and |common_set| and written to |out_certs|. | 331 // resolved using |cached_certs| and |common_set| and written to |out_certs|. |
329 // |in_out| is updated to contain the trailing data. | 332 // |in_out| is updated to contain the trailing data. |
330 bool ParseEntries(StringPiece* in_out, | 333 bool ParseEntries(StringPiece* in_out, |
331 const vector<string>& cached_certs, | 334 const vector<string>& cached_certs, |
332 CommonCertSet* common_set, | 335 const CommonCertSets* common_set, |
333 vector<CertEntry>* out_entries, | 336 vector<CertEntry>* out_entries, |
334 vector<string>* out_certs) { | 337 vector<string>* out_certs) { |
335 StringPiece in = *in_out; | 338 StringPiece in = *in_out; |
336 vector<uint64> cached_hashes; | 339 vector<uint64> cached_hashes; |
337 | 340 |
338 out_entries->clear(); | 341 out_entries->clear(); |
339 out_certs->clear(); | 342 out_certs->clear(); |
340 | 343 |
341 for (;;) { | 344 for (;;) { |
342 if (in.empty()) { | 345 if (in.empty()) { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 return true; | 415 return true; |
413 } | 416 } |
414 | 417 |
415 class ScopedZLib { | 418 class ScopedZLib { |
416 public: | 419 public: |
417 enum Type { | 420 enum Type { |
418 INFLATE, | 421 INFLATE, |
419 DEFLATE, | 422 DEFLATE, |
420 }; | 423 }; |
421 | 424 |
422 explicit ScopedZLib(Type type) | 425 explicit ScopedZLib(Type type) : z_(NULL), type_(type) {} |
423 : z_(NULL), | |
424 type_(type) { | |
425 } | |
426 | 426 |
427 void reset(z_stream* z) { | 427 void reset(z_stream* z) { z_ = z; } |
428 z_ = z; | |
429 } | |
430 | 428 |
431 ~ScopedZLib() { | 429 ~ScopedZLib() { |
432 if (z_) { | 430 if (z_) { |
433 if (type_ == DEFLATE) { | 431 if (type_ == DEFLATE) { |
434 deflateEnd(z_); | 432 deflateEnd(z_); |
435 } else { | 433 } else { |
436 inflateEnd(z_); | 434 inflateEnd(z_); |
437 } | 435 } |
438 z_ = NULL; | 436 z_ = NULL; |
439 } | 437 } |
440 } | 438 } |
441 | 439 |
442 private: | 440 private: |
443 z_stream* z_; | 441 z_stream* z_; |
444 const Type type_; | 442 const Type type_; |
445 }; | 443 }; |
446 | 444 |
447 } // anonymous namespace | 445 } // anonymous namespace |
448 | 446 |
449 | 447 |
450 // static | 448 // static |
451 string CertCompressor::CompressChain(const vector<string>& certs, | 449 string CertCompressor::CompressChain(const vector<string>& certs, |
452 StringPiece client_common_set_hashes, | 450 StringPiece client_common_set_hashes, |
453 StringPiece client_cached, | 451 StringPiece client_cached_cert_hashes, |
454 CommonCertSet* common_set) { | 452 const CommonCertSets* common_set) { |
455 const vector<CertEntry> entries = MatchCerts( | 453 const vector<CertEntry> entries = MatchCerts( |
456 certs, client_common_set_hashes, client_cached, common_set); | 454 certs, client_common_set_hashes, client_cached_cert_hashes, common_set); |
457 DCHECK_EQ(entries.size(), certs.size()); | 455 DCHECK_EQ(entries.size(), certs.size()); |
458 | 456 |
459 size_t uncompressed_size = 0; | 457 size_t uncompressed_size = 0; |
460 for (size_t i = 0; i < entries.size(); i++) { | 458 for (size_t i = 0; i < entries.size(); i++) { |
461 if (entries[i].type == CertEntry::COMPRESSED) { | 459 if (entries[i].type == CertEntry::COMPRESSED) { |
462 uncompressed_size += 4 /* uint32 length */ + certs[i].size(); | 460 uncompressed_size += 4 /* uint32 length */ + certs[i].size(); |
463 } | 461 } |
464 } | 462 } |
465 | 463 |
466 size_t compressed_size = 0; | 464 size_t compressed_size = 0; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
544 return ""; | 542 return ""; |
545 } | 543 } |
546 | 544 |
547 result.resize(result.size() - z.avail_out); | 545 result.resize(result.size() - z.avail_out); |
548 return result; | 546 return result; |
549 } | 547 } |
550 | 548 |
551 // static | 549 // static |
552 bool CertCompressor::DecompressChain(StringPiece in, | 550 bool CertCompressor::DecompressChain(StringPiece in, |
553 const vector<string>& cached_certs, | 551 const vector<string>& cached_certs, |
554 CommonCertSet* common_set, | 552 const CommonCertSets* common_set, |
555 vector<string>* out_certs) { | 553 vector<string>* out_certs) { |
556 vector<CertEntry> entries; | 554 vector<CertEntry> entries; |
557 if (!ParseEntries(&in, cached_certs, common_set, &entries, out_certs)) { | 555 if (!ParseEntries(&in, cached_certs, common_set, &entries, out_certs)) { |
558 return false; | 556 return false; |
559 } | 557 } |
560 DCHECK_EQ(entries.size(), out_certs->size()); | 558 DCHECK_EQ(entries.size(), out_certs->size()); |
561 | 559 |
562 scoped_ptr<uint8[]> uncompressed_data; | 560 scoped_ptr<uint8[]> uncompressed_data; |
563 StringPiece uncompressed; | 561 StringPiece uncompressed; |
564 | 562 |
(...skipping 28 matching lines...) Expand all Loading... |
593 int rv = inflate(&z, Z_FINISH); | 591 int rv = inflate(&z, Z_FINISH); |
594 if (rv == Z_NEED_DICT) { | 592 if (rv == Z_NEED_DICT) { |
595 string zlib_dict = ZlibDictForEntries(entries, *out_certs); | 593 string zlib_dict = ZlibDictForEntries(entries, *out_certs); |
596 const uint8* dict = reinterpret_cast<const uint8*>(zlib_dict.data()); | 594 const uint8* dict = reinterpret_cast<const uint8*>(zlib_dict.data()); |
597 if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) { | 595 if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) { |
598 return false; | 596 return false; |
599 } | 597 } |
600 rv = inflate(&z, Z_FINISH); | 598 rv = inflate(&z, Z_FINISH); |
601 } | 599 } |
602 | 600 |
603 if (Z_STREAM_END != rv || | 601 if (Z_STREAM_END != rv || z.avail_out > 0 || z.avail_in > 0) { |
604 z.avail_out > 0 || | |
605 z.avail_in > 0) { | |
606 return false; | 602 return false; |
607 } | 603 } |
608 | 604 |
609 uncompressed = StringPiece(reinterpret_cast<char*>(uncompressed_data.get()), | 605 uncompressed = StringPiece(reinterpret_cast<char*>(uncompressed_data.get()), |
610 uncompressed_size); | 606 uncompressed_size); |
611 } | 607 } |
612 | 608 |
613 for (size_t i = 0; i < entries.size(); i++) { | 609 for (size_t i = 0; i < entries.size(); i++) { |
614 switch (entries[i].type) { | 610 switch (entries[i].type) { |
615 case CertEntry::COMPRESSED: | 611 case CertEntry::COMPRESSED: |
(...skipping 16 matching lines...) Expand all Loading... |
632 } | 628 } |
633 | 629 |
634 if (!uncompressed.empty()) { | 630 if (!uncompressed.empty()) { |
635 return false; | 631 return false; |
636 } | 632 } |
637 | 633 |
638 return true; | 634 return true; |
639 } | 635 } |
640 | 636 |
641 } // namespace net | 637 } // namespace net |
OLD | NEW |