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 "base/profiler/scoped_tracker.h"
9 #include "net/quic/crypto/crypto_protocol.h"
10 #include "net/quic/crypto/crypto_utils.h"
11 #include "net/quic/crypto/null_encrypter.h"
12 #include "net/quic/quic_client_session_base.h"
13 #include "net/quic/quic_protocol.h"
14 #include "net/quic/quic_session.h"
20 QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
21 ChannelIDSourceCallbackImpl(QuicCryptoClientStream
* stream
)
24 QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
25 ~ChannelIDSourceCallbackImpl() {}
27 void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Run(
28 scoped_ptr
<ChannelIDKey
>* channel_id_key
) {
29 if (stream_
== nullptr) {
33 stream_
->channel_id_key_
.reset(channel_id_key
->release());
34 stream_
->channel_id_source_callback_run_
= true;
35 stream_
->channel_id_source_callback_
= nullptr;
36 stream_
->DoHandshakeLoop(nullptr);
38 // The ChannelIDSource owns this object and will delete it when this method
42 void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Cancel() {
46 QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
47 QuicCryptoClientStream
* stream
)
50 QuicCryptoClientStream::ProofVerifierCallbackImpl::
51 ~ProofVerifierCallbackImpl() {}
53 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
55 const string
& error_details
,
56 scoped_ptr
<ProofVerifyDetails
>* details
) {
57 if (stream_
== nullptr) {
61 stream_
->verify_ok_
= ok
;
62 stream_
->verify_error_details_
= error_details
;
63 stream_
->verify_details_
.reset(details
->release());
64 stream_
->proof_verify_callback_
= nullptr;
65 stream_
->DoHandshakeLoop(nullptr);
67 // The ProofVerifier owns this object and will delete it when this method
71 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
75 QuicCryptoClientStream::QuicCryptoClientStream(
76 const QuicServerId
& server_id
,
77 QuicClientSessionBase
* session
,
78 ProofVerifyContext
* verify_context
,
79 QuicCryptoClientConfig
* crypto_config
)
80 : QuicCryptoStream(session
),
81 next_state_(STATE_IDLE
),
82 num_client_hellos_(0),
83 crypto_config_(crypto_config
),
84 server_id_(server_id
),
85 generation_counter_(0),
86 channel_id_sent_(false),
87 channel_id_source_callback_run_(false),
88 channel_id_source_callback_(nullptr),
89 verify_context_(verify_context
),
90 proof_verify_callback_(nullptr) {
91 DCHECK(!session
->connection()->is_server());
94 QuicCryptoClientStream::~QuicCryptoClientStream() {
95 if (channel_id_source_callback_
) {
96 channel_id_source_callback_
->Cancel();
98 if (proof_verify_callback_
) {
99 proof_verify_callback_
->Cancel();
103 void QuicCryptoClientStream::OnHandshakeMessage(
104 const CryptoHandshakeMessage
& message
) {
105 QuicCryptoStream::OnHandshakeMessage(message
);
107 if (message
.tag() == kSCUP
) {
108 if (!handshake_confirmed()) {
109 CloseConnection(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE
);
113 // |message| is an update from the server, so we treat it differently from a
114 // handshake message.
115 HandleServerConfigUpdateMessage(message
);
119 // Do not process handshake messages after the handshake is confirmed.
120 if (handshake_confirmed()) {
121 CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE
);
125 DoHandshakeLoop(&message
);
128 void QuicCryptoClientStream::CryptoConnect() {
129 next_state_
= STATE_INITIALIZE
;
130 DoHandshakeLoop(nullptr);
133 int QuicCryptoClientStream::num_sent_client_hellos() const {
134 return num_client_hellos_
;
137 // Used in Chromium, but not in the server.
138 bool QuicCryptoClientStream::WasChannelIDSent() const {
139 return channel_id_sent_
;
142 bool QuicCryptoClientStream::WasChannelIDSourceCallbackRun() const {
143 return channel_id_source_callback_run_
;
146 void QuicCryptoClientStream::HandleServerConfigUpdateMessage(
147 const CryptoHandshakeMessage
& server_config_update
) {
148 DCHECK(server_config_update
.tag() == kSCUP
);
149 string error_details
;
150 QuicCryptoClientConfig::CachedState
* cached
=
151 crypto_config_
->LookupOrCreate(server_id_
);
152 QuicErrorCode error
= crypto_config_
->ProcessServerConfigUpdate(
153 server_config_update
,
154 session()->connection()->clock()->WallNow(),
156 &crypto_negotiated_params_
,
159 if (error
!= QUIC_NO_ERROR
) {
160 CloseConnectionWithDetails(
161 error
, "Server config update invalid: " + error_details
);
165 DCHECK(handshake_confirmed());
166 if (proof_verify_callback_
) {
167 proof_verify_callback_
->Cancel();
169 next_state_
= STATE_INITIALIZE_SCUP
;
170 DoHandshakeLoop(nullptr);
173 // kMaxClientHellos is the maximum number of times that we'll send a client
174 // hello. The value 3 accounts for:
175 // * One failure due to an incorrect or missing source-address token.
176 // * One failure due the server's certificate chain being unavailible and the
177 // server being unwilling to send it without a valid source-address token.
178 static const int kMaxClientHellos
= 3;
180 void QuicCryptoClientStream::DoHandshakeLoop(
181 const CryptoHandshakeMessage
* in
) {
182 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
183 tracked_objects::ScopedTracker
tracking_profile(
184 FROM_HERE_WITH_EXPLICIT_FUNCTION(
185 "422516 QuicCryptoClientStream::DoHandshakeLoop"));
187 QuicCryptoClientConfig::CachedState
* cached
=
188 crypto_config_
->LookupOrCreate(server_id_
);
190 QuicAsyncStatus rv
= QUIC_SUCCESS
;
192 CHECK_NE(STATE_NONE
, next_state_
);
193 const State state
= next_state_
;
194 next_state_
= STATE_IDLE
;
197 case STATE_INITIALIZE
:
198 DoInitialize(cached
);
200 case STATE_SEND_CHLO
:
201 DoSendCHLO(in
, cached
);
202 return; // return waiting to hear from server.
204 DoReceiveREJ(in
, cached
);
206 case STATE_VERIFY_PROOF
:
207 rv
= DoVerifyProof(cached
);
209 case STATE_VERIFY_PROOF_COMPLETE
:
210 DoVerifyProofComplete(cached
);
212 case STATE_GET_CHANNEL_ID
:
213 rv
= DoGetChannelID(cached
);
215 case STATE_GET_CHANNEL_ID_COMPLETE
:
216 DoGetChannelIDComplete();
218 case STATE_RECV_SHLO
:
219 DoReceiveSHLO(in
, cached
);
222 // This means that the peer sent us a message that we weren't expecting.
223 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
);
225 case STATE_INITIALIZE_SCUP
:
226 DoInitializeServerConfigUpdate(cached
);
230 return; // We are done.
232 } while (rv
!= QUIC_PENDING
&& next_state_
!= STATE_NONE
);
235 void QuicCryptoClientStream::DoInitialize(
236 QuicCryptoClientConfig::CachedState
* cached
) {
237 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
238 tracked_objects::ScopedTracker
tracking_profile(
239 FROM_HERE_WITH_EXPLICIT_FUNCTION(
240 "422516 QuicCryptoClientStream::DoInitialize"));
242 if (!cached
->IsEmpty() && !cached
->signature().empty() &&
243 server_id_
.is_https()) {
244 // Note that we verify the proof even if the cached proof is valid.
245 // This allows us to respond to CA trust changes or certificate
246 // expiration because it may have been a while since we last verified
248 DCHECK(crypto_config_
->proof_verifier());
249 // If the cached state needs to be verified, do it now.
250 next_state_
= STATE_VERIFY_PROOF
;
252 next_state_
= STATE_GET_CHANNEL_ID
;
256 void QuicCryptoClientStream::DoSendCHLO(
257 const CryptoHandshakeMessage
* in
,
258 QuicCryptoClientConfig::CachedState
* cached
) {
259 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
260 tracked_objects::ScopedTracker
tracking_profile1(
261 FROM_HERE_WITH_EXPLICIT_FUNCTION(
262 "422516 QuicCryptoClientStream::DoSendCHLO1"));
264 // Send the client hello in plaintext.
265 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE
);
266 if (num_client_hellos_
> kMaxClientHellos
) {
267 CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS
);
270 num_client_hellos_
++;
272 CryptoHandshakeMessage out
;
273 if (!cached
->IsComplete(session()->connection()->clock()->WallNow())) {
274 crypto_config_
->FillInchoateClientHello(
276 session()->connection()->supported_versions().front(),
277 cached
, &crypto_negotiated_params_
, &out
);
278 // Pad the inchoate client hello to fill up a packet.
279 const QuicByteCount kFramingOverhead
= 50; // A rough estimate.
280 const QuicByteCount max_packet_size
=
281 session()->connection()->max_packet_length();
282 if (max_packet_size
<= kFramingOverhead
) {
283 DLOG(DFATAL
) << "max_packet_length (" << max_packet_size
284 << ") has no room for framing overhead.";
285 CloseConnection(QUIC_INTERNAL_ERROR
);
288 if (kClientHelloMinimumSize
> max_packet_size
- kFramingOverhead
) {
289 DLOG(DFATAL
) << "Client hello won't fit in a single packet.";
290 CloseConnection(QUIC_INTERNAL_ERROR
);
293 out
.set_minimum_size(
294 static_cast<size_t>(max_packet_size
- kFramingOverhead
));
295 next_state_
= STATE_RECV_REJ
;
296 SendHandshakeMessage(out
);
300 session()->config()->ToHandshakeMessage(&out
);
301 string error_details
;
302 QuicErrorCode error
= crypto_config_
->FillClientHello(
304 session()->connection()->connection_id(),
305 session()->connection()->supported_versions().front(),
307 session()->connection()->clock()->WallNow(),
308 session()->connection()->random_generator(),
309 channel_id_key_
.get(),
310 &crypto_negotiated_params_
,
314 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
315 tracked_objects::ScopedTracker
tracking_profile2(
316 FROM_HERE_WITH_EXPLICIT_FUNCTION(
317 "422516 QuicCryptoClientStream::DoSendCHLO2"));
319 if (error
!= QUIC_NO_ERROR
) {
320 // Flush the cached config so that, if it's bad, the server has a
321 // chance to send us another in the future.
322 cached
->InvalidateServerConfig();
323 CloseConnectionWithDetails(error
, error_details
);
326 channel_id_sent_
= (channel_id_key_
.get() != nullptr);
327 if (cached
->proof_verify_details()) {
328 client_session()->OnProofVerifyDetailsAvailable(
329 *cached
->proof_verify_details());
331 next_state_
= STATE_RECV_SHLO
;
332 SendHandshakeMessage(out
);
333 // Be prepared to decrypt with the new server write key.
334 session()->connection()->SetAlternativeDecrypter(
335 crypto_negotiated_params_
.initial_crypters
.decrypter
.release(),
337 true /* latch once used */);
338 // Send subsequent packets under encryption on the assumption that the
339 // server will accept the handshake.
340 session()->connection()->SetEncrypter(
342 crypto_negotiated_params_
.initial_crypters
.encrypter
.release());
343 session()->connection()->SetDefaultEncryptionLevel(
345 if (!encryption_established_
) {
346 encryption_established_
= true;
347 session()->OnCryptoHandshakeEvent(
348 QuicSession::ENCRYPTION_FIRST_ESTABLISHED
);
350 session()->OnCryptoHandshakeEvent(
351 QuicSession::ENCRYPTION_REESTABLISHED
);
355 void QuicCryptoClientStream::DoReceiveREJ(
356 const CryptoHandshakeMessage
* in
,
357 QuicCryptoClientConfig::CachedState
* cached
) {
358 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
359 tracked_objects::ScopedTracker
tracking_profile(
360 FROM_HERE_WITH_EXPLICIT_FUNCTION(
361 "422516 QuicCryptoClientStream::DoReceiveREJ"));
363 // We sent a dummy CHLO because we didn't have enough information to
364 // perform a handshake, or we sent a full hello that the server
365 // rejected. Here we hope to have a REJ that contains the information
367 if (in
->tag() != kREJ
) {
368 next_state_
= STATE_NONE
;
369 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
373 string error_details
;
374 QuicErrorCode error
= crypto_config_
->ProcessRejection(
375 *in
, session()->connection()->clock()->WallNow(), cached
,
376 server_id_
.is_https(), &crypto_negotiated_params_
, &error_details
);
377 if (error
!= QUIC_NO_ERROR
) {
378 next_state_
= STATE_NONE
;
379 CloseConnectionWithDetails(error
, error_details
);
382 if (!cached
->proof_valid()) {
383 if (!server_id_
.is_https()) {
384 // We don't check the certificates for insecure QUIC connections.
385 SetCachedProofValid(cached
);
386 } else if (!cached
->signature().empty()) {
387 // Note that we only verify the proof if the cached proof is not
388 // valid. If the cached proof is valid here, someone else must have
389 // just added the server config to the cache and verified the proof,
390 // so we can assume no CA trust changes or certificate expiration
391 // has happened since then.
392 next_state_
= STATE_VERIFY_PROOF
;
396 next_state_
= STATE_GET_CHANNEL_ID
;
399 QuicAsyncStatus
QuicCryptoClientStream::DoVerifyProof(
400 QuicCryptoClientConfig::CachedState
* cached
) {
401 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
402 tracked_objects::ScopedTracker
tracking_profile(
403 FROM_HERE_WITH_EXPLICIT_FUNCTION(
404 "422516 QuicCryptoClientStream::DoVerifyProof"));
406 ProofVerifier
* verifier
= crypto_config_
->proof_verifier();
408 next_state_
= STATE_VERIFY_PROOF_COMPLETE
;
409 generation_counter_
= cached
->generation_counter();
411 ProofVerifierCallbackImpl
* proof_verify_callback
=
412 new ProofVerifierCallbackImpl(this);
416 QuicAsyncStatus status
= verifier
->VerifyProof(
418 cached
->server_config(),
421 verify_context_
.get(),
422 &verify_error_details_
,
424 proof_verify_callback
);
428 proof_verify_callback_
= proof_verify_callback
;
429 DVLOG(1) << "Doing VerifyProof";
432 delete proof_verify_callback
;
435 delete proof_verify_callback
;
442 void QuicCryptoClientStream::DoVerifyProofComplete(
443 QuicCryptoClientConfig::CachedState
* cached
) {
444 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
445 tracked_objects::ScopedTracker
tracking_profile(
446 FROM_HERE_WITH_EXPLICIT_FUNCTION(
447 "422516 QuicCryptoClientStream::DoVerifyProofComplete"));
450 next_state_
= STATE_NONE
;
451 if (verify_details_
.get()) {
452 client_session()->OnProofVerifyDetailsAvailable(*verify_details_
);
454 UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed",
455 handshake_confirmed());
456 CloseConnectionWithDetails(
457 QUIC_PROOF_INVALID
, "Proof invalid: " + verify_error_details_
);
461 // Check if generation_counter has changed between STATE_VERIFY_PROOF and
462 // STATE_VERIFY_PROOF_COMPLETE state changes.
463 if (generation_counter_
!= cached
->generation_counter()) {
464 next_state_
= STATE_VERIFY_PROOF
;
466 SetCachedProofValid(cached
);
467 cached
->SetProofVerifyDetails(verify_details_
.release());
468 if (!handshake_confirmed()) {
469 next_state_
= STATE_GET_CHANNEL_ID
;
471 next_state_
= STATE_NONE
;
476 QuicAsyncStatus
QuicCryptoClientStream::DoGetChannelID(
477 QuicCryptoClientConfig::CachedState
* cached
) {
478 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
479 tracked_objects::ScopedTracker
tracking_profile(
480 FROM_HERE_WITH_EXPLICIT_FUNCTION(
481 "422516 QuicCryptoClientStream::DoGetChannelID"));
483 next_state_
= STATE_GET_CHANNEL_ID_COMPLETE
;
484 channel_id_key_
.reset();
485 if (!RequiresChannelID(cached
)) {
486 next_state_
= STATE_SEND_CHLO
;
490 ChannelIDSourceCallbackImpl
* channel_id_source_callback
=
491 new ChannelIDSourceCallbackImpl(this);
492 QuicAsyncStatus status
=
493 crypto_config_
->channel_id_source()->GetChannelIDKey(
494 server_id_
.host(), &channel_id_key_
,
495 channel_id_source_callback
);
499 channel_id_source_callback_
= channel_id_source_callback
;
500 DVLOG(1) << "Looking up channel ID";
503 next_state_
= STATE_NONE
;
504 delete channel_id_source_callback
;
505 CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE
,
506 "Channel ID lookup failed");
509 delete channel_id_source_callback
;
515 void QuicCryptoClientStream::DoGetChannelIDComplete() {
516 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
517 tracked_objects::ScopedTracker
tracking_profile(
518 FROM_HERE_WITH_EXPLICIT_FUNCTION(
519 "422516 QuicCryptoClientStream::DoGetChannelIDComplete"));
521 if (!channel_id_key_
.get()) {
522 next_state_
= STATE_NONE
;
523 CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE
,
524 "Channel ID lookup failed");
527 next_state_
= STATE_SEND_CHLO
;
530 void QuicCryptoClientStream::DoReceiveSHLO(
531 const CryptoHandshakeMessage
* in
,
532 QuicCryptoClientConfig::CachedState
* cached
) {
533 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
534 tracked_objects::ScopedTracker
tracking_profile(
535 FROM_HERE_WITH_EXPLICIT_FUNCTION(
536 "422516 QuicCryptoClientStream::DoReceiveSHLO"));
538 next_state_
= STATE_NONE
;
539 // We sent a CHLO that we expected to be accepted and now we're hoping
540 // for a SHLO from the server to confirm that.
541 if (in
->tag() == kREJ
) {
542 // alternative_decrypter will be nullptr if the original alternative
543 // decrypter latched and became the primary decrypter. That happens
544 // if we received a message encrypted with the INITIAL key.
545 if (session()->connection()->alternative_decrypter() == nullptr) {
546 // The rejection was sent encrypted!
547 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT
,
548 "encrypted REJ message");
551 next_state_
= STATE_RECV_REJ
;
555 if (in
->tag() != kSHLO
) {
556 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
557 "Expected SHLO or REJ");
561 // alternative_decrypter will be nullptr if the original alternative
562 // decrypter latched and became the primary decrypter. That happens
563 // if we received a message encrypted with the INITIAL key.
564 if (session()->connection()->alternative_decrypter() != nullptr) {
565 // The server hello was sent without encryption.
566 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT
,
567 "unencrypted SHLO message");
571 string error_details
;
572 QuicErrorCode error
= crypto_config_
->ProcessServerHello(
573 *in
, session()->connection()->connection_id(),
574 session()->connection()->server_supported_versions(),
575 cached
, &crypto_negotiated_params_
, &error_details
);
577 if (error
!= QUIC_NO_ERROR
) {
578 CloseConnectionWithDetails(error
, "Server hello invalid: " + error_details
);
581 error
= session()->config()->ProcessPeerHello(*in
, SERVER
, &error_details
);
582 if (error
!= QUIC_NO_ERROR
) {
583 CloseConnectionWithDetails(error
, "Server hello invalid: " + error_details
);
586 session()->OnConfigNegotiated();
588 CrypterPair
* crypters
= &crypto_negotiated_params_
.forward_secure_crypters
;
589 // TODO(agl): we don't currently latch this decrypter because the idea
590 // has been floated that the server shouldn't send packets encrypted
591 // with the FORWARD_SECURE key until it receives a FORWARD_SECURE
592 // packet from the client.
593 session()->connection()->SetAlternativeDecrypter(
594 crypters
->decrypter
.release(), ENCRYPTION_FORWARD_SECURE
,
595 false /* don't latch */);
596 session()->connection()->SetEncrypter(
597 ENCRYPTION_FORWARD_SECURE
, crypters
->encrypter
.release());
598 session()->connection()->SetDefaultEncryptionLevel(
599 ENCRYPTION_FORWARD_SECURE
);
601 handshake_confirmed_
= true;
602 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED
);
603 session()->connection()->OnHandshakeComplete();
606 void QuicCryptoClientStream::DoInitializeServerConfigUpdate(
607 QuicCryptoClientConfig::CachedState
* cached
) {
608 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
609 tracked_objects::ScopedTracker
tracking_profile(
610 FROM_HERE_WITH_EXPLICIT_FUNCTION(
611 "422516 QuicCryptoClientStream::DoInitializeServerConfigUpdate"));
613 bool update_ignored
= false;
614 if (!server_id_
.is_https()) {
615 // We don't check the certificates for insecure QUIC connections.
616 SetCachedProofValid(cached
);
617 next_state_
= STATE_NONE
;
618 } else if (!cached
->IsEmpty() && !cached
->signature().empty()) {
619 // Note that we verify the proof even if the cached proof is valid.
620 DCHECK(crypto_config_
->proof_verifier());
621 next_state_
= STATE_VERIFY_PROOF
;
623 update_ignored
= true;
624 next_state_
= STATE_NONE
;
626 UMA_HISTOGRAM_COUNTS("Net.QuicNumServerConfig.UpdateMessagesIgnored",
630 void QuicCryptoClientStream::SetCachedProofValid(
631 QuicCryptoClientConfig::CachedState
* cached
) {
632 cached
->SetProofValid();
633 client_session()->OnProofValid(*cached
);
636 bool QuicCryptoClientStream::RequiresChannelID(
637 QuicCryptoClientConfig::CachedState
* cached
) {
638 if (!server_id_
.is_https() ||
639 server_id_
.privacy_mode() == PRIVACY_MODE_ENABLED
||
640 !crypto_config_
->channel_id_source()) {
643 const CryptoHandshakeMessage
* scfg
= cached
->GetServerConfig();
644 if (!scfg
) { // scfg may be null then we send an inchoate CHLO.
647 const QuicTag
* their_proof_demands
;
648 size_t num_their_proof_demands
;
649 if (scfg
->GetTaglist(kPDMD
, &their_proof_demands
,
650 &num_their_proof_demands
) != QUIC_NO_ERROR
) {
653 for (size_t i
= 0; i
< num_their_proof_demands
; i
++) {
654 if (their_proof_demands
[i
] == kCHID
) {
661 QuicClientSessionBase
* QuicCryptoClientStream::client_session() {
662 return reinterpret_cast<QuicClientSessionBase
*>(session());