Add explicit |forceOnlineSignin| to user pod status
[chromium-blink-merge.git] / net / quic / quic_crypto_client_stream.cc
blob3e5f84059fc0ec4567c39d1da66a18f7c3f5299c
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/base/completion_callback.h"
8 #include "net/base/net_errors.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/crypto/proof_verifier.h"
13 #include "net/quic/crypto/proof_verifier_chromium.h"
14 #include "net/quic/quic_protocol.h"
15 #include "net/quic/quic_session.h"
16 #include "net/ssl/ssl_connection_status_flags.h"
17 #include "net/ssl/ssl_info.h"
19 namespace net {
21 namespace {
23 // Copies CertVerifyResult from |verify_details| to |cert_verify_result|.
24 void CopyCertVerifyResult(
25 const ProofVerifyDetails* verify_details,
26 scoped_ptr<CertVerifyResult>* cert_verify_result) {
27 const CertVerifyResult* cert_verify_result_other =
28 &(reinterpret_cast<const ProofVerifyDetailsChromium*>(
29 verify_details))->cert_verify_result;
30 CertVerifyResult* result_copy = new CertVerifyResult;
31 result_copy->CopyFrom(*cert_verify_result_other);
32 cert_verify_result->reset(result_copy);
35 } // namespace
37 QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
38 QuicCryptoClientStream* stream)
39 : stream_(stream) {}
41 QuicCryptoClientStream::ProofVerifierCallbackImpl::
42 ~ProofVerifierCallbackImpl() {}
44 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
45 bool ok,
46 const string& error_details,
47 scoped_ptr<ProofVerifyDetails>* details) {
48 if (stream_ == NULL) {
49 return;
52 stream_->verify_ok_ = ok;
53 stream_->verify_error_details_ = error_details;
54 stream_->verify_details_.reset(details->release());
55 stream_->proof_verify_callback_ = NULL;
56 stream_->DoHandshakeLoop(NULL);
58 // The ProofVerifier owns this object and will delete it when this method
59 // returns.
62 void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
63 stream_ = NULL;
67 QuicCryptoClientStream::QuicCryptoClientStream(
68 const string& server_hostname,
69 QuicSession* session,
70 QuicCryptoClientConfig* crypto_config)
71 : QuicCryptoStream(session),
72 next_state_(STATE_IDLE),
73 num_client_hellos_(0),
74 crypto_config_(crypto_config),
75 server_hostname_(server_hostname),
76 generation_counter_(0),
77 proof_verify_callback_(NULL) {
80 QuicCryptoClientStream::~QuicCryptoClientStream() {
81 if (proof_verify_callback_) {
82 proof_verify_callback_->Cancel();
86 void QuicCryptoClientStream::OnHandshakeMessage(
87 const CryptoHandshakeMessage& message) {
88 QuicCryptoStream::OnHandshakeMessage(message);
90 DoHandshakeLoop(&message);
93 bool QuicCryptoClientStream::CryptoConnect() {
94 next_state_ = STATE_SEND_CHLO;
95 DoHandshakeLoop(NULL);
96 return true;
99 int QuicCryptoClientStream::num_sent_client_hellos() const {
100 return num_client_hellos_;
103 // TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
104 // we learn about SSL info (sync vs async vs cached).
105 bool QuicCryptoClientStream::GetSSLInfo(SSLInfo* ssl_info) {
106 ssl_info->Reset();
107 if (!cert_verify_result_) {
108 return false;
111 ssl_info->cert_status = cert_verify_result_->cert_status;
112 ssl_info->cert = cert_verify_result_->verified_cert;
114 // TODO(rtenneti): Figure out what to set for the following.
115 // Temporarily hard coded cipher_suite as 0xc031 to represent
116 // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (from
117 // net/ssl/ssl_cipher_suite_names.cc) and encryption as 256.
118 int cipher_suite = 0xc02f;
119 int ssl_connection_status = 0;
120 ssl_connection_status |=
121 (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
122 SSL_CONNECTION_CIPHERSUITE_SHIFT;
123 ssl_connection_status |=
124 (SSL_CONNECTION_VERSION_TLS1_2 & SSL_CONNECTION_VERSION_MASK) <<
125 SSL_CONNECTION_VERSION_SHIFT;
127 ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
128 ssl_info->is_issued_by_known_root =
129 cert_verify_result_->is_issued_by_known_root;
131 ssl_info->connection_status = ssl_connection_status;
132 ssl_info->client_cert_sent = false;
133 ssl_info->channel_id_sent = false;
134 ssl_info->security_bits = 256;
135 ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
136 return true;
139 // kMaxClientHellos is the maximum number of times that we'll send a client
140 // hello. The value 3 accounts for:
141 // * One failure due to an incorrect or missing source-address token.
142 // * One failure due the server's certificate chain being unavailible and the
143 // server being unwilling to send it without a valid source-address token.
144 static const int kMaxClientHellos = 3;
146 void QuicCryptoClientStream::DoHandshakeLoop(
147 const CryptoHandshakeMessage* in) {
148 CryptoHandshakeMessage out;
149 QuicErrorCode error;
150 string error_details;
151 QuicCryptoClientConfig::CachedState* cached =
152 crypto_config_->LookupOrCreate(server_hostname_);
154 if (in != NULL) {
155 DVLOG(1) << "Client: Received " << in->DebugString();
158 for (;;) {
159 const State state = next_state_;
160 next_state_ = STATE_IDLE;
161 switch (state) {
162 case STATE_SEND_CHLO: {
163 // Send the client hello in plaintext.
164 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE);
165 if (num_client_hellos_ > kMaxClientHellos) {
166 CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS);
167 return;
169 num_client_hellos_++;
171 if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
172 crypto_config_->FillInchoateClientHello(
173 server_hostname_,
174 session()->connection()->supported_versions().front(),
175 cached, &crypto_negotiated_params_, &out);
176 // Pad the inchoate client hello to fill up a packet.
177 const size_t kFramingOverhead = 50; // A rough estimate.
178 const size_t max_packet_size =
179 session()->connection()->options()->max_packet_length;
180 if (max_packet_size <= kFramingOverhead) {
181 DLOG(DFATAL) << "max_packet_length (" << max_packet_size
182 << ") has no room for framing overhead.";
183 CloseConnection(QUIC_INTERNAL_ERROR);
184 return;
186 if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) {
187 DLOG(DFATAL) << "Client hello won't fit in a single packet.";
188 CloseConnection(QUIC_INTERNAL_ERROR);
189 return;
191 out.set_minimum_size(max_packet_size - kFramingOverhead);
192 next_state_ = STATE_RECV_REJ;
193 DVLOG(1) << "Client: Sending " << out.DebugString();
194 SendHandshakeMessage(out);
195 return;
197 session()->config()->ToHandshakeMessage(&out);
198 error = crypto_config_->FillClientHello(
199 server_hostname_,
200 session()->connection()->guid(),
201 session()->connection()->supported_versions().front(),
202 cached,
203 session()->connection()->clock()->WallNow(),
204 session()->connection()->random_generator(),
205 &crypto_negotiated_params_,
206 &out,
207 &error_details);
208 if (error != QUIC_NO_ERROR) {
209 // Flush the cached config so that, if it's bad, the server has a
210 // chance to send us another in the future.
211 cached->InvalidateServerConfig();
212 CloseConnectionWithDetails(error, error_details);
213 return;
215 if (cached->proof_verify_details()) {
216 CopyCertVerifyResult(cached->proof_verify_details(),
217 &cert_verify_result_);
218 } else {
219 cert_verify_result_.reset();
221 next_state_ = STATE_RECV_SHLO;
222 DVLOG(1) << "Client: Sending " << out.DebugString();
223 SendHandshakeMessage(out);
224 // Be prepared to decrypt with the new server write key.
225 session()->connection()->SetAlternativeDecrypter(
226 crypto_negotiated_params_.initial_crypters.decrypter.release(),
227 true /* latch once used */);
228 // Send subsequent packets under encryption on the assumption that the
229 // server will accept the handshake.
230 session()->connection()->SetEncrypter(
231 ENCRYPTION_INITIAL,
232 crypto_negotiated_params_.initial_crypters.encrypter.release());
233 session()->connection()->SetDefaultEncryptionLevel(
234 ENCRYPTION_INITIAL);
235 if (!encryption_established_) {
236 encryption_established_ = true;
237 session()->OnCryptoHandshakeEvent(
238 QuicSession::ENCRYPTION_FIRST_ESTABLISHED);
239 } else {
240 session()->OnCryptoHandshakeEvent(
241 QuicSession::ENCRYPTION_REESTABLISHED);
243 return;
245 case STATE_RECV_REJ:
246 // We sent a dummy CHLO because we didn't have enough information to
247 // perform a handshake, or we sent a full hello that the server
248 // rejected. Here we hope to have a REJ that contains the information
249 // that we need.
250 if (in->tag() != kREJ) {
251 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
252 "Expected REJ");
253 return;
255 error = crypto_config_->ProcessRejection(
256 *in, session()->connection()->clock()->WallNow(), cached,
257 &crypto_negotiated_params_, &error_details);
258 if (error != QUIC_NO_ERROR) {
259 CloseConnectionWithDetails(error, error_details);
260 return;
262 if (!cached->proof_valid()) {
263 ProofVerifier* verifier = crypto_config_->proof_verifier();
264 if (!verifier) {
265 // If no verifier is set then we don't check the certificates.
266 cached->SetProofValid();
267 } else if (!cached->signature().empty()) {
268 next_state_ = STATE_VERIFY_PROOF;
269 break;
272 next_state_ = STATE_SEND_CHLO;
273 break;
274 case STATE_VERIFY_PROOF: {
275 ProofVerifier* verifier = crypto_config_->proof_verifier();
276 DCHECK(verifier);
277 next_state_ = STATE_VERIFY_PROOF_COMPLETE;
278 generation_counter_ = cached->generation_counter();
280 ProofVerifierCallbackImpl* proof_verify_callback =
281 new ProofVerifierCallbackImpl(this);
283 verify_ok_ = false;
285 ProofVerifier::Status status = verifier->VerifyProof(
286 server_hostname_,
287 cached->server_config(),
288 cached->certs(),
289 cached->signature(),
290 &verify_error_details_,
291 &verify_details_,
292 proof_verify_callback);
294 switch (status) {
295 case ProofVerifier::PENDING:
296 proof_verify_callback_ = proof_verify_callback;
297 DVLOG(1) << "Doing VerifyProof";
298 return;
299 case ProofVerifier::FAILURE:
300 break;
301 case ProofVerifier::SUCCESS:
302 verify_ok_ = true;
303 break;
305 break;
307 case STATE_VERIFY_PROOF_COMPLETE:
308 if (!verify_ok_) {
309 CopyCertVerifyResult(verify_details_.get(), &cert_verify_result_);
310 CloseConnectionWithDetails(
311 QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_);
312 return;
314 // Check if generation_counter has changed between STATE_VERIFY_PROOF
315 // and STATE_VERIFY_PROOF_COMPLETE state changes.
316 if (generation_counter_ != cached->generation_counter()) {
317 next_state_ = STATE_VERIFY_PROOF;
318 } else {
319 cached->SetProofValid();
320 cached->SetProofVerifyDetails(verify_details_.release());
321 next_state_ = STATE_SEND_CHLO;
323 break;
324 case STATE_RECV_SHLO: {
325 // We sent a CHLO that we expected to be accepted and now we're hoping
326 // for a SHLO from the server to confirm that.
327 if (in->tag() == kREJ) {
328 // alternative_decrypter will be NULL if the original alternative
329 // decrypter latched and became the primary decrypter. That happens
330 // if we received a message encrypted with the INITIAL key.
331 if (session()->connection()->alternative_decrypter() == NULL) {
332 // The rejection was sent encrypted!
333 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
334 "encrypted REJ message");
335 return;
337 next_state_ = STATE_RECV_REJ;
338 break;
340 if (in->tag() != kSHLO) {
341 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
342 "Expected SHLO or REJ");
343 return;
345 // alternative_decrypter will be NULL if the original alternative
346 // decrypter latched and became the primary decrypter. That happens
347 // if we received a message encrypted with the INITIAL key.
348 if (session()->connection()->alternative_decrypter() != NULL) {
349 // The server hello was sent without encryption.
350 CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
351 "unencrypted SHLO message");
352 return;
354 error = crypto_config_->ProcessServerHello(
355 *in, session()->connection()->guid(),
356 session()->connection()->server_supported_versions(),
357 cached, &crypto_negotiated_params_, &error_details);
359 if (error != QUIC_NO_ERROR) {
360 CloseConnectionWithDetails(
361 error, "Server hello invalid: " + error_details);
362 return;
364 error = session()->config()->ProcessServerHello(*in, &error_details);
365 if (error != QUIC_NO_ERROR) {
366 CloseConnectionWithDetails(
367 error, "Server hello invalid: " + error_details);
368 return;
370 session()->OnConfigNegotiated();
372 CrypterPair* crypters =
373 &crypto_negotiated_params_.forward_secure_crypters;
374 // TODO(agl): we don't currently latch this decrypter because the idea
375 // has been floated that the server shouldn't send packets encrypted
376 // with the FORWARD_SECURE key until it receives a FORWARD_SECURE
377 // packet from the client.
378 session()->connection()->SetAlternativeDecrypter(
379 crypters->decrypter.release(), false /* don't latch */);
380 session()->connection()->SetEncrypter(
381 ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release());
382 session()->connection()->SetDefaultEncryptionLevel(
383 ENCRYPTION_FORWARD_SECURE);
385 handshake_confirmed_ = true;
386 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
387 return;
389 case STATE_IDLE:
390 // This means that the peer sent us a message that we weren't expecting.
391 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
392 return;
397 } // namespace net