• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "net/quic/quic_crypto_server_stream.h"
6 
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/crypto/source_address_token.h"
13 #include "net/quic/quic_config.h"
14 #include "net/quic/quic_protocol.h"
15 #include "net/quic/quic_session.h"
16 
17 namespace net {
18 
OnAckNotification(int num_original_packets,int num_original_bytes,int num_retransmitted_packets,int num_retransmitted_bytes,QuicTime::Delta delta_largest_observed)19 void ServerHelloNotifier::OnAckNotification(
20     int num_original_packets,
21     int num_original_bytes,
22     int num_retransmitted_packets,
23     int num_retransmitted_bytes,
24     QuicTime::Delta delta_largest_observed) {
25   server_stream_->OnServerHelloAcked();
26 }
27 
QuicCryptoServerStream(const QuicCryptoServerConfig & crypto_config,QuicSession * session)28 QuicCryptoServerStream::QuicCryptoServerStream(
29     const QuicCryptoServerConfig& crypto_config,
30     QuicSession* session)
31     : QuicCryptoStream(session),
32       crypto_config_(crypto_config),
33       validate_client_hello_cb_(NULL),
34       num_handshake_messages_(0),
35       num_server_config_update_messages_sent_(0) {
36 }
37 
~QuicCryptoServerStream()38 QuicCryptoServerStream::~QuicCryptoServerStream() {
39   CancelOutstandingCallbacks();
40 }
41 
CancelOutstandingCallbacks()42 void QuicCryptoServerStream::CancelOutstandingCallbacks() {
43   // Detach from the validation callback.  Calling this multiple times is safe.
44   if (validate_client_hello_cb_ != NULL) {
45     validate_client_hello_cb_->Cancel();
46   }
47 }
48 
OnHandshakeMessage(const CryptoHandshakeMessage & message)49 void QuicCryptoServerStream::OnHandshakeMessage(
50     const CryptoHandshakeMessage& message) {
51   QuicCryptoStream::OnHandshakeMessage(message);
52   ++num_handshake_messages_;
53 
54   // Do not process handshake messages after the handshake is confirmed.
55   if (handshake_confirmed_) {
56     CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
57     return;
58   }
59 
60   if (message.tag() != kCHLO) {
61     CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
62     return;
63   }
64 
65   if (validate_client_hello_cb_ != NULL) {
66     // Already processing some other handshake message.  The protocol
67     // does not allow for clients to send multiple handshake messages
68     // before the server has a chance to respond.
69     CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO);
70     return;
71   }
72 
73   validate_client_hello_cb_ = new ValidateCallback(this);
74   return crypto_config_.ValidateClientHello(
75       message,
76       session()->connection()->peer_address(),
77       session()->connection()->clock(),
78       validate_client_hello_cb_);
79 }
80 
FinishProcessingHandshakeMessage(const CryptoHandshakeMessage & message,const ValidateClientHelloResultCallback::Result & result)81 void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
82     const CryptoHandshakeMessage& message,
83     const ValidateClientHelloResultCallback::Result& result) {
84   // Clear the callback that got us here.
85   DCHECK(validate_client_hello_cb_ != NULL);
86   validate_client_hello_cb_ = NULL;
87 
88   string error_details;
89   CryptoHandshakeMessage reply;
90   QuicErrorCode error = ProcessClientHello(
91       message, result, &reply, &error_details);
92 
93   if (error != QUIC_NO_ERROR) {
94     CloseConnectionWithDetails(error, error_details);
95     return;
96   }
97 
98   if (reply.tag() != kSHLO) {
99     SendHandshakeMessage(reply);
100     return;
101   }
102 
103   // If we are returning a SHLO then we accepted the handshake.
104   QuicConfig* config = session()->config();
105   OverrideQuicConfigDefaults(config);
106   error = config->ProcessPeerHello(message, CLIENT, &error_details);
107   if (error != QUIC_NO_ERROR) {
108     CloseConnectionWithDetails(error, error_details);
109     return;
110   }
111   session()->OnConfigNegotiated();
112 
113   config->ToHandshakeMessage(&reply);
114 
115   // Receiving a full CHLO implies the client is prepared to decrypt with
116   // the new server write key.  We can start to encrypt with the new server
117   // write key.
118   //
119   // NOTE: the SHLO will be encrypted with the new server write key.
120   session()->connection()->SetEncrypter(
121       ENCRYPTION_INITIAL,
122       crypto_negotiated_params_.initial_crypters.encrypter.release());
123   session()->connection()->SetDefaultEncryptionLevel(
124       ENCRYPTION_INITIAL);
125   // Set the decrypter immediately so that we no longer accept unencrypted
126   // packets.
127   session()->connection()->SetDecrypter(
128       crypto_negotiated_params_.initial_crypters.decrypter.release(),
129       ENCRYPTION_INITIAL);
130 
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   if (session()->connection()->version() <= QUIC_VERSION_21) {
134     SendHandshakeMessage(reply);
135   } else {
136     scoped_refptr<ServerHelloNotifier> server_hello_notifier(
137         new ServerHelloNotifier(this));
138     SendHandshakeMessage(reply, server_hello_notifier.get());
139   }
140 
141   session()->connection()->SetEncrypter(
142       ENCRYPTION_FORWARD_SECURE,
143       crypto_negotiated_params_.forward_secure_crypters.encrypter.release());
144   session()->connection()->SetDefaultEncryptionLevel(
145       ENCRYPTION_FORWARD_SECURE);
146   session()->connection()->SetAlternativeDecrypter(
147       crypto_negotiated_params_.forward_secure_crypters.decrypter.release(),
148       ENCRYPTION_FORWARD_SECURE, false /* don't latch */);
149 
150   encryption_established_ = true;
151   handshake_confirmed_ = true;
152   session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
153 
154   // Now that the handshake is complete, send an updated server config and
155   // source-address token to the client.
156   SendServerConfigUpdate(NULL);
157 }
158 
SendServerConfigUpdate(const CachedNetworkParameters * cached_network_params)159 void QuicCryptoServerStream::SendServerConfigUpdate(
160     const CachedNetworkParameters* cached_network_params) {
161   if (session()->connection()->version() <= QUIC_VERSION_21 ||
162       !handshake_confirmed_) {
163     return;
164   }
165 
166   CryptoHandshakeMessage server_config_update_message;
167   if (!crypto_config_.BuildServerConfigUpdateMessage(
168           session()->connection()->peer_address(),
169           session()->connection()->clock(),
170           session()->connection()->random_generator(),
171           crypto_negotiated_params_,
172           cached_network_params,
173           &server_config_update_message)) {
174     DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
175     return;
176   }
177 
178   DVLOG(1) << "Server: Sending server config update: "
179            << server_config_update_message.DebugString();
180   const QuicData& data = server_config_update_message.GetSerialized();
181   WriteOrBufferData(string(data.data(), data.length()), false, NULL);
182 
183   ++num_server_config_update_messages_sent_;
184 }
185 
OnServerHelloAcked()186 void QuicCryptoServerStream::OnServerHelloAcked() {
187   session()->connection()->OnHandshakeComplete();
188 }
189 
GetBase64SHA256ClientChannelID(string * output) const190 bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
191     string* output) const {
192   if (!encryption_established_ ||
193       crypto_negotiated_params_.channel_id.empty()) {
194     return false;
195   }
196 
197   const string& channel_id(crypto_negotiated_params_.channel_id);
198   scoped_ptr<crypto::SecureHash> hash(
199       crypto::SecureHash::Create(crypto::SecureHash::SHA256));
200   hash->Update(channel_id.data(), channel_id.size());
201   uint8 digest[32];
202   hash->Finish(digest, sizeof(digest));
203 
204   base::Base64Encode(string(
205       reinterpret_cast<const char*>(digest), sizeof(digest)), output);
206   // Remove padding.
207   size_t len = output->size();
208   if (len >= 2) {
209     if ((*output)[len - 1] == '=') {
210       len--;
211       if ((*output)[len - 1] == '=') {
212         len--;
213       }
214       output->resize(len);
215     }
216   }
217   return true;
218 }
219 
ProcessClientHello(const CryptoHandshakeMessage & message,const ValidateClientHelloResultCallback::Result & result,CryptoHandshakeMessage * reply,string * error_details)220 QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
221     const CryptoHandshakeMessage& message,
222     const ValidateClientHelloResultCallback::Result& result,
223     CryptoHandshakeMessage* reply,
224     string* error_details) {
225   return crypto_config_.ProcessClientHello(
226       result,
227       session()->connection()->connection_id(),
228       session()->connection()->peer_address(),
229       session()->connection()->version(),
230       session()->connection()->supported_versions(),
231       session()->connection()->clock(),
232       session()->connection()->random_generator(),
233       &crypto_negotiated_params_, reply, error_details);
234 }
235 
OverrideQuicConfigDefaults(QuicConfig * config)236 void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) {
237 }
238 
ValidateCallback(QuicCryptoServerStream * parent)239 QuicCryptoServerStream::ValidateCallback::ValidateCallback(
240     QuicCryptoServerStream* parent) : parent_(parent) {
241 }
242 
Cancel()243 void QuicCryptoServerStream::ValidateCallback::Cancel() {
244   parent_ = NULL;
245 }
246 
RunImpl(const CryptoHandshakeMessage & client_hello,const Result & result)247 void QuicCryptoServerStream::ValidateCallback::RunImpl(
248     const CryptoHandshakeMessage& client_hello,
249     const Result& result) {
250   if (parent_ != NULL) {
251     parent_->FinishProcessingHandshakeMessage(client_hello, result);
252   }
253 }
254 
255 }  // namespace net
256