1 // Copyright (c) 2012 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.
5 #include "net/quic/quic_crypto_client_stream.h"
7 #include "base/metrics/histogram.h"
8 #include "net/quic/crypto/crypto_protocol.h"
9 #include "net/quic/crypto/crypto_utils.h"
10 #include "net/quic/crypto/null_encrypter.h"
11 #include "net/quic/quic_client_session_base.h"
12 #include "net/quic/quic_protocol.h"
13 #include "net/quic/quic_session.h"
19 QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
20 ChannelIDSourceCallbackImpl(QuicCryptoClientStream
* stream
)
23 QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
24 ~ChannelIDSourceCallbackImpl() {}
26 void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Run(
27 scoped_ptr
<ChannelIDKey
>* channel_id_key
) {
28 if (stream_
== nullptr) {
32 stream_
->channel_id_key_
.reset(channel_id_key
->release());
33 stream_
->channel_id_source_callback_run_
= true;
34 stream_
->channel_id_source_callback_
= nullptr;
35 stream_
->DoHandshakeLoop(nullptr);
37 // The ChannelIDSource owns this object and will delete it when this method
41 void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Cancel() {
45 QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
46 QuicCryptoClientStream
* stream
)
49 QuicCryptoClientStream::ProofVerifierCallbackImpl::
50 ~ProofVerifierCallbackImpl() {}
52 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
54 const string
& error_details
,
55 scoped_ptr
<ProofVerifyDetails
>* details
) {
56 if (stream_
== nullptr) {
60 stream_
->verify_ok_
= ok
;
61 stream_
->verify_error_details_
= error_details
;
62 stream_
->verify_details_
.reset(details
->release());
63 stream_
->proof_verify_callback_
= nullptr;
64 stream_
->DoHandshakeLoop(nullptr);
66 // The ProofVerifier owns this object and will delete it when this method
70 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
74 QuicCryptoClientStream::QuicCryptoClientStream(
75 const QuicServerId
& server_id
,
76 QuicClientSessionBase
* session
,
77 ProofVerifyContext
* verify_context
,
78 QuicCryptoClientConfig
* crypto_config
)
79 : QuicCryptoStream(session
),
80 next_state_(STATE_IDLE
),
81 num_client_hellos_(0),
82 crypto_config_(crypto_config
),
83 server_id_(server_id
),
84 generation_counter_(0),
85 channel_id_sent_(false),
86 channel_id_source_callback_run_(false),
87 channel_id_source_callback_(nullptr),
88 verify_context_(verify_context
),
89 proof_verify_callback_(nullptr) {}
91 QuicCryptoClientStream::~QuicCryptoClientStream() {
92 if (channel_id_source_callback_
) {
93 channel_id_source_callback_
->Cancel();
95 if (proof_verify_callback_
) {
96 proof_verify_callback_
->Cancel();
100 void QuicCryptoClientStream::OnHandshakeMessage(
101 const CryptoHandshakeMessage
& message
) {
102 QuicCryptoStream::OnHandshakeMessage(message
);
104 if (message
.tag() == kSCUP
) {
105 if (!handshake_confirmed()) {
106 CloseConnection(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE
);
110 // |message| is an update from the server, so we treat it differently from a
111 // handshake message.
112 HandleServerConfigUpdateMessage(message
);
116 // Do not process handshake messages after the handshake is confirmed.
117 if (handshake_confirmed()) {
118 CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE
);
122 DoHandshakeLoop(&message
);
125 bool QuicCryptoClientStream::CryptoConnect() {
126 next_state_
= STATE_INITIALIZE
;
127 DoHandshakeLoop(nullptr);
131 int QuicCryptoClientStream::num_sent_client_hellos() const {
132 return num_client_hellos_
;
135 bool QuicCryptoClientStream::WasChannelIDSent() const {
136 return channel_id_sent_
;
139 bool QuicCryptoClientStream::WasChannelIDSourceCallbackRun() const {
140 return channel_id_source_callback_run_
;
143 void QuicCryptoClientStream::HandleServerConfigUpdateMessage(
144 const CryptoHandshakeMessage
& server_config_update
) {
145 DCHECK(server_config_update
.tag() == kSCUP
);
146 string error_details
;
147 QuicCryptoClientConfig::CachedState
* cached
=
148 crypto_config_
->LookupOrCreate(server_id_
);
149 QuicErrorCode error
= crypto_config_
->ProcessServerConfigUpdate(
150 server_config_update
,
151 session()->connection()->clock()->WallNow(),
153 &crypto_negotiated_params_
,
156 if (error
!= QUIC_NO_ERROR
) {
157 CloseConnectionWithDetails(
158 error
, "Server config update invalid: " + error_details
);
162 DCHECK(handshake_confirmed());
163 if (proof_verify_callback_
) {
164 proof_verify_callback_
->Cancel();
166 next_state_
= STATE_INITIALIZE_SCUP
;
167 DoHandshakeLoop(nullptr);
170 // kMaxClientHellos is the maximum number of times that we'll send a client
171 // hello. The value 3 accounts for:
172 // * One failure due to an incorrect or missing source-address token.
173 // * One failure due the server's certificate chain being unavailible and the
174 // server being unwilling to send it without a valid source-address token.
175 static const int kMaxClientHellos
= 3;
177 void QuicCryptoClientStream::DoHandshakeLoop(
178 const CryptoHandshakeMessage
* in
) {
179 QuicCryptoClientConfig::CachedState
* cached
=
180 crypto_config_
->LookupOrCreate(server_id_
);
182 QuicAsyncStatus rv
= QUIC_SUCCESS
;
184 CHECK_NE(STATE_NONE
, next_state_
);
185 const State state
= next_state_
;
186 next_state_
= STATE_IDLE
;
189 case STATE_INITIALIZE
:
190 DoInitialize(cached
);
192 case STATE_SEND_CHLO
:
193 DoSendCHLO(in
, cached
);
196 DoReceiveREJ(in
, cached
);
198 case STATE_VERIFY_PROOF
:
199 rv
= DoVerifyProof(cached
);
201 case STATE_VERIFY_PROOF_COMPLETE
:
202 DoVerifyProofComplete(cached
);
204 case STATE_GET_CHANNEL_ID
:
205 rv
= DoGetChannelID(cached
);
207 case STATE_GET_CHANNEL_ID_COMPLETE
:
208 DoGetChannelIDComplete();
210 case STATE_RECV_SHLO
:
211 DoReceiveSHLO(in
, cached
);
214 // This means that the peer sent us a message that we weren't expecting.
215 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
);
217 case STATE_INITIALIZE_SCUP
:
218 DoInitializeServerConfigUpdate(cached
);
222 return; // We are done.
224 } while (rv
!= QUIC_PENDING
&& next_state_
!= STATE_NONE
);
227 void QuicCryptoClientStream::DoInitialize(
228 QuicCryptoClientConfig::CachedState
* cached
) {
229 if (!cached
->IsEmpty() && !cached
->signature().empty() &&
230 server_id_
.is_https()) {
231 // Note that we verify the proof even if the cached proof is valid.
232 // This allows us to respond to CA trust changes or certificate
233 // expiration because it may have been a while since we last verified
235 DCHECK(crypto_config_
->proof_verifier());
236 // If the cached state needs to be verified, do it now.
237 next_state_
= STATE_VERIFY_PROOF
;
239 next_state_
= STATE_GET_CHANNEL_ID
;
243 void QuicCryptoClientStream::DoSendCHLO(
244 const CryptoHandshakeMessage
* in
,
245 QuicCryptoClientConfig::CachedState
* cached
) {
246 // Send the client hello in plaintext.
247 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE
);
248 if (num_client_hellos_
> kMaxClientHellos
) {
249 CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS
);
252 num_client_hellos_
++;
254 CryptoHandshakeMessage out
;
255 if (!cached
->IsComplete(session()->connection()->clock()->WallNow())) {
256 crypto_config_
->FillInchoateClientHello(
258 session()->connection()->supported_versions().front(),
259 cached
, &crypto_negotiated_params_
, &out
);
260 // Pad the inchoate client hello to fill up a packet.
261 const QuicByteCount kFramingOverhead
= 50; // A rough estimate.
262 const QuicByteCount max_packet_size
=
263 session()->connection()->max_packet_length();
264 if (max_packet_size
<= kFramingOverhead
) {
265 DLOG(DFATAL
) << "max_packet_length (" << max_packet_size
266 << ") has no room for framing overhead.";
267 CloseConnection(QUIC_INTERNAL_ERROR
);
270 if (kClientHelloMinimumSize
> max_packet_size
- kFramingOverhead
) {
271 DLOG(DFATAL
) << "Client hello won't fit in a single packet.";
272 CloseConnection(QUIC_INTERNAL_ERROR
);
275 out
.set_minimum_size(
276 static_cast<size_t>(max_packet_size
- kFramingOverhead
));
277 next_state_
= STATE_RECV_REJ
;
278 SendHandshakeMessage(out
);
282 session()->config()->ToHandshakeMessage(&out
);
283 string error_details
;
284 QuicErrorCode error
= crypto_config_
->FillClientHello(
286 session()->connection()->connection_id(),
287 session()->connection()->supported_versions().front(),
289 session()->connection()->clock()->WallNow(),
290 session()->connection()->random_generator(),
291 channel_id_key_
.get(),
292 &crypto_negotiated_params_
,
295 if (error
!= QUIC_NO_ERROR
) {
296 // Flush the cached config so that, if it's bad, the server has a
297 // chance to send us another in the future.
298 cached
->InvalidateServerConfig();
299 CloseConnectionWithDetails(error
, error_details
);
302 channel_id_sent_
= (channel_id_key_
.get() != nullptr);
303 if (cached
->proof_verify_details()) {
304 client_session()->OnProofVerifyDetailsAvailable(
305 *cached
->proof_verify_details());
307 next_state_
= STATE_RECV_SHLO
;
308 SendHandshakeMessage(out
);
309 // Be prepared to decrypt with the new server write key.
310 session()->connection()->SetAlternativeDecrypter(
311 crypto_negotiated_params_
.initial_crypters
.decrypter
.release(),
313 true /* latch once used */);
314 // Send subsequent packets under encryption on the assumption that the
315 // server will accept the handshake.
316 session()->connection()->SetEncrypter(
318 crypto_negotiated_params_
.initial_crypters
.encrypter
.release());
319 session()->connection()->SetDefaultEncryptionLevel(
321 if (!encryption_established_
) {
322 encryption_established_
= true;
323 session()->OnCryptoHandshakeEvent(
324 QuicSession::ENCRYPTION_FIRST_ESTABLISHED
);
326 session()->OnCryptoHandshakeEvent(
327 QuicSession::ENCRYPTION_REESTABLISHED
);
331 void QuicCryptoClientStream::DoReceiveREJ(
332 const CryptoHandshakeMessage
* in
,
333 QuicCryptoClientConfig::CachedState
* cached
) {
334 // We sent a dummy CHLO because we didn't have enough information to
335 // perform a handshake, or we sent a full hello that the server
336 // rejected. Here we hope to have a REJ that contains the information
338 if (in
->tag() != kREJ
) {
339 next_state_
= STATE_NONE
;
340 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
344 string error_details
;
345 QuicErrorCode error
= crypto_config_
->ProcessRejection(
346 *in
, session()->connection()->clock()->WallNow(), cached
,
347 server_id_
.is_https(), &crypto_negotiated_params_
, &error_details
);
348 if (error
!= QUIC_NO_ERROR
) {
349 next_state_
= STATE_NONE
;
350 CloseConnectionWithDetails(error
, error_details
);
353 if (!cached
->proof_valid()) {
354 if (!server_id_
.is_https()) {
355 // We don't check the certificates for insecure QUIC connections.
356 SetCachedProofValid(cached
);
357 } else if (!cached
->signature().empty()) {
358 // Note that we only verify the proof if the cached proof is not
359 // valid. If the cached proof is valid here, someone else must have
360 // just added the server config to the cache and verified the proof,
361 // so we can assume no CA trust changes or certificate expiration
362 // has happened since then.
363 next_state_
= STATE_VERIFY_PROOF
;
367 next_state_
= STATE_GET_CHANNEL_ID
;
370 QuicAsyncStatus
QuicCryptoClientStream::DoVerifyProof(
371 QuicCryptoClientConfig::CachedState
* cached
) {
372 ProofVerifier
* verifier
= crypto_config_
->proof_verifier();
374 next_state_
= STATE_VERIFY_PROOF_COMPLETE
;
375 generation_counter_
= cached
->generation_counter();
377 ProofVerifierCallbackImpl
* proof_verify_callback
=
378 new ProofVerifierCallbackImpl(this);
382 QuicAsyncStatus status
= verifier
->VerifyProof(
384 cached
->server_config(),
387 verify_context_
.get(),
388 &verify_error_details_
,
390 proof_verify_callback
);
394 proof_verify_callback_
= proof_verify_callback
;
395 DVLOG(1) << "Doing VerifyProof";
398 delete proof_verify_callback
;
401 delete proof_verify_callback
;
408 void QuicCryptoClientStream::DoVerifyProofComplete(
409 QuicCryptoClientConfig::CachedState
* cached
) {
411 next_state_
= STATE_NONE
;
412 client_session()->OnProofVerifyDetailsAvailable(*verify_details_
);
413 UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed",
414 handshake_confirmed());
415 CloseConnectionWithDetails(
416 QUIC_PROOF_INVALID
, "Proof invalid: " + verify_error_details_
);
420 // Check if generation_counter has changed between STATE_VERIFY_PROOF and
421 // STATE_VERIFY_PROOF_COMPLETE state changes.
422 if (generation_counter_
!= cached
->generation_counter()) {
423 next_state_
= STATE_VERIFY_PROOF
;
425 SetCachedProofValid(cached
);
426 cached
->SetProofVerifyDetails(verify_details_
.release());
427 if (!handshake_confirmed()) {
428 next_state_
= STATE_GET_CHANNEL_ID
;
430 next_state_
= STATE_NONE
;
435 QuicAsyncStatus
QuicCryptoClientStream::DoGetChannelID(
436 QuicCryptoClientConfig::CachedState
* cached
) {
437 next_state_
= STATE_GET_CHANNEL_ID_COMPLETE
;
438 channel_id_key_
.reset();
439 if (!RequiresChannelID(cached
)) {
440 next_state_
= STATE_SEND_CHLO
;
444 ChannelIDSourceCallbackImpl
* channel_id_source_callback
=
445 new ChannelIDSourceCallbackImpl(this);
446 QuicAsyncStatus status
=
447 crypto_config_
->channel_id_source()->GetChannelIDKey(
448 server_id_
.host(), &channel_id_key_
,
449 channel_id_source_callback
);
453 channel_id_source_callback_
= channel_id_source_callback
;
454 DVLOG(1) << "Looking up channel ID";
457 next_state_
= STATE_NONE
;
458 delete channel_id_source_callback
;
459 CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE
,
460 "Channel ID lookup failed");
463 delete channel_id_source_callback
;
469 void QuicCryptoClientStream::DoGetChannelIDComplete() {
470 if (!channel_id_key_
.get()) {
471 next_state_
= STATE_NONE
;
472 CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE
,
473 "Channel ID lookup failed");
476 next_state_
= STATE_SEND_CHLO
;
479 void QuicCryptoClientStream::DoReceiveSHLO(
480 const CryptoHandshakeMessage
* in
,
481 QuicCryptoClientConfig::CachedState
* cached
) {
482 next_state_
= STATE_NONE
;
483 // We sent a CHLO that we expected to be accepted and now we're hoping
484 // for a SHLO from the server to confirm that.
485 if (in
->tag() == kREJ
) {
486 // alternative_decrypter will be nullptr if the original alternative
487 // decrypter latched and became the primary decrypter. That happens
488 // if we received a message encrypted with the INITIAL key.
489 if (session()->connection()->alternative_decrypter() == nullptr) {
490 // The rejection was sent encrypted!
491 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT
,
492 "encrypted REJ message");
495 next_state_
= STATE_RECV_REJ
;
499 if (in
->tag() != kSHLO
) {
500 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
501 "Expected SHLO or REJ");
505 // alternative_decrypter will be nullptr if the original alternative
506 // decrypter latched and became the primary decrypter. That happens
507 // if we received a message encrypted with the INITIAL key.
508 if (session()->connection()->alternative_decrypter() != nullptr) {
509 // The server hello was sent without encryption.
510 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT
,
511 "unencrypted SHLO message");
515 string error_details
;
516 QuicErrorCode error
= crypto_config_
->ProcessServerHello(
517 *in
, session()->connection()->connection_id(),
518 session()->connection()->server_supported_versions(),
519 cached
, &crypto_negotiated_params_
, &error_details
);
521 if (error
!= QUIC_NO_ERROR
) {
522 CloseConnectionWithDetails(error
, "Server hello invalid: " + error_details
);
525 error
= session()->config()->ProcessPeerHello(*in
, SERVER
, &error_details
);
526 if (error
!= QUIC_NO_ERROR
) {
527 CloseConnectionWithDetails(error
, "Server hello invalid: " + error_details
);
530 session()->OnConfigNegotiated();
532 CrypterPair
* crypters
= &crypto_negotiated_params_
.forward_secure_crypters
;
533 // TODO(agl): we don't currently latch this decrypter because the idea
534 // has been floated that the server shouldn't send packets encrypted
535 // with the FORWARD_SECURE key until it receives a FORWARD_SECURE
536 // packet from the client.
537 session()->connection()->SetAlternativeDecrypter(
538 crypters
->decrypter
.release(), ENCRYPTION_FORWARD_SECURE
,
539 false /* don't latch */);
540 session()->connection()->SetEncrypter(
541 ENCRYPTION_FORWARD_SECURE
, crypters
->encrypter
.release());
542 session()->connection()->SetDefaultEncryptionLevel(
543 ENCRYPTION_FORWARD_SECURE
);
545 handshake_confirmed_
= true;
546 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED
);
547 session()->connection()->OnHandshakeComplete();
550 void QuicCryptoClientStream::DoInitializeServerConfigUpdate(
551 QuicCryptoClientConfig::CachedState
* cached
) {
552 bool update_ignored
= false;
553 if (!server_id_
.is_https()) {
554 // We don't check the certificates for insecure QUIC connections.
555 SetCachedProofValid(cached
);
556 next_state_
= STATE_NONE
;
557 } else if (!cached
->IsEmpty() && !cached
->signature().empty()) {
558 // Note that we verify the proof even if the cached proof is valid.
559 DCHECK(crypto_config_
->proof_verifier());
560 next_state_
= STATE_VERIFY_PROOF
;
562 update_ignored
= true;
563 next_state_
= STATE_NONE
;
565 UMA_HISTOGRAM_COUNTS("Net.QuicNumServerConfig.UpdateMessagesIgnored",
569 void QuicCryptoClientStream::SetCachedProofValid(
570 QuicCryptoClientConfig::CachedState
* cached
) {
571 cached
->SetProofValid();
572 client_session()->OnProofValid(*cached
);
575 bool QuicCryptoClientStream::RequiresChannelID(
576 QuicCryptoClientConfig::CachedState
* cached
) {
577 if (!server_id_
.is_https() ||
578 server_id_
.privacy_mode() == PRIVACY_MODE_ENABLED
||
579 !crypto_config_
->channel_id_source()) {
582 const CryptoHandshakeMessage
* scfg
= cached
->GetServerConfig();
583 if (!scfg
) { // scfg may be null when we send an inchoate CHLO.
586 const QuicTag
* their_proof_demands
;
587 size_t num_their_proof_demands
;
588 if (scfg
->GetTaglist(kPDMD
, &their_proof_demands
,
589 &num_their_proof_demands
) != QUIC_NO_ERROR
) {
592 for (size_t i
= 0; i
< num_their_proof_demands
; i
++) {
593 if (their_proof_demands
[i
] == kCHID
) {
600 QuicClientSessionBase
* QuicCryptoClientStream::client_session() {
601 return reinterpret_cast<QuicClientSessionBase
*>(session());