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 "net/quic/crypto/crypto_protocol.h"
8 #include "net/quic/crypto/crypto_utils.h"
9 #include "net/quic/crypto/proof_verifier.h"
10 #include "net/quic/quic_protocol.h"
11 #include "net/quic/quic_session.h"
15 QuicCryptoClientStream::QuicCryptoClientStream(
16 const string
& server_hostname
,
17 const QuicConfig
& config
,
19 QuicCryptoClientConfig
* crypto_config
)
20 : QuicCryptoStream(session
),
21 next_state_(STATE_IDLE
),
22 num_client_hellos_(0),
24 crypto_config_(crypto_config
),
25 decrypter_pushed_(false),
26 server_hostname_(server_hostname
) {
29 QuicCryptoClientStream::~QuicCryptoClientStream() {
32 void QuicCryptoClientStream::OnHandshakeMessage(
33 const CryptoHandshakeMessage
& message
) {
34 DoHandshakeLoop(&message
);
37 bool QuicCryptoClientStream::CryptoConnect() {
38 next_state_
= STATE_SEND_CHLO
;
39 DoHandshakeLoop(NULL
);
43 const QuicNegotiatedParameters
&
44 QuicCryptoClientStream::negotiated_params() const {
45 return negotiated_params_
;
48 const QuicCryptoNegotiatedParameters
&
49 QuicCryptoClientStream::crypto_negotiated_params() const {
50 return crypto_negotiated_params_
;
53 int QuicCryptoClientStream::num_sent_client_hellos() const {
54 return num_client_hellos_
;
57 // kMaxClientHellos is the maximum number of times that we'll send a client
58 // hello. The value 3 accounts for:
59 // * One failure due to an incorrect or missing source-address token.
60 // * One failure due the server's certificate chain being unavailible and the
61 // server being unwilling to send it without a valid source-address token.
62 static const int kMaxClientHellos
= 3;
64 void QuicCryptoClientStream::DoHandshakeLoop(
65 const CryptoHandshakeMessage
* in
) {
66 CryptoHandshakeMessage out
;
69 QuicCryptoClientConfig::CachedState
* cached
=
70 crypto_config_
->LookupOrCreate(server_hostname_
);
73 DLOG(INFO
) << "Client received: " << in
->DebugString();
77 const State state
= next_state_
;
78 next_state_
= STATE_IDLE
;
80 case STATE_SEND_CHLO
: {
81 if (num_client_hellos_
> kMaxClientHellos
) {
82 CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS
);
87 if (!cached
->is_complete()) {
88 crypto_config_
->FillInchoateClientHello(server_hostname_
, cached
,
90 next_state_
= STATE_RECV_REJ
;
91 DLOG(INFO
) << "Client Sending: " << out
.DebugString();
92 SendHandshakeMessage(out
);
95 const CryptoHandshakeMessage
* scfg
= cached
->GetServerConfig();
96 config_
.ToHandshakeMessage(&out
);
97 error
= crypto_config_
->FillClientHello(
99 session()->connection()->guid(),
101 session()->connection()->clock(),
102 session()->connection()->random_generator(),
103 &crypto_negotiated_params_
,
106 if (error
!= QUIC_NO_ERROR
) {
107 CloseConnectionWithDetails(error
, error_details
);
110 error
= config_
.ProcessFinalPeerHandshake(
111 *scfg
, CryptoUtils::PEER_PRIORITY
, &negotiated_params_
,
113 if (error
!= QUIC_NO_ERROR
) {
114 CloseConnectionWithDetails(error
, error_details
);
117 next_state_
= STATE_RECV_SHLO
;
118 DLOG(INFO
) << "Client Sending: " << out
.DebugString();
119 SendHandshakeMessage(out
);
120 // Be prepared to decrypt with the new server write key.
121 session()->connection()->PushDecrypter(
122 crypto_negotiated_params_
.decrypter
.release());
123 decrypter_pushed_
= true;
127 // We sent a dummy CHLO because we didn't have enough information to
128 // perform a handshake, or we sent a full hello that the server
129 // rejected. Here we hope to have a REJ that contains the information
131 if (in
->tag() != kREJ
) {
132 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
136 error
= crypto_config_
->ProcessRejection(cached
, *in
,
137 &crypto_negotiated_params_
,
139 if (error
!= QUIC_NO_ERROR
) {
140 CloseConnectionWithDetails(error
, error_details
);
143 if (!cached
->proof_valid()) {
144 const ProofVerifier
* verifier
= crypto_config_
->proof_verifier();
146 // If no verifier is set then we don't check the certificates.
147 cached
->SetProofValid();
148 } else if (!cached
->signature().empty()) {
149 // TODO(rtenneti): In Chromium, we will need to make VerifyProof()
151 if (!verifier
->VerifyProof(server_hostname_
,
152 cached
->server_config(),
156 CloseConnectionWithDetails(QUIC_PROOF_INVALID
,
157 "Proof invalid: " + error_details
);
160 cached
->SetProofValid();
163 // Clear any new server write key that we may have set before.
164 if (decrypter_pushed_
) {
165 session()->connection()->PopDecrypter();
166 decrypter_pushed_
= false;
168 next_state_
= STATE_SEND_CHLO
;
170 case STATE_RECV_SHLO
:
171 // We sent a CHLO that we expected to be accepted and now we're hoping
172 // for a SHLO from the server to confirm that.
173 if (in
->tag() == kREJ
) {
174 next_state_
= STATE_RECV_REJ
;
177 if (in
->tag() != kSHLO
) {
178 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
,
179 "Expected SHLO or REJ");
182 // Receiving SHLO implies the server must have processed our full
183 // CHLO and is ready to decrypt with the new client write key. We
184 // can start to encrypt with the new client write key.
185 // TODO(wtc): when we support 0-RTT, we will need to change the
186 // encrypter when we send a full CHLO because we will be sending
187 // application data immediately after.
188 session()->connection()->ChangeEncrypter(
189 crypto_negotiated_params_
.encrypter
.release());
190 SetHandshakeComplete(QUIC_NO_ERROR
);
193 // This means that the peer sent us a message that we weren't expecting.
194 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
);