| 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 |