OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/quic/crypto/quic_crypto_client_config.h" | |
6 | |
7 #include "base/metrics/histogram.h" | |
8 #include "base/metrics/sparse_histogram.h" | |
9 #include "base/profiler/scoped_tracker.h" | |
10 #include "base/stl_util.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "net/quic/crypto/cert_compressor.h" | |
13 #include "net/quic/crypto/chacha20_poly1305_encrypter.h" | |
14 #include "net/quic/crypto/channel_id.h" | |
15 #include "net/quic/crypto/common_cert_set.h" | |
16 #include "net/quic/crypto/crypto_framer.h" | |
17 #include "net/quic/crypto/crypto_utils.h" | |
18 #include "net/quic/crypto/curve25519_key_exchange.h" | |
19 #include "net/quic/crypto/key_exchange.h" | |
20 #include "net/quic/crypto/p256_key_exchange.h" | |
21 #include "net/quic/crypto/proof_verifier.h" | |
22 #include "net/quic/crypto/quic_encrypter.h" | |
23 #include "net/quic/quic_utils.h" | |
24 | |
25 using base::StringPiece; | |
26 using std::map; | |
27 using std::string; | |
28 using std::vector; | |
29 | |
30 namespace net { | |
31 | |
32 namespace { | |
33 | |
34 // Tracks the reason (the state of the server config) for sending inchoate | |
35 // ClientHello to the server. | |
36 void RecordInchoateClientHelloReason( | |
37 QuicCryptoClientConfig::CachedState::ServerConfigState state) { | |
38 UMA_HISTOGRAM_ENUMERATION( | |
39 "Net.QuicInchoateClientHelloReason", state, | |
40 QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT); | |
41 } | |
42 | |
43 // Tracks the state of the QUIC server information loaded from the disk cache. | |
44 void RecordDiskCacheServerConfigState( | |
45 QuicCryptoClientConfig::CachedState::ServerConfigState state) { | |
46 UMA_HISTOGRAM_ENUMERATION( | |
47 "Net.QuicServerInfo.DiskCacheState", state, | |
48 QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT); | |
49 } | |
50 | |
51 } // namespace | |
52 | |
53 QuicCryptoClientConfig::QuicCryptoClientConfig() | |
54 : disable_ecdsa_(false) { | |
55 SetDefaults(); | |
56 } | |
57 | |
58 QuicCryptoClientConfig::~QuicCryptoClientConfig() { | |
59 STLDeleteValues(&cached_states_); | |
60 } | |
61 | |
62 QuicCryptoClientConfig::CachedState::CachedState() | |
63 : server_config_valid_(false), | |
64 generation_counter_(0) {} | |
65 | |
66 QuicCryptoClientConfig::CachedState::~CachedState() {} | |
67 | |
68 bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { | |
69 if (server_config_.empty()) { | |
70 RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY); | |
71 return false; | |
72 } | |
73 | |
74 if (!server_config_valid_) { | |
75 RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID); | |
76 return false; | |
77 } | |
78 | |
79 const CryptoHandshakeMessage* scfg = GetServerConfig(); | |
80 if (!scfg) { | |
81 // Should be impossible short of cache corruption. | |
82 DCHECK(false); | |
83 RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED); | |
84 return false; | |
85 } | |
86 | |
87 uint64 expiry_seconds; | |
88 if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { | |
89 RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID_EXPIRY); | |
90 return false; | |
91 } | |
92 if (now.ToUNIXSeconds() >= expiry_seconds) { | |
93 UMA_HISTOGRAM_CUSTOM_TIMES( | |
94 "Net.QuicClientHelloServerConfig.InvalidDuration", | |
95 base::TimeDelta::FromSeconds(now.ToUNIXSeconds() - expiry_seconds), | |
96 base::TimeDelta::FromMinutes(1), base::TimeDelta::FromDays(20), 50); | |
97 RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED); | |
98 return false; | |
99 } | |
100 | |
101 return true; | |
102 } | |
103 | |
104 bool QuicCryptoClientConfig::CachedState::IsEmpty() const { | |
105 return server_config_.empty(); | |
106 } | |
107 | |
108 const CryptoHandshakeMessage* | |
109 QuicCryptoClientConfig::CachedState::GetServerConfig() const { | |
110 if (server_config_.empty()) { | |
111 return nullptr; | |
112 } | |
113 | |
114 if (!scfg_.get()) { | |
115 scfg_.reset(CryptoFramer::ParseMessage(server_config_)); | |
116 DCHECK(scfg_.get()); | |
117 } | |
118 return scfg_.get(); | |
119 } | |
120 | |
121 QuicCryptoClientConfig::CachedState::ServerConfigState | |
122 QuicCryptoClientConfig::CachedState::SetServerConfig( | |
123 StringPiece server_config, QuicWallTime now, string* error_details) { | |
124 const bool matches_existing = server_config == server_config_; | |
125 | |
126 // Even if the new server config matches the existing one, we still wish to | |
127 // reject it if it has expired. | |
128 scoped_ptr<CryptoHandshakeMessage> new_scfg_storage; | |
129 const CryptoHandshakeMessage* new_scfg; | |
130 | |
131 if (!matches_existing) { | |
132 new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config)); | |
133 new_scfg = new_scfg_storage.get(); | |
134 } else { | |
135 new_scfg = GetServerConfig(); | |
136 } | |
137 | |
138 if (!new_scfg) { | |
139 *error_details = "SCFG invalid"; | |
140 return SERVER_CONFIG_INVALID; | |
141 } | |
142 | |
143 uint64 expiry_seconds; | |
144 if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { | |
145 *error_details = "SCFG missing EXPY"; | |
146 return SERVER_CONFIG_INVALID_EXPIRY; | |
147 } | |
148 | |
149 if (now.ToUNIXSeconds() >= expiry_seconds) { | |
150 *error_details = "SCFG has expired"; | |
151 return SERVER_CONFIG_EXPIRED; | |
152 } | |
153 | |
154 if (!matches_existing) { | |
155 server_config_ = server_config.as_string(); | |
156 SetProofInvalid(); | |
157 scfg_.reset(new_scfg_storage.release()); | |
158 } | |
159 return SERVER_CONFIG_VALID; | |
160 } | |
161 | |
162 void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { | |
163 server_config_.clear(); | |
164 scfg_.reset(); | |
165 SetProofInvalid(); | |
166 } | |
167 | |
168 void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, | |
169 StringPiece signature) { | |
170 bool has_changed = | |
171 signature != server_config_sig_ || certs_.size() != certs.size(); | |
172 | |
173 if (!has_changed) { | |
174 for (size_t i = 0; i < certs_.size(); i++) { | |
175 if (certs_[i] != certs[i]) { | |
176 has_changed = true; | |
177 break; | |
178 } | |
179 } | |
180 } | |
181 | |
182 if (!has_changed) { | |
183 return; | |
184 } | |
185 | |
186 // If the proof has changed then it needs to be revalidated. | |
187 SetProofInvalid(); | |
188 certs_ = certs; | |
189 server_config_sig_ = signature.as_string(); | |
190 } | |
191 | |
192 void QuicCryptoClientConfig::CachedState::Clear() { | |
193 server_config_.clear(); | |
194 source_address_token_.clear(); | |
195 certs_.clear(); | |
196 server_config_sig_.clear(); | |
197 server_config_valid_ = false; | |
198 proof_verify_details_.reset(); | |
199 scfg_.reset(); | |
200 ++generation_counter_; | |
201 } | |
202 | |
203 void QuicCryptoClientConfig::CachedState::ClearProof() { | |
204 SetProofInvalid(); | |
205 certs_.clear(); | |
206 server_config_sig_.clear(); | |
207 } | |
208 | |
209 void QuicCryptoClientConfig::CachedState::SetProofValid() { | |
210 server_config_valid_ = true; | |
211 } | |
212 | |
213 void QuicCryptoClientConfig::CachedState::SetProofInvalid() { | |
214 server_config_valid_ = false; | |
215 ++generation_counter_; | |
216 } | |
217 | |
218 bool QuicCryptoClientConfig::CachedState::Initialize( | |
219 StringPiece server_config, | |
220 StringPiece source_address_token, | |
221 const vector<string>& certs, | |
222 StringPiece signature, | |
223 QuicWallTime now) { | |
224 DCHECK(server_config_.empty()); | |
225 | |
226 if (server_config.empty()) { | |
227 RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY); | |
228 return false; | |
229 } | |
230 | |
231 string error_details; | |
232 ServerConfigState state = SetServerConfig(server_config, now, | |
233 &error_details); | |
234 RecordDiskCacheServerConfigState(state); | |
235 if (state != SERVER_CONFIG_VALID) { | |
236 DVLOG(1) << "SetServerConfig failed with " << error_details; | |
237 return false; | |
238 } | |
239 | |
240 signature.CopyToString(&server_config_sig_); | |
241 source_address_token.CopyToString(&source_address_token_); | |
242 certs_ = certs; | |
243 return true; | |
244 } | |
245 | |
246 const string& QuicCryptoClientConfig::CachedState::server_config() const { | |
247 return server_config_; | |
248 } | |
249 | |
250 const string& | |
251 QuicCryptoClientConfig::CachedState::source_address_token() const { | |
252 return source_address_token_; | |
253 } | |
254 | |
255 const vector<string>& QuicCryptoClientConfig::CachedState::certs() const { | |
256 return certs_; | |
257 } | |
258 | |
259 const string& QuicCryptoClientConfig::CachedState::signature() const { | |
260 return server_config_sig_; | |
261 } | |
262 | |
263 bool QuicCryptoClientConfig::CachedState::proof_valid() const { | |
264 return server_config_valid_; | |
265 } | |
266 | |
267 uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { | |
268 return generation_counter_; | |
269 } | |
270 | |
271 const ProofVerifyDetails* | |
272 QuicCryptoClientConfig::CachedState::proof_verify_details() const { | |
273 return proof_verify_details_.get(); | |
274 } | |
275 | |
276 void QuicCryptoClientConfig::CachedState::set_source_address_token( | |
277 StringPiece token) { | |
278 source_address_token_ = token.as_string(); | |
279 } | |
280 | |
281 void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( | |
282 ProofVerifyDetails* details) { | |
283 proof_verify_details_.reset(details); | |
284 } | |
285 | |
286 void QuicCryptoClientConfig::CachedState::InitializeFrom( | |
287 const QuicCryptoClientConfig::CachedState& other) { | |
288 DCHECK(server_config_.empty()); | |
289 DCHECK(!server_config_valid_); | |
290 server_config_ = other.server_config_; | |
291 source_address_token_ = other.source_address_token_; | |
292 certs_ = other.certs_; | |
293 server_config_sig_ = other.server_config_sig_; | |
294 server_config_valid_ = other.server_config_valid_; | |
295 if (other.proof_verify_details_.get() != nullptr) { | |
296 proof_verify_details_.reset(other.proof_verify_details_->Clone()); | |
297 } | |
298 ++generation_counter_; | |
299 } | |
300 | |
301 void QuicCryptoClientConfig::SetDefaults() { | |
302 // Key exchange methods. | |
303 kexs.resize(2); | |
304 kexs[0] = kC255; | |
305 kexs[1] = kP256; | |
306 | |
307 // Authenticated encryption algorithms. Prefer ChaCha20 by default. | |
308 aead.clear(); | |
309 if (ChaCha20Poly1305Encrypter::IsSupported()) { | |
310 aead.push_back(kCC12); | |
311 } | |
312 aead.push_back(kAESG); | |
313 | |
314 disable_ecdsa_ = false; | |
315 } | |
316 | |
317 QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( | |
318 const QuicServerId& server_id) { | |
319 CachedStateMap::const_iterator it = cached_states_.find(server_id); | |
320 if (it != cached_states_.end()) { | |
321 return it->second; | |
322 } | |
323 | |
324 CachedState* cached = new CachedState; | |
325 cached_states_.insert(std::make_pair(server_id, cached)); | |
326 bool cache_populated = PopulateFromCanonicalConfig(server_id, cached); | |
327 UMA_HISTOGRAM_BOOLEAN( | |
328 "Net.QuicCryptoClientConfig.PopulatedFromCanonicalConfig", | |
329 cache_populated); | |
330 return cached; | |
331 } | |
332 | |
333 void QuicCryptoClientConfig::ClearCachedStates() { | |
334 for (CachedStateMap::const_iterator it = cached_states_.begin(); | |
335 it != cached_states_.end(); ++it) { | |
336 it->second->Clear(); | |
337 } | |
338 } | |
339 | |
340 void QuicCryptoClientConfig::FillInchoateClientHello( | |
341 const QuicServerId& server_id, | |
342 const QuicVersion preferred_version, | |
343 const CachedState* cached, | |
344 QuicCryptoNegotiatedParameters* out_params, | |
345 CryptoHandshakeMessage* out) const { | |
346 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
347 tracked_objects::ScopedTracker tracking_profile( | |
348 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
349 "422516 QuicCryptoClientConfig::FillInchoateClientHello")); | |
350 | |
351 out->set_tag(kCHLO); | |
352 out->set_minimum_size(kClientHelloMinimumSize); | |
353 | |
354 // Server name indication. We only send SNI if it's a valid domain name, as | |
355 // per the spec. | |
356 if (CryptoUtils::IsValidSNI(server_id.host())) { | |
357 out->SetStringPiece(kSNI, server_id.host()); | |
358 } | |
359 out->SetValue(kVER, QuicVersionToQuicTag(preferred_version)); | |
360 | |
361 if (!user_agent_id_.empty()) { | |
362 out->SetStringPiece(kUAID, user_agent_id_); | |
363 } | |
364 | |
365 if (!cached->source_address_token().empty()) { | |
366 out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); | |
367 } | |
368 | |
369 if (server_id.is_https()) { | |
370 if (disable_ecdsa_) { | |
371 out->SetTaglist(kPDMD, kX59R, 0); | |
372 } else { | |
373 out->SetTaglist(kPDMD, kX509, 0); | |
374 } | |
375 } | |
376 | |
377 if (common_cert_sets) { | |
378 out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); | |
379 } | |
380 | |
381 const vector<string>& certs = cached->certs(); | |
382 // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the | |
383 // client config is being used for multiple connections, another connection | |
384 // doesn't update the cached certificates and cause us to be unable to | |
385 // process the server's compressed certificate chain. | |
386 out_params->cached_certs = certs; | |
387 if (!certs.empty()) { | |
388 vector<uint64> hashes; | |
389 hashes.reserve(certs.size()); | |
390 for (vector<string>::const_iterator i = certs.begin(); | |
391 i != certs.end(); ++i) { | |
392 hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); | |
393 } | |
394 out->SetVector(kCCRT, hashes); | |
395 } | |
396 } | |
397 | |
398 QuicErrorCode QuicCryptoClientConfig::FillClientHello( | |
399 const QuicServerId& server_id, | |
400 QuicConnectionId connection_id, | |
401 const QuicVersion preferred_version, | |
402 const CachedState* cached, | |
403 QuicWallTime now, | |
404 QuicRandom* rand, | |
405 const ChannelIDKey* channel_id_key, | |
406 QuicCryptoNegotiatedParameters* out_params, | |
407 CryptoHandshakeMessage* out, | |
408 string* error_details) const { | |
409 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
410 tracked_objects::ScopedTracker tracking_profile( | |
411 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
412 "422516 QuicCryptoClientConfig::FillClientHello")); | |
413 | |
414 DCHECK(error_details != nullptr); | |
415 | |
416 FillInchoateClientHello(server_id, preferred_version, cached, | |
417 out_params, out); | |
418 | |
419 const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); | |
420 if (!scfg) { | |
421 // This should never happen as our caller should have checked | |
422 // cached->IsComplete() before calling this function. | |
423 *error_details = "Handshake not ready"; | |
424 return QUIC_CRYPTO_INTERNAL_ERROR; | |
425 } | |
426 | |
427 StringPiece scid; | |
428 if (!scfg->GetStringPiece(kSCID, &scid)) { | |
429 *error_details = "SCFG missing SCID"; | |
430 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
431 } | |
432 out->SetStringPiece(kSCID, scid); | |
433 | |
434 const QuicTag* their_aeads; | |
435 const QuicTag* their_key_exchanges; | |
436 size_t num_their_aeads, num_their_key_exchanges; | |
437 if (scfg->GetTaglist(kAEAD, &their_aeads, | |
438 &num_their_aeads) != QUIC_NO_ERROR || | |
439 scfg->GetTaglist(kKEXS, &their_key_exchanges, | |
440 &num_their_key_exchanges) != QUIC_NO_ERROR) { | |
441 *error_details = "Missing AEAD or KEXS"; | |
442 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
443 } | |
444 | |
445 // AEAD: the work loads on the client and server are symmetric. Since the | |
446 // client is more likely to be CPU-constrained, break the tie by favoring | |
447 // the client's preference. | |
448 // Key exchange: the client does more work than the server, so favor the | |
449 // client's preference. | |
450 size_t key_exchange_index; | |
451 if (!QuicUtils::FindMutualTag( | |
452 aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY, | |
453 &out_params->aead, nullptr) || | |
454 !QuicUtils::FindMutualTag( | |
455 kexs, their_key_exchanges, num_their_key_exchanges, | |
456 QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange, | |
457 &key_exchange_index)) { | |
458 *error_details = "Unsupported AEAD or KEXS"; | |
459 return QUIC_CRYPTO_NO_SUPPORT; | |
460 } | |
461 out->SetTaglist(kAEAD, out_params->aead, 0); | |
462 out->SetTaglist(kKEXS, out_params->key_exchange, 0); | |
463 | |
464 StringPiece public_value; | |
465 if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != | |
466 QUIC_NO_ERROR) { | |
467 *error_details = "Missing public value"; | |
468 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
469 } | |
470 | |
471 StringPiece orbit; | |
472 if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { | |
473 *error_details = "SCFG missing OBIT"; | |
474 return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; | |
475 } | |
476 | |
477 CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); | |
478 out->SetStringPiece(kNONC, out_params->client_nonce); | |
479 if (!out_params->server_nonce.empty()) { | |
480 out->SetStringPiece(kServerNonceTag, out_params->server_nonce); | |
481 } | |
482 | |
483 switch (out_params->key_exchange) { | |
484 case kC255: | |
485 out_params->client_key_exchange.reset(Curve25519KeyExchange::New( | |
486 Curve25519KeyExchange::NewPrivateKey(rand))); | |
487 break; | |
488 case kP256: | |
489 out_params->client_key_exchange.reset(P256KeyExchange::New( | |
490 P256KeyExchange::NewPrivateKey())); | |
491 break; | |
492 default: | |
493 DCHECK(false); | |
494 *error_details = "Configured to support an unknown key exchange"; | |
495 return QUIC_CRYPTO_INTERNAL_ERROR; | |
496 } | |
497 | |
498 if (!out_params->client_key_exchange->CalculateSharedKey( | |
499 public_value, &out_params->initial_premaster_secret)) { | |
500 *error_details = "Key exchange failure"; | |
501 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
502 } | |
503 out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); | |
504 | |
505 if (channel_id_key) { | |
506 // In order to calculate the encryption key for the CETV block we need to | |
507 // serialise the client hello as it currently is (i.e. without the CETV | |
508 // block). For this, the client hello is serialized without padding. | |
509 const size_t orig_min_size = out->minimum_size(); | |
510 out->set_minimum_size(0); | |
511 | |
512 CryptoHandshakeMessage cetv; | |
513 cetv.set_tag(kCETV); | |
514 | |
515 string hkdf_input; | |
516 const QuicData& client_hello_serialized = out->GetSerialized(); | |
517 hkdf_input.append(QuicCryptoConfig::kCETVLabel, | |
518 strlen(QuicCryptoConfig::kCETVLabel) + 1); | |
519 hkdf_input.append(reinterpret_cast<char*>(&connection_id), | |
520 sizeof(connection_id)); | |
521 hkdf_input.append(client_hello_serialized.data(), | |
522 client_hello_serialized.length()); | |
523 hkdf_input.append(cached->server_config()); | |
524 | |
525 string key = channel_id_key->SerializeKey(); | |
526 string signature; | |
527 if (!channel_id_key->Sign(hkdf_input, &signature)) { | |
528 *error_details = "Channel ID signature failed"; | |
529 return QUIC_INVALID_CHANNEL_ID_SIGNATURE; | |
530 } | |
531 | |
532 cetv.SetStringPiece(kCIDK, key); | |
533 cetv.SetStringPiece(kCIDS, signature); | |
534 | |
535 CrypterPair crypters; | |
536 if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, | |
537 out_params->aead, out_params->client_nonce, | |
538 out_params->server_nonce, hkdf_input, | |
539 CryptoUtils::CLIENT, &crypters, | |
540 nullptr /* subkey secret */)) { | |
541 *error_details = "Symmetric key setup failed"; | |
542 return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; | |
543 } | |
544 | |
545 const QuicData& cetv_plaintext = cetv.GetSerialized(); | |
546 const size_t encrypted_len = | |
547 crypters.encrypter->GetCiphertextSize(cetv_plaintext.length()); | |
548 scoped_ptr<char[]> output(new char[encrypted_len]); | |
549 size_t output_size = 0; | |
550 if (!crypters.encrypter->EncryptPacket( | |
551 0 /* sequence number */, StringPiece() /* associated data */, | |
552 cetv_plaintext.AsStringPiece(), output.get(), &output_size, | |
553 encrypted_len)) { | |
554 *error_details = "Packet encryption failed"; | |
555 return QUIC_ENCRYPTION_FAILURE; | |
556 } | |
557 | |
558 out->SetStringPiece(kCETV, StringPiece(output.get(), output_size)); | |
559 out->MarkDirty(); | |
560 | |
561 out->set_minimum_size(orig_min_size); | |
562 } | |
563 | |
564 // Derive the symmetric keys and set up the encrypters and decrypters. | |
565 // Set the following members of out_params: | |
566 // out_params->hkdf_input_suffix | |
567 // out_params->initial_crypters | |
568 out_params->hkdf_input_suffix.clear(); | |
569 out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id), | |
570 sizeof(connection_id)); | |
571 const QuicData& client_hello_serialized = out->GetSerialized(); | |
572 out_params->hkdf_input_suffix.append(client_hello_serialized.data(), | |
573 client_hello_serialized.length()); | |
574 out_params->hkdf_input_suffix.append(cached->server_config()); | |
575 | |
576 string hkdf_input; | |
577 const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; | |
578 hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); | |
579 hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); | |
580 hkdf_input.append(out_params->hkdf_input_suffix); | |
581 | |
582 if (!CryptoUtils::DeriveKeys( | |
583 out_params->initial_premaster_secret, out_params->aead, | |
584 out_params->client_nonce, out_params->server_nonce, hkdf_input, | |
585 CryptoUtils::CLIENT, &out_params->initial_crypters, | |
586 nullptr /* subkey secret */)) { | |
587 *error_details = "Symmetric key setup failed"; | |
588 return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; | |
589 } | |
590 | |
591 return QUIC_NO_ERROR; | |
592 } | |
593 | |
594 QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig( | |
595 const CryptoHandshakeMessage& message, | |
596 QuicWallTime now, | |
597 const vector<string>& cached_certs, | |
598 CachedState* cached, | |
599 string* error_details) { | |
600 DCHECK(error_details != nullptr); | |
601 | |
602 StringPiece scfg; | |
603 if (!message.GetStringPiece(kSCFG, &scfg)) { | |
604 *error_details = "Missing SCFG"; | |
605 return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; | |
606 } | |
607 | |
608 CachedState::ServerConfigState state = cached->SetServerConfig( | |
609 scfg, now, error_details); | |
610 if (state == CachedState::SERVER_CONFIG_EXPIRED) { | |
611 return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; | |
612 } | |
613 // TODO(rtenneti): Return more specific error code than returning | |
614 // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER. | |
615 if (state != CachedState::SERVER_CONFIG_VALID) { | |
616 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
617 } | |
618 | |
619 StringPiece token; | |
620 if (message.GetStringPiece(kSourceAddressTokenTag, &token)) { | |
621 cached->set_source_address_token(token); | |
622 } | |
623 | |
624 StringPiece proof, cert_bytes; | |
625 bool has_proof = message.GetStringPiece(kPROF, &proof); | |
626 bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes); | |
627 if (has_proof && has_cert) { | |
628 vector<string> certs; | |
629 if (!CertCompressor::DecompressChain(cert_bytes, cached_certs, | |
630 common_cert_sets, &certs)) { | |
631 *error_details = "Certificate data invalid"; | |
632 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
633 } | |
634 | |
635 cached->SetProof(certs, proof); | |
636 } else { | |
637 if (proof_verifier() != nullptr) { | |
638 // Secure QUIC: clear existing proof as we have been sent a new SCFG | |
639 // without matching proof/certs. | |
640 cached->ClearProof(); | |
641 } | |
642 | |
643 if (has_proof && !has_cert) { | |
644 *error_details = "Certificate missing"; | |
645 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
646 } | |
647 | |
648 if (!has_proof && has_cert) { | |
649 *error_details = "Proof missing"; | |
650 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
651 } | |
652 } | |
653 | |
654 return QUIC_NO_ERROR; | |
655 } | |
656 | |
657 QuicErrorCode QuicCryptoClientConfig::ProcessRejection( | |
658 const CryptoHandshakeMessage& rej, | |
659 QuicWallTime now, | |
660 CachedState* cached, | |
661 bool is_https, | |
662 QuicCryptoNegotiatedParameters* out_params, | |
663 string* error_details) { | |
664 DCHECK(error_details != nullptr); | |
665 | |
666 if (rej.tag() != kREJ) { | |
667 *error_details = "Message is not REJ"; | |
668 return QUIC_CRYPTO_INTERNAL_ERROR; | |
669 } | |
670 | |
671 QuicErrorCode error = CacheNewServerConfig(rej, now, out_params->cached_certs, | |
672 cached, error_details); | |
673 if (error != QUIC_NO_ERROR) { | |
674 return error; | |
675 } | |
676 | |
677 StringPiece nonce; | |
678 if (rej.GetStringPiece(kServerNonceTag, &nonce)) { | |
679 out_params->server_nonce = nonce.as_string(); | |
680 } | |
681 | |
682 const uint32* reject_reasons; | |
683 size_t num_reject_reasons; | |
684 static_assert(sizeof(QuicTag) == sizeof(uint32), "header out of sync"); | |
685 if (rej.GetTaglist(kRREJ, &reject_reasons, | |
686 &num_reject_reasons) == QUIC_NO_ERROR) { | |
687 uint32 packed_error = 0; | |
688 for (size_t i = 0; i < num_reject_reasons; ++i) { | |
689 // HANDSHAKE_OK is 0 and don't report that as error. | |
690 if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) { | |
691 continue; | |
692 } | |
693 HandshakeFailureReason reason = | |
694 static_cast<HandshakeFailureReason>(reject_reasons[i]); | |
695 packed_error |= 1 << (reason - 1); | |
696 } | |
697 DVLOG(1) << "Reasons for rejection: " << packed_error; | |
698 if (is_https) { | |
699 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Secure", | |
700 packed_error); | |
701 } else { | |
702 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Insecure", | |
703 packed_error); | |
704 } | |
705 } | |
706 | |
707 return QUIC_NO_ERROR; | |
708 } | |
709 | |
710 QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( | |
711 const CryptoHandshakeMessage& server_hello, | |
712 QuicConnectionId connection_id, | |
713 const QuicVersionVector& negotiated_versions, | |
714 CachedState* cached, | |
715 QuicCryptoNegotiatedParameters* out_params, | |
716 string* error_details) { | |
717 DCHECK(error_details != nullptr); | |
718 | |
719 if (server_hello.tag() != kSHLO) { | |
720 *error_details = "Bad tag"; | |
721 return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; | |
722 } | |
723 | |
724 const QuicTag* supported_version_tags; | |
725 size_t num_supported_versions; | |
726 | |
727 if (server_hello.GetTaglist(kVER, &supported_version_tags, | |
728 &num_supported_versions) != QUIC_NO_ERROR) { | |
729 *error_details = "server hello missing version list"; | |
730 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
731 } | |
732 if (!negotiated_versions.empty()) { | |
733 bool mismatch = num_supported_versions != negotiated_versions.size(); | |
734 for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) { | |
735 mismatch = QuicTagToQuicVersion(supported_version_tags[i]) != | |
736 negotiated_versions[i]; | |
737 } | |
738 // The server sent a list of supported versions, and the connection | |
739 // reports that there was a version negotiation during the handshake. | |
740 // Ensure that these two lists are identical. | |
741 if (mismatch) { | |
742 *error_details = "Downgrade attack detected"; | |
743 return QUIC_VERSION_NEGOTIATION_MISMATCH; | |
744 } | |
745 } | |
746 | |
747 // Learn about updated source address tokens. | |
748 StringPiece token; | |
749 if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { | |
750 cached->set_source_address_token(token); | |
751 } | |
752 | |
753 // TODO(agl): | |
754 // learn about updated SCFGs. | |
755 | |
756 StringPiece public_value; | |
757 if (!server_hello.GetStringPiece(kPUBS, &public_value)) { | |
758 *error_details = "server hello missing forward secure public value"; | |
759 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
760 } | |
761 | |
762 if (!out_params->client_key_exchange->CalculateSharedKey( | |
763 public_value, &out_params->forward_secure_premaster_secret)) { | |
764 *error_details = "Key exchange failure"; | |
765 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; | |
766 } | |
767 | |
768 string hkdf_input; | |
769 const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; | |
770 hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); | |
771 hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); | |
772 hkdf_input.append(out_params->hkdf_input_suffix); | |
773 | |
774 if (!CryptoUtils::DeriveKeys( | |
775 out_params->forward_secure_premaster_secret, out_params->aead, | |
776 out_params->client_nonce, out_params->server_nonce, hkdf_input, | |
777 CryptoUtils::CLIENT, &out_params->forward_secure_crypters, | |
778 &out_params->subkey_secret)) { | |
779 *error_details = "Symmetric key setup failed"; | |
780 return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; | |
781 } | |
782 | |
783 return QUIC_NO_ERROR; | |
784 } | |
785 | |
786 QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate( | |
787 const CryptoHandshakeMessage& server_config_update, | |
788 QuicWallTime now, | |
789 CachedState* cached, | |
790 QuicCryptoNegotiatedParameters* out_params, | |
791 string* error_details) { | |
792 DCHECK(error_details != nullptr); | |
793 | |
794 if (server_config_update.tag() != kSCUP) { | |
795 *error_details = "ServerConfigUpdate must have kSCUP tag."; | |
796 return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; | |
797 } | |
798 | |
799 return CacheNewServerConfig(server_config_update, now, | |
800 out_params->cached_certs, cached, error_details); | |
801 } | |
802 | |
803 ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { | |
804 return proof_verifier_.get(); | |
805 } | |
806 | |
807 void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { | |
808 proof_verifier_.reset(verifier); | |
809 } | |
810 | |
811 ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const { | |
812 return channel_id_source_.get(); | |
813 } | |
814 | |
815 void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) { | |
816 channel_id_source_.reset(source); | |
817 } | |
818 | |
819 void QuicCryptoClientConfig::InitializeFrom( | |
820 const QuicServerId& server_id, | |
821 const QuicServerId& canonical_server_id, | |
822 QuicCryptoClientConfig* canonical_crypto_config) { | |
823 CachedState* canonical_cached = | |
824 canonical_crypto_config->LookupOrCreate(canonical_server_id); | |
825 if (!canonical_cached->proof_valid()) { | |
826 return; | |
827 } | |
828 CachedState* cached = LookupOrCreate(server_id); | |
829 cached->InitializeFrom(*canonical_cached); | |
830 } | |
831 | |
832 void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) { | |
833 canonical_suffixes_.push_back(suffix); | |
834 } | |
835 | |
836 void QuicCryptoClientConfig::PreferAesGcm() { | |
837 DCHECK(!aead.empty()); | |
838 if (aead.size() <= 1) { | |
839 return; | |
840 } | |
841 QuicTagVector::iterator pos = std::find(aead.begin(), aead.end(), kAESG); | |
842 if (pos != aead.end()) { | |
843 aead.erase(pos); | |
844 aead.insert(aead.begin(), kAESG); | |
845 } | |
846 } | |
847 | |
848 void QuicCryptoClientConfig::DisableEcdsa() { | |
849 disable_ecdsa_ = true; | |
850 } | |
851 | |
852 bool QuicCryptoClientConfig::PopulateFromCanonicalConfig( | |
853 const QuicServerId& server_id, | |
854 CachedState* server_state) { | |
855 DCHECK(server_state->IsEmpty()); | |
856 size_t i = 0; | |
857 for (; i < canonical_suffixes_.size(); ++i) { | |
858 if (EndsWith(server_id.host(), canonical_suffixes_[i], false)) { | |
859 break; | |
860 } | |
861 } | |
862 if (i == canonical_suffixes_.size()) { | |
863 return false; | |
864 } | |
865 | |
866 QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(), | |
867 server_id.is_https(), | |
868 server_id.privacy_mode()); | |
869 if (!ContainsKey(canonical_server_map_, suffix_server_id)) { | |
870 // This is the first host we've seen which matches the suffix, so make it | |
871 // canonical. | |
872 canonical_server_map_[suffix_server_id] = server_id; | |
873 return false; | |
874 } | |
875 | |
876 const QuicServerId& canonical_server_id = | |
877 canonical_server_map_[suffix_server_id]; | |
878 CachedState* canonical_state = cached_states_[canonical_server_id]; | |
879 if (!canonical_state->proof_valid()) { | |
880 return false; | |
881 } | |
882 | |
883 // Update canonical version to point at the "most recent" entry. | |
884 canonical_server_map_[suffix_server_id] = server_id; | |
885 | |
886 server_state->InitializeFrom(*canonical_state); | |
887 return true; | |
888 } | |
889 | |
890 } // namespace net | |
OLD | NEW |