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_server_stream.h"
7 #include "base/base64.h"
8 #include "crypto/secure_hash.h"
9 #include "net/quic/crypto/crypto_protocol.h"
10 #include "net/quic/crypto/crypto_utils.h"
11 #include "net/quic/crypto/quic_crypto_server_config.h"
12 #include "net/quic/proto/cached_network_parameters.pb.h"
13 #include "net/quic/quic_config.h"
14 #include "net/quic/quic_protocol.h"
15 #include "net/quic/quic_session.h"
21 void ServerHelloNotifier::OnAckNotification(
22 int num_retransmitted_packets
,
23 int num_retransmitted_bytes
,
24 QuicTime::Delta delta_largest_observed
) {
25 server_stream_
->OnServerHelloAcked();
28 QuicCryptoServerStream::QuicCryptoServerStream(
29 const QuicCryptoServerConfig
* crypto_config
,
31 : QuicCryptoStream(session
),
32 crypto_config_(crypto_config
),
33 validate_client_hello_cb_(nullptr),
34 num_handshake_messages_(0),
35 num_server_config_update_messages_sent_(0) {
36 DCHECK_EQ(Perspective::IS_SERVER
, session
->connection()->perspective());
39 QuicCryptoServerStream::~QuicCryptoServerStream() {
40 CancelOutstandingCallbacks();
43 void QuicCryptoServerStream::CancelOutstandingCallbacks() {
44 // Detach from the validation callback. Calling this multiple times is safe.
45 if (validate_client_hello_cb_
!= nullptr) {
46 validate_client_hello_cb_
->Cancel();
50 void QuicCryptoServerStream::OnHandshakeMessage(
51 const CryptoHandshakeMessage
& message
) {
52 QuicCryptoStream::OnHandshakeMessage(message
);
53 ++num_handshake_messages_
;
55 // Do not process handshake messages after the handshake is confirmed.
56 if (handshake_confirmed_
) {
57 CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE
);
61 if (message
.tag() != kCHLO
) {
62 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE
);
66 if (validate_client_hello_cb_
!= nullptr) {
67 // Already processing some other handshake message. The protocol
68 // does not allow for clients to send multiple handshake messages
69 // before the server has a chance to respond.
70 CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO
);
74 validate_client_hello_cb_
= new ValidateCallback(this);
75 return crypto_config_
->ValidateClientHello(
76 message
, session()->connection()->peer_address().address(),
77 session()->connection()->clock(), validate_client_hello_cb_
);
80 void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
81 const CryptoHandshakeMessage
& message
,
82 const ValidateClientHelloResultCallback::Result
& result
) {
83 // Clear the callback that got us here.
84 DCHECK(validate_client_hello_cb_
!= nullptr);
85 validate_client_hello_cb_
= nullptr;
88 CryptoHandshakeMessage reply
;
89 QuicErrorCode error
= ProcessClientHello(
90 message
, result
, &reply
, &error_details
);
92 if (error
!= QUIC_NO_ERROR
) {
93 CloseConnectionWithDetails(error
, error_details
);
97 if (reply
.tag() != kSHLO
) {
98 SendHandshakeMessage(reply
);
102 // If we are returning a SHLO then we accepted the handshake.
103 QuicConfig
* config
= session()->config();
104 OverrideQuicConfigDefaults(config
);
105 error
= config
->ProcessPeerHello(message
, CLIENT
, &error_details
);
106 if (error
!= QUIC_NO_ERROR
) {
107 CloseConnectionWithDetails(error
, error_details
);
110 session()->OnConfigNegotiated();
112 config
->ToHandshakeMessage(&reply
);
114 // Receiving a full CHLO implies the client is prepared to decrypt with
115 // the new server write key. We can start to encrypt with the new server
118 // NOTE: the SHLO will be encrypted with the new server write key.
119 session()->connection()->SetEncrypter(
121 crypto_negotiated_params_
.initial_crypters
.encrypter
.release());
122 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL
);
123 // Set the decrypter immediately so that we no longer accept unencrypted
125 session()->connection()->SetDecrypter(
126 crypto_negotiated_params_
.initial_crypters
.decrypter
.release(),
129 // We want to be notified when the SHLO is ACKed so that we can disable
130 // HANDSHAKE_MODE in the sent packet manager.
131 scoped_refptr
<ServerHelloNotifier
> server_hello_notifier(
132 new ServerHelloNotifier(this));
133 SendHandshakeMessage(reply
, server_hello_notifier
.get());
135 session()->connection()->SetEncrypter(
136 ENCRYPTION_FORWARD_SECURE
,
137 crypto_negotiated_params_
.forward_secure_crypters
.encrypter
.release());
138 session()->connection()->SetAlternativeDecrypter(
139 crypto_negotiated_params_
.forward_secure_crypters
.decrypter
.release(),
140 ENCRYPTION_FORWARD_SECURE
, false /* don't latch */);
142 encryption_established_
= true;
143 handshake_confirmed_
= true;
144 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED
);
147 void QuicCryptoServerStream::SendServerConfigUpdate(
148 const CachedNetworkParameters
* cached_network_params
) {
149 if (!handshake_confirmed_
) {
153 CryptoHandshakeMessage server_config_update_message
;
154 if (!crypto_config_
->BuildServerConfigUpdateMessage(
155 previous_source_address_tokens_
,
156 session()->connection()->self_address().address(),
157 session()->connection()->peer_address().address(),
158 session()->connection()->clock(),
159 session()->connection()->random_generator(),
160 crypto_negotiated_params_
, cached_network_params
,
161 &server_config_update_message
)) {
162 DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
166 DVLOG(1) << "Server: Sending server config update: "
167 << server_config_update_message
.DebugString();
168 const QuicData
& data
= server_config_update_message
.GetSerialized();
169 WriteOrBufferData(string(data
.data(), data
.length()), false, nullptr);
171 ++num_server_config_update_messages_sent_
;
174 void QuicCryptoServerStream::OnServerHelloAcked() {
175 session()->connection()->OnHandshakeComplete();
178 void QuicCryptoServerStream::set_previous_cached_network_params(
179 CachedNetworkParameters cached_network_params
) {
180 previous_cached_network_params_
.reset(
181 new CachedNetworkParameters(cached_network_params
));
184 bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
185 string
* output
) const {
186 if (!encryption_established_
||
187 crypto_negotiated_params_
.channel_id
.empty()) {
191 const string
& channel_id(crypto_negotiated_params_
.channel_id
);
192 scoped_ptr
<crypto::SecureHash
> hash(
193 crypto::SecureHash::Create(crypto::SecureHash::SHA256
));
194 hash
->Update(channel_id
.data(), channel_id
.size());
196 hash
->Finish(digest
, sizeof(digest
));
198 base::Base64Encode(string(
199 reinterpret_cast<const char*>(digest
), sizeof(digest
)), output
);
201 size_t len
= output
->size();
203 if ((*output
)[len
- 1] == '=') {
205 if ((*output
)[len
- 1] == '=') {
214 QuicErrorCode
QuicCryptoServerStream::ProcessClientHello(
215 const CryptoHandshakeMessage
& message
,
216 const ValidateClientHelloResultCallback::Result
& result
,
217 CryptoHandshakeMessage
* reply
,
218 string
* error_details
) {
219 // Store the bandwidth estimate from the client.
220 if (result
.cached_network_params
.bandwidth_estimate_bytes_per_second() > 0) {
221 previous_cached_network_params_
.reset(
222 new CachedNetworkParameters(result
.cached_network_params
));
224 previous_source_address_tokens_
= result
.info
.source_address_tokens
;
226 return crypto_config_
->ProcessClientHello(
227 result
, session()->connection()->connection_id(),
228 session()->connection()->self_address().address(),
229 session()->connection()->peer_address(),
230 session()->connection()->version(),
231 session()->connection()->supported_versions(),
232 session()->connection()->clock(),
233 session()->connection()->random_generator(), &crypto_negotiated_params_
,
234 reply
, error_details
);
237 void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig
* config
) {
240 const CachedNetworkParameters
*
241 QuicCryptoServerStream::previous_cached_network_params() const {
242 return previous_cached_network_params_
.get();
245 QuicCryptoServerStream::ValidateCallback::ValidateCallback(
246 QuicCryptoServerStream
* parent
) : parent_(parent
) {
249 void QuicCryptoServerStream::ValidateCallback::Cancel() { parent_
= nullptr; }
251 void QuicCryptoServerStream::ValidateCallback::RunImpl(
252 const CryptoHandshakeMessage
& client_hello
,
253 const Result
& result
) {
254 if (parent_
!= nullptr) {
255 parent_
->FinishProcessingHandshakeMessage(client_hello
, result
);