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/cached_network_parameters.h"
10 #include "net/quic/crypto/crypto_protocol.h"
11 #include "net/quic/crypto/crypto_utils.h"
12 #include "net/quic/crypto/quic_crypto_server_config.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(
77 session()->connection()->peer_address(),
78 session()->connection()->clock(),
79 validate_client_hello_cb_
);
82 void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
83 const CryptoHandshakeMessage
& message
,
84 const ValidateClientHelloResultCallback::Result
& result
) {
85 // Clear the callback that got us here.
86 DCHECK(validate_client_hello_cb_
!= nullptr);
87 validate_client_hello_cb_
= nullptr;
90 CryptoHandshakeMessage reply
;
91 QuicErrorCode error
= ProcessClientHello(
92 message
, result
, &reply
, &error_details
);
94 if (error
!= QUIC_NO_ERROR
) {
95 CloseConnectionWithDetails(error
, error_details
);
99 if (reply
.tag() != kSHLO
) {
100 SendHandshakeMessage(reply
);
104 // If we are returning a SHLO then we accepted the handshake.
105 QuicConfig
* config
= session()->config();
106 OverrideQuicConfigDefaults(config
);
107 error
= config
->ProcessPeerHello(message
, CLIENT
, &error_details
);
108 if (error
!= QUIC_NO_ERROR
) {
109 CloseConnectionWithDetails(error
, error_details
);
112 session()->OnConfigNegotiated();
114 config
->ToHandshakeMessage(&reply
);
116 // Receiving a full CHLO implies the client is prepared to decrypt with
117 // the new server write key. We can start to encrypt with the new server
120 // NOTE: the SHLO will be encrypted with the new server write key.
121 session()->connection()->SetEncrypter(
123 crypto_negotiated_params_
.initial_crypters
.encrypter
.release());
124 session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL
);
125 // Set the decrypter immediately so that we no longer accept unencrypted
127 session()->connection()->SetDecrypter(
128 crypto_negotiated_params_
.initial_crypters
.decrypter
.release(),
131 // We want to be notified when the SHLO is ACKed so that we can disable
132 // HANDSHAKE_MODE in the sent packet manager.
133 scoped_refptr
<ServerHelloNotifier
> server_hello_notifier(
134 new ServerHelloNotifier(this));
135 SendHandshakeMessage(reply
, server_hello_notifier
.get());
137 session()->connection()->SetEncrypter(
138 ENCRYPTION_FORWARD_SECURE
,
139 crypto_negotiated_params_
.forward_secure_crypters
.encrypter
.release());
140 session()->connection()->SetAlternativeDecrypter(
141 crypto_negotiated_params_
.forward_secure_crypters
.decrypter
.release(),
142 ENCRYPTION_FORWARD_SECURE
, false /* don't latch */);
144 encryption_established_
= true;
145 handshake_confirmed_
= true;
146 session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED
);
149 void QuicCryptoServerStream::SendServerConfigUpdate(
150 const CachedNetworkParameters
* cached_network_params
) {
151 if (!handshake_confirmed_
) {
155 CryptoHandshakeMessage server_config_update_message
;
156 if (!crypto_config_
->BuildServerConfigUpdateMessage(
157 previous_source_address_tokens_
,
158 session()->connection()->self_address(),
159 session()->connection()->peer_address(),
160 session()->connection()->clock(),
161 session()->connection()->random_generator(),
162 crypto_negotiated_params_
, cached_network_params
,
163 &server_config_update_message
)) {
164 DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
168 DVLOG(1) << "Server: Sending server config update: "
169 << server_config_update_message
.DebugString();
170 const QuicData
& data
= server_config_update_message
.GetSerialized();
171 WriteOrBufferData(string(data
.data(), data
.length()), false, nullptr);
173 ++num_server_config_update_messages_sent_
;
176 void QuicCryptoServerStream::OnServerHelloAcked() {
177 session()->connection()->OnHandshakeComplete();
180 void QuicCryptoServerStream::set_previous_cached_network_params(
181 CachedNetworkParameters cached_network_params
) {
182 previous_cached_network_params_
.reset(
183 new CachedNetworkParameters(cached_network_params
));
186 bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
187 string
* output
) const {
188 if (!encryption_established_
||
189 crypto_negotiated_params_
.channel_id
.empty()) {
193 const string
& channel_id(crypto_negotiated_params_
.channel_id
);
194 scoped_ptr
<crypto::SecureHash
> hash(
195 crypto::SecureHash::Create(crypto::SecureHash::SHA256
));
196 hash
->Update(channel_id
.data(), channel_id
.size());
198 hash
->Finish(digest
, sizeof(digest
));
200 base::Base64Encode(string(
201 reinterpret_cast<const char*>(digest
), sizeof(digest
)), output
);
203 size_t len
= output
->size();
205 if ((*output
)[len
- 1] == '=') {
207 if ((*output
)[len
- 1] == '=') {
216 QuicErrorCode
QuicCryptoServerStream::ProcessClientHello(
217 const CryptoHandshakeMessage
& message
,
218 const ValidateClientHelloResultCallback::Result
& result
,
219 CryptoHandshakeMessage
* reply
,
220 string
* error_details
) {
221 // Store the bandwidth estimate from the client.
222 if (result
.cached_network_params
.bandwidth_estimate_bytes_per_second() > 0) {
223 previous_cached_network_params_
.reset(
224 new CachedNetworkParameters(result
.cached_network_params
));
226 previous_source_address_tokens_
= result
.info
.source_address_tokens
;
228 return crypto_config_
->ProcessClientHello(
229 result
, session()->connection()->connection_id(),
230 session()->connection()->self_address(),
231 session()->connection()->peer_address(),
232 session()->connection()->version(),
233 session()->connection()->supported_versions(),
234 session()->connection()->clock(),
235 session()->connection()->random_generator(), &crypto_negotiated_params_
,
236 reply
, error_details
);
239 void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig
* config
) {
242 const CachedNetworkParameters
*
243 QuicCryptoServerStream::previous_cached_network_params() const {
244 return previous_cached_network_params_
.get();
247 QuicCryptoServerStream::ValidateCallback::ValidateCallback(
248 QuicCryptoServerStream
* parent
) : parent_(parent
) {
251 void QuicCryptoServerStream::ValidateCallback::Cancel() { parent_
= nullptr; }
253 void QuicCryptoServerStream::ValidateCallback::RunImpl(
254 const CryptoHandshakeMessage
& client_hello
,
255 const Result
& result
) {
256 if (parent_
!= nullptr) {
257 parent_
->FinishProcessingHandshakeMessage(client_hello
, result
);