Chromium Code Reviews| Index: net/quic/quic_crypto_client_stream.cc |
| diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc |
| index 4d9d032a9041b0c9827818d6d4f6564fe45448cc..669636daad9650ce8529722ee28fdf3ce56816d0 100644 |
| --- a/net/quic/quic_crypto_client_stream.cc |
| +++ b/net/quic/quic_crypto_client_stream.cc |
| @@ -175,9 +175,6 @@ static const int kMaxClientHellos = 3; |
| void QuicCryptoClientStream::DoHandshakeLoop( |
| const CryptoHandshakeMessage* in) { |
| - CryptoHandshakeMessage out; |
| - QuicErrorCode error; |
| - string error_details; |
| QuicCryptoClientConfig::CachedState* cached = |
| crypto_config_->LookupOrCreate(server_id_); |
| @@ -185,135 +182,14 @@ void QuicCryptoClientStream::DoHandshakeLoop( |
| const State state = next_state_; |
| next_state_ = STATE_IDLE; |
| switch (state) { |
| - case STATE_INITIALIZE: { |
| - if (!cached->IsEmpty() && !cached->signature().empty() && |
| - server_id_.is_https()) { |
| - // Note that we verify the proof even if the cached proof is valid. |
| - // This allows us to respond to CA trust changes or certificate |
| - // expiration because it may have been a while since we last verified |
| - // the proof. |
| - DCHECK(crypto_config_->proof_verifier()); |
| - // If the cached state needs to be verified, do it now. |
| - next_state_ = STATE_VERIFY_PROOF; |
| - } else { |
| - next_state_ = STATE_GET_CHANNEL_ID; |
| - } |
| + case STATE_INITIALIZE: |
| + DoInitialize(cached); |
| break; |
| - } |
| - case STATE_SEND_CHLO: { |
| - // Send the client hello in plaintext. |
| - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); |
| - if (num_client_hellos_ > kMaxClientHellos) { |
| - CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS); |
| - return; |
| - } |
| - num_client_hellos_++; |
| - |
| - if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { |
| - crypto_config_->FillInchoateClientHello( |
| - server_id_, |
| - session()->connection()->supported_versions().front(), |
| - cached, &crypto_negotiated_params_, &out); |
| - // Pad the inchoate client hello to fill up a packet. |
| - const size_t kFramingOverhead = 50; // A rough estimate. |
| - const size_t max_packet_size = |
| - session()->connection()->max_packet_length(); |
| - if (max_packet_size <= kFramingOverhead) { |
| - DLOG(DFATAL) << "max_packet_length (" << max_packet_size |
| - << ") has no room for framing overhead."; |
| - CloseConnection(QUIC_INTERNAL_ERROR); |
| - return; |
| - } |
| - if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) { |
| - DLOG(DFATAL) << "Client hello won't fit in a single packet."; |
| - CloseConnection(QUIC_INTERNAL_ERROR); |
| - return; |
| - } |
| - out.set_minimum_size(max_packet_size - kFramingOverhead); |
| - next_state_ = STATE_RECV_REJ; |
| - SendHandshakeMessage(out); |
| - return; |
| - } |
| - session()->config()->ToHandshakeMessage(&out); |
| - error = crypto_config_->FillClientHello( |
| - server_id_, |
| - session()->connection()->connection_id(), |
| - session()->connection()->supported_versions().front(), |
| - cached, |
| - session()->connection()->clock()->WallNow(), |
| - session()->connection()->random_generator(), |
| - channel_id_key_.get(), |
| - &crypto_negotiated_params_, |
| - &out, |
| - &error_details); |
| - if (error != QUIC_NO_ERROR) { |
| - // Flush the cached config so that, if it's bad, the server has a |
| - // chance to send us another in the future. |
| - cached->InvalidateServerConfig(); |
| - CloseConnectionWithDetails(error, error_details); |
| - return; |
| - } |
| - channel_id_sent_ = (channel_id_key_.get() != NULL); |
| - if (cached->proof_verify_details()) { |
| - client_session()->OnProofVerifyDetailsAvailable( |
| - *cached->proof_verify_details()); |
| - } |
| - next_state_ = STATE_RECV_SHLO; |
| - SendHandshakeMessage(out); |
| - // Be prepared to decrypt with the new server write key. |
| - session()->connection()->SetAlternativeDecrypter( |
| - crypto_negotiated_params_.initial_crypters.decrypter.release(), |
| - ENCRYPTION_INITIAL, |
| - true /* latch once used */); |
| - // Send subsequent packets under encryption on the assumption that the |
| - // server will accept the handshake. |
| - session()->connection()->SetEncrypter( |
| - ENCRYPTION_INITIAL, |
| - crypto_negotiated_params_.initial_crypters.encrypter.release()); |
| - session()->connection()->SetDefaultEncryptionLevel( |
| - ENCRYPTION_INITIAL); |
| - if (!encryption_established_) { |
| - encryption_established_ = true; |
| - session()->OnCryptoHandshakeEvent( |
| - QuicSession::ENCRYPTION_FIRST_ESTABLISHED); |
| - } else { |
| - session()->OnCryptoHandshakeEvent( |
| - QuicSession::ENCRYPTION_REESTABLISHED); |
| - } |
| + case STATE_SEND_CHLO: |
| + DoSendCHLO(in, cached); |
| return; |
| - } |
| case STATE_RECV_REJ: |
| - // We sent a dummy CHLO because we didn't have enough information to |
| - // perform a handshake, or we sent a full hello that the server |
| - // rejected. Here we hope to have a REJ that contains the information |
| - // that we need. |
| - if (in->tag() != kREJ) { |
| - CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
| - "Expected REJ"); |
| - return; |
| - } |
| - error = crypto_config_->ProcessRejection( |
| - *in, session()->connection()->clock()->WallNow(), cached, |
| - server_id_.is_https(), &crypto_negotiated_params_, &error_details); |
| - if (error != QUIC_NO_ERROR) { |
| - CloseConnectionWithDetails(error, error_details); |
| - return; |
| - } |
| - if (!cached->proof_valid()) { |
| - if (!server_id_.is_https()) { |
| - // We don't check the certificates for insecure QUIC connections. |
| - SetCachedProofValid(cached); |
| - } else if (!cached->signature().empty()) { |
| - // Note that we only verify the proof if the cached proof is not |
| - // valid. If the cached proof is valid here, someone else must have |
| - // just added the server config to the cache and verified the proof, |
| - // so we can assume no CA trust changes or certificate expiration |
| - // has happened since then. |
| - next_state_ = STATE_VERIFY_PROOF; |
| - break; |
| - } |
| - } |
| - next_state_ = STATE_GET_CHANNEL_ID; |
| + DoReceiveREJ(in, cached); |
| break; |
| case STATE_VERIFY_PROOF: { |
| if (QUIC_PENDING == DoVerifyProof(cached)) { |
| @@ -322,117 +198,20 @@ void QuicCryptoClientStream::DoHandshakeLoop( |
| break; |
| } |
| case STATE_VERIFY_PROOF_COMPLETE: |
| - if (QUIC_PROOF_INVALID == DoVerifyProofComplete(cached)) { |
| - return; |
| - } |
| + DoVerifyProofComplete(cached); |
| break; |
| case STATE_GET_CHANNEL_ID: { |
| - next_state_ = STATE_GET_CHANNEL_ID_COMPLETE; |
| - channel_id_key_.reset(); |
| - if (!RequiresChannelID(cached)) { |
| - next_state_ = STATE_SEND_CHLO; |
| - break; |
| - } |
| - |
| - ChannelIDSourceCallbackImpl* channel_id_source_callback = |
| - new ChannelIDSourceCallbackImpl(this); |
| - QuicAsyncStatus status = |
| - crypto_config_->channel_id_source()->GetChannelIDKey( |
| - server_id_.host(), &channel_id_key_, |
| - channel_id_source_callback); |
| - |
| - switch (status) { |
| - case QUIC_PENDING: |
| - channel_id_source_callback_ = channel_id_source_callback; |
| - DVLOG(1) << "Looking up channel ID"; |
| - return; |
| - case QUIC_FAILURE: |
| - delete channel_id_source_callback; |
| - CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, |
| - "Channel ID lookup failed"); |
| - return; |
| - case QUIC_SUCCESS: |
| - delete channel_id_source_callback; |
| - break; |
| + if (QUIC_SUCCESS != DoGetChannelID(cached)) { |
|
agl
2014/09/19 17:26:52
In other cases you have changed returns into next_
ramant (doing other things)
2014/09/19 21:25:25
In DoGetChannelID and DoVerifyProof we shouldn't m
|
| + return; |
| } |
| break; |
| } |
| case STATE_GET_CHANNEL_ID_COMPLETE: |
| - if (!channel_id_key_.get()) { |
| - CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, |
| - "Channel ID lookup failed"); |
| - return; |
| - } |
| - next_state_ = STATE_SEND_CHLO; |
| + DoGetChannelIDComplete(); |
| + break; |
| + case STATE_RECV_SHLO: |
| + DoReceiveSHLO(in, cached); |
| break; |
| - case STATE_RECV_SHLO: { |
| - // We sent a CHLO that we expected to be accepted and now we're hoping |
| - // for a SHLO from the server to confirm that. |
| - if (in->tag() == kREJ) { |
| - // alternative_decrypter will be NULL if the original alternative |
| - // decrypter latched and became the primary decrypter. That happens |
| - // if we received a message encrypted with the INITIAL key. |
| - if (session()->connection()->alternative_decrypter() == NULL) { |
| - // The rejection was sent encrypted! |
| - CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, |
| - "encrypted REJ message"); |
| - return; |
| - } |
| - next_state_ = STATE_RECV_REJ; |
| - break; |
| - } |
| - if (in->tag() != kSHLO) { |
| - CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
| - "Expected SHLO or REJ"); |
| - return; |
| - } |
| - // alternative_decrypter will be NULL if the original alternative |
| - // decrypter latched and became the primary decrypter. That happens |
| - // if we received a message encrypted with the INITIAL key. |
| - if (session()->connection()->alternative_decrypter() != NULL) { |
| - // The server hello was sent without encryption. |
| - CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, |
| - "unencrypted SHLO message"); |
| - return; |
| - } |
| - error = crypto_config_->ProcessServerHello( |
| - *in, session()->connection()->connection_id(), |
| - session()->connection()->server_supported_versions(), |
| - cached, &crypto_negotiated_params_, &error_details); |
| - |
| - if (error != QUIC_NO_ERROR) { |
| - CloseConnectionWithDetails( |
| - error, "Server hello invalid: " + error_details); |
| - return; |
| - } |
| - error = |
| - session()->config()->ProcessPeerHello(*in, SERVER, &error_details); |
| - if (error != QUIC_NO_ERROR) { |
| - CloseConnectionWithDetails( |
| - error, "Server hello invalid: " + error_details); |
| - return; |
| - } |
| - session()->OnConfigNegotiated(); |
| - |
| - CrypterPair* crypters = |
| - &crypto_negotiated_params_.forward_secure_crypters; |
| - // TODO(agl): we don't currently latch this decrypter because the idea |
| - // has been floated that the server shouldn't send packets encrypted |
| - // with the FORWARD_SECURE key until it receives a FORWARD_SECURE |
| - // packet from the client. |
| - session()->connection()->SetAlternativeDecrypter( |
| - crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE, |
| - false /* don't latch */); |
| - session()->connection()->SetEncrypter( |
| - ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release()); |
| - session()->connection()->SetDefaultEncryptionLevel( |
| - ENCRYPTION_FORWARD_SECURE); |
| - |
| - handshake_confirmed_ = true; |
| - session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); |
| - session()->connection()->OnHandshakeComplete(); |
| - return; |
| - } |
| case STATE_IDLE: |
| // This means that the peer sent us a message that we weren't expecting. |
| CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); |
| @@ -440,27 +219,154 @@ void QuicCryptoClientStream::DoHandshakeLoop( |
| case STATE_INITIALIZE_SCUP: |
| DoInitializeServerConfigUpdate(cached); |
| break; |
| - case STATE_VERIFY_PROOF_DONE: |
| + case STATE_DONE: |
| return; // We are done. |
| } |
| } |
| } |
| -void QuicCryptoClientStream::DoInitializeServerConfigUpdate( |
| +void QuicCryptoClientStream::DoInitialize( |
| QuicCryptoClientConfig::CachedState* cached) { |
| - if (!server_id_.is_https()) { |
| - // We don't check the certificates for insecure QUIC connections. |
| - SetCachedProofValid(cached); |
| - next_state_ = STATE_VERIFY_PROOF_DONE; |
| - return; |
| - } |
| - if (!cached->IsEmpty() && !cached->signature().empty()) { |
| + if (!cached->IsEmpty() && !cached->signature().empty() && |
| + server_id_.is_https()) { |
| // Note that we verify the proof even if the cached proof is valid. |
| + // This allows us to respond to CA trust changes or certificate |
| + // expiration because it may have been a while since we last verified |
| + // the proof. |
| DCHECK(crypto_config_->proof_verifier()); |
| + // If the cached state needs to be verified, do it now. |
| next_state_ = STATE_VERIFY_PROOF; |
| + } else { |
| + next_state_ = STATE_GET_CHANNEL_ID; |
| + } |
| +} |
| + |
| +void QuicCryptoClientStream::DoSendCHLO( |
| + const CryptoHandshakeMessage* in, |
| + QuicCryptoClientConfig::CachedState* cached) { |
| + // Send the client hello in plaintext. |
| + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); |
| + if (num_client_hellos_ > kMaxClientHellos) { |
| + CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS); |
| + return; |
| + } |
| + num_client_hellos_++; |
| + |
| + CryptoHandshakeMessage out; |
| + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { |
| + crypto_config_->FillInchoateClientHello( |
| + server_id_, |
| + session()->connection()->supported_versions().front(), |
| + cached, &crypto_negotiated_params_, &out); |
| + // Pad the inchoate client hello to fill up a packet. |
| + const size_t kFramingOverhead = 50; // A rough estimate. |
| + const size_t max_packet_size = |
| + session()->connection()->max_packet_length(); |
| + if (max_packet_size <= kFramingOverhead) { |
| + DLOG(DFATAL) << "max_packet_length (" << max_packet_size |
| + << ") has no room for framing overhead."; |
| + CloseConnection(QUIC_INTERNAL_ERROR); |
| + return; |
| + } |
| + if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) { |
| + DLOG(DFATAL) << "Client hello won't fit in a single packet."; |
| + CloseConnection(QUIC_INTERNAL_ERROR); |
| + return; |
| + } |
| + out.set_minimum_size(max_packet_size - kFramingOverhead); |
| + next_state_ = STATE_RECV_REJ; |
| + SendHandshakeMessage(out); |
| + return; |
| + } |
| + |
| + session()->config()->ToHandshakeMessage(&out); |
| + string error_details; |
| + QuicErrorCode error = crypto_config_->FillClientHello( |
| + server_id_, |
| + session()->connection()->connection_id(), |
| + session()->connection()->supported_versions().front(), |
| + cached, |
| + session()->connection()->clock()->WallNow(), |
| + session()->connection()->random_generator(), |
| + channel_id_key_.get(), |
| + &crypto_negotiated_params_, |
| + &out, |
| + &error_details); |
| + if (error != QUIC_NO_ERROR) { |
| + // Flush the cached config so that, if it's bad, the server has a |
| + // chance to send us another in the future. |
| + cached->InvalidateServerConfig(); |
| + CloseConnectionWithDetails(error, error_details); |
| + return; |
| + } |
| + channel_id_sent_ = (channel_id_key_.get() != NULL); |
| + if (cached->proof_verify_details()) { |
| + client_session()->OnProofVerifyDetailsAvailable( |
| + *cached->proof_verify_details()); |
| + } |
| + next_state_ = STATE_RECV_SHLO; |
| + SendHandshakeMessage(out); |
| + // Be prepared to decrypt with the new server write key. |
| + session()->connection()->SetAlternativeDecrypter( |
| + crypto_negotiated_params_.initial_crypters.decrypter.release(), |
| + ENCRYPTION_INITIAL, |
| + true /* latch once used */); |
| + // Send subsequent packets under encryption on the assumption that the |
| + // server will accept the handshake. |
| + session()->connection()->SetEncrypter( |
| + ENCRYPTION_INITIAL, |
| + crypto_negotiated_params_.initial_crypters.encrypter.release()); |
| + session()->connection()->SetDefaultEncryptionLevel( |
| + ENCRYPTION_INITIAL); |
| + if (!encryption_established_) { |
| + encryption_established_ = true; |
| + session()->OnCryptoHandshakeEvent( |
| + QuicSession::ENCRYPTION_FIRST_ESTABLISHED); |
| + } else { |
| + session()->OnCryptoHandshakeEvent( |
| + QuicSession::ENCRYPTION_REESTABLISHED); |
| } |
| } |
| +void QuicCryptoClientStream::DoReceiveREJ( |
| + const CryptoHandshakeMessage* in, |
| + QuicCryptoClientConfig::CachedState* cached) { |
| + // We sent a dummy CHLO because we didn't have enough information to |
| + // perform a handshake, or we sent a full hello that the server |
| + // rejected. Here we hope to have a REJ that contains the information |
| + // that we need. |
| + if (in->tag() != kREJ) { |
| + next_state_ = STATE_DONE; |
| + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
| + "Expected REJ"); |
| + return; |
| + } |
| + string error_details; |
| + QuicErrorCode error = crypto_config_->ProcessRejection( |
| + *in, session()->connection()->clock()->WallNow(), cached, |
| + server_id_.is_https(), &crypto_negotiated_params_, &error_details); |
| + if (error != QUIC_NO_ERROR) { |
| + next_state_ = STATE_DONE; |
| + CloseConnectionWithDetails(error, error_details); |
| + return; |
| + } |
| + if (!cached->proof_valid()) { |
| + if (!server_id_.is_https()) { |
| + // We don't check the certificates for insecure QUIC connections. |
| + SetCachedProofValid(cached); |
| + } else if (!cached->signature().empty()) { |
| + // Note that we only verify the proof if the cached proof is not |
| + // valid. If the cached proof is valid here, someone else must have |
| + // just added the server config to the cache and verified the proof, |
| + // so we can assume no CA trust changes or certificate expiration |
| + // has happened since then. |
| + next_state_ = STATE_VERIFY_PROOF; |
| + return; |
| + } |
| + } |
| + next_state_ = STATE_GET_CHANNEL_ID; |
| +} |
| + |
| QuicAsyncStatus QuicCryptoClientStream::DoVerifyProof( |
| QuicCryptoClientConfig::CachedState* cached) { |
| ProofVerifier* verifier = crypto_config_->proof_verifier(); |
| @@ -499,15 +405,16 @@ QuicAsyncStatus QuicCryptoClientStream::DoVerifyProof( |
| return status; |
| } |
| -QuicErrorCode QuicCryptoClientStream::DoVerifyProofComplete( |
| +void QuicCryptoClientStream::DoVerifyProofComplete( |
| QuicCryptoClientConfig::CachedState* cached) { |
| if (!verify_ok_) { |
| + next_state_ = STATE_DONE; |
| client_session()->OnProofVerifyDetailsAvailable(*verify_details_); |
| UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed", |
| handshake_confirmed()); |
| CloseConnectionWithDetails( |
| QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_); |
| - return QUIC_PROOF_INVALID; |
| + return; |
| } |
| // Check if generation_counter has changed between STATE_VERIFY_PROOF and |
| @@ -520,10 +427,142 @@ QuicErrorCode QuicCryptoClientStream::DoVerifyProofComplete( |
| if (!handshake_confirmed()) { |
| next_state_ = STATE_GET_CHANNEL_ID; |
| } else { |
| - next_state_ = STATE_VERIFY_PROOF_DONE; |
| + next_state_ = STATE_DONE; |
| } |
| } |
| - return QUIC_NO_ERROR; |
| +} |
| + |
| +QuicAsyncStatus QuicCryptoClientStream::DoGetChannelID( |
| + QuicCryptoClientConfig::CachedState* cached) { |
| + next_state_ = STATE_GET_CHANNEL_ID_COMPLETE; |
| + channel_id_key_.reset(); |
| + if (!RequiresChannelID(cached)) { |
| + next_state_ = STATE_SEND_CHLO; |
| + return QUIC_SUCCESS; |
| + } |
| + |
| + ChannelIDSourceCallbackImpl* channel_id_source_callback = |
| + new ChannelIDSourceCallbackImpl(this); |
| + QuicAsyncStatus status = |
| + crypto_config_->channel_id_source()->GetChannelIDKey( |
| + server_id_.host(), &channel_id_key_, |
| + channel_id_source_callback); |
| + |
| + switch (status) { |
| + case QUIC_PENDING: |
| + channel_id_source_callback_ = channel_id_source_callback; |
| + DVLOG(1) << "Looking up channel ID"; |
| + break; |
| + case QUIC_FAILURE: |
| + delete channel_id_source_callback; |
| + CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, |
| + "Channel ID lookup failed"); |
| + break; |
| + case QUIC_SUCCESS: |
| + delete channel_id_source_callback; |
| + break; |
| + } |
| + return status; |
| +} |
| + |
| +void QuicCryptoClientStream::DoGetChannelIDComplete() { |
| + if (!channel_id_key_.get()) { |
| + next_state_ = STATE_DONE; |
| + CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, |
| + "Channel ID lookup failed"); |
| + } else { |
| + next_state_ = STATE_SEND_CHLO; |
| + } |
| +} |
| + |
| +void QuicCryptoClientStream::DoReceiveSHLO( |
| + const CryptoHandshakeMessage* in, |
| + QuicCryptoClientConfig::CachedState* cached) { |
| + // We sent a CHLO that we expected to be accepted and now we're hoping |
| + // for a SHLO from the server to confirm that. |
| + if (in->tag() == kREJ) { |
| + // alternative_decrypter will be NULL if the original alternative |
| + // decrypter latched and became the primary decrypter. That happens |
| + // if we received a message encrypted with the INITIAL key. |
| + if (session()->connection()->alternative_decrypter() == NULL) { |
| + next_state_ = STATE_DONE; |
| + // The rejection was sent encrypted! |
| + CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, |
| + "encrypted REJ message"); |
| + return; |
| + } |
| + next_state_ = STATE_RECV_REJ; |
| + return; |
| + } |
| + |
| + next_state_ = STATE_DONE; |
| + if (in->tag() != kSHLO) { |
| + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
| + "Expected SHLO or REJ"); |
| + return; |
| + } |
| + |
| + // alternative_decrypter will be NULL if the original alternative |
| + // decrypter latched and became the primary decrypter. That happens |
| + // if we received a message encrypted with the INITIAL key. |
| + if (session()->connection()->alternative_decrypter() != NULL) { |
| + // The server hello was sent without encryption. |
| + CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, |
| + "unencrypted SHLO message"); |
| + return; |
| + } |
| + |
| + string error_details; |
| + QuicErrorCode error = crypto_config_->ProcessServerHello( |
| + *in, session()->connection()->connection_id(), |
| + session()->connection()->server_supported_versions(), |
| + cached, &crypto_negotiated_params_, &error_details); |
| + |
| + if (error != QUIC_NO_ERROR) { |
| + CloseConnectionWithDetails(error, "Server hello invalid: " + error_details); |
| + return; |
| + } |
| + error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details); |
| + if (error != QUIC_NO_ERROR) { |
| + CloseConnectionWithDetails(error, "Server hello invalid: " + error_details); |
| + return; |
| + } |
| + session()->OnConfigNegotiated(); |
| + |
| + CrypterPair* crypters = &crypto_negotiated_params_.forward_secure_crypters; |
| + // TODO(agl): we don't currently latch this decrypter because the idea |
| + // has been floated that the server shouldn't send packets encrypted |
| + // with the FORWARD_SECURE key until it receives a FORWARD_SECURE |
| + // packet from the client. |
| + session()->connection()->SetAlternativeDecrypter( |
| + crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE, |
| + false /* don't latch */); |
| + session()->connection()->SetEncrypter( |
| + ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release()); |
| + session()->connection()->SetDefaultEncryptionLevel( |
| + ENCRYPTION_FORWARD_SECURE); |
| + |
| + handshake_confirmed_ = true; |
| + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); |
| + session()->connection()->OnHandshakeComplete(); |
| +} |
| + |
| +void QuicCryptoClientStream::DoInitializeServerConfigUpdate( |
| + QuicCryptoClientConfig::CachedState* cached) { |
| + if (!server_id_.is_https()) { |
| + // We don't check the certificates for insecure QUIC connections. |
| + SetCachedProofValid(cached); |
| + next_state_ = STATE_DONE; |
| + return; |
| + } |
| + if (!cached->IsEmpty() && !cached->signature().empty()) { |
| + // Note that we verify the proof even if the cached proof is valid. |
| + DCHECK(crypto_config_->proof_verifier()); |
| + next_state_ = STATE_VERIFY_PROOF; |
| + return; |
| + } |
| + UMA_HISTOGRAM_COUNTS("Net.QuicNumServerConfigUpdateMessagesIgnored", 1); |
| + next_state_ = STATE_DONE; |
| } |
| void QuicCryptoClientStream::SetCachedProofValid( |