OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/quic/quic_crypto_client_stream.h" | 5 #include "net/quic/quic_crypto_client_stream.h" |
6 | 6 |
7 #include "net/quic/crypto/crypto_protocol.h" | 7 #include "net/quic/crypto/crypto_protocol.h" |
8 #include "net/quic/crypto/crypto_utils.h" | 8 #include "net/quic/crypto/crypto_utils.h" |
9 #include "net/quic/quic_protocol.h" | 9 #include "net/quic/quic_protocol.h" |
10 #include "net/quic/quic_session.h" | 10 #include "net/quic/quic_session.h" |
11 | 11 |
12 namespace net { | 12 namespace net { |
13 | 13 |
14 QuicCryptoClientStream::QuicCryptoClientStream(QuicSession* session, | 14 QuicCryptoClientStream::QuicCryptoClientStream( |
15 const string& server_hostname) | 15 const string& server_hostname, |
| 16 const QuicConfig& config, |
| 17 QuicSession* session, |
| 18 QuicCryptoClientConfig* crypto_config) |
16 : QuicCryptoStream(session), | 19 : QuicCryptoStream(session), |
17 next_state_(STATE_IDLE), | 20 next_state_(STATE_IDLE), |
| 21 num_client_hellos_(0), |
| 22 config_(config), |
| 23 crypto_config_(crypto_config), |
18 decrypter_pushed_(false), | 24 decrypter_pushed_(false), |
19 server_hostname_(server_hostname) { | 25 server_hostname_(server_hostname) { |
20 config_.SetDefaults(); | |
21 crypto_config_.SetDefaults(); | |
22 } | 26 } |
23 | 27 |
24 QuicCryptoClientStream::~QuicCryptoClientStream() { | 28 QuicCryptoClientStream::~QuicCryptoClientStream() { |
25 } | 29 } |
26 | 30 |
27 void QuicCryptoClientStream::OnHandshakeMessage( | 31 void QuicCryptoClientStream::OnHandshakeMessage( |
28 const CryptoHandshakeMessage& message) { | 32 const CryptoHandshakeMessage& message) { |
29 DoHandshakeLoop(&message); | 33 DoHandshakeLoop(&message); |
30 } | 34 } |
31 | 35 |
32 bool QuicCryptoClientStream::CryptoConnect() { | 36 bool QuicCryptoClientStream::CryptoConnect() { |
33 next_state_ = STATE_SEND_CHLO; | 37 next_state_ = STATE_SEND_CHLO; |
34 DoHandshakeLoop(NULL); | 38 DoHandshakeLoop(NULL); |
35 return true; | 39 return true; |
36 } | 40 } |
37 | 41 |
38 const QuicNegotiatedParameters& | 42 const QuicNegotiatedParameters& |
39 QuicCryptoClientStream::negotiated_params() const { | 43 QuicCryptoClientStream::negotiated_params() const { |
40 return negotiated_params_; | 44 return negotiated_params_; |
41 } | 45 } |
42 | 46 |
43 const QuicCryptoNegotiatedParameters& | 47 const QuicCryptoNegotiatedParameters& |
44 QuicCryptoClientStream::crypto_negotiated_params() const { | 48 QuicCryptoClientStream::crypto_negotiated_params() const { |
45 return crypto_negotiated_params_; | 49 return crypto_negotiated_params_; |
46 } | 50 } |
47 | 51 |
| 52 // kMaxClientHellos is the maximum number of times that we'll send a client |
| 53 // hello. The value 3 accounts for: |
| 54 // * One failure due to an incorrect or missing source-address token. |
| 55 // * One failure due the server's certificate chain being unavailible and the |
| 56 // server being unwilling to send it without a valid source-address token. |
| 57 static const int kMaxClientHellos = 3; |
| 58 |
48 void QuicCryptoClientStream::DoHandshakeLoop( | 59 void QuicCryptoClientStream::DoHandshakeLoop( |
49 const CryptoHandshakeMessage* in) { | 60 const CryptoHandshakeMessage* in) { |
50 CryptoHandshakeMessage out; | 61 CryptoHandshakeMessage out; |
51 QuicErrorCode error; | 62 QuicErrorCode error; |
52 string error_details; | 63 string error_details; |
53 | 64 |
54 if (in != NULL) { | 65 if (in != NULL) { |
55 DLOG(INFO) << "Client received: " << in->DebugString(); | 66 DLOG(INFO) << "Client received: " << in->DebugString(); |
56 } | 67 } |
57 | 68 |
58 for (;;) { | 69 for (;;) { |
59 const State state = next_state_; | 70 const State state = next_state_; |
60 next_state_ = STATE_IDLE; | 71 next_state_ = STATE_IDLE; |
61 switch (state) { | 72 switch (state) { |
62 case STATE_SEND_CHLO: { | 73 case STATE_SEND_CHLO: { |
| 74 if (num_client_hellos_ > kMaxClientHellos) { |
| 75 CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS); |
| 76 return; |
| 77 } |
| 78 num_client_hellos_++; |
| 79 |
63 const QuicCryptoClientConfig::CachedState* cached = | 80 const QuicCryptoClientConfig::CachedState* cached = |
64 crypto_config_.Lookup(server_hostname_); | 81 crypto_config_->Lookup(server_hostname_); |
65 if (!cached || !cached->is_complete()) { | 82 if (!cached || !cached->is_complete()) { |
66 crypto_config_.FillInchoateClientHello(server_hostname_, cached, | 83 crypto_config_->FillInchoateClientHello(server_hostname_, cached, |
67 &out); | 84 &out); |
68 next_state_ = STATE_RECV_REJ; | 85 next_state_ = STATE_RECV_REJ; |
69 DLOG(INFO) << "Client Sending: " << out.DebugString(); | 86 DLOG(INFO) << "Client Sending: " << out.DebugString(); |
70 SendHandshakeMessage(out); | 87 SendHandshakeMessage(out); |
71 return; | 88 return; |
72 } | 89 } |
73 const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); | 90 const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); |
74 config_.ToHandshakeMessage(&out); | 91 config_.ToHandshakeMessage(&out); |
75 error = crypto_config_.FillClientHello( | 92 error = crypto_config_->FillClientHello( |
76 server_hostname_, | 93 server_hostname_, |
77 session()->connection()->guid(), | 94 session()->connection()->guid(), |
78 cached, | 95 cached, |
79 session()->connection()->clock(), | 96 session()->connection()->clock(), |
80 session()->connection()->random_generator(), | 97 session()->connection()->random_generator(), |
81 &crypto_negotiated_params_, | 98 &crypto_negotiated_params_, |
82 &out, | 99 &out, |
83 &error_details); | 100 &error_details); |
84 if (error != QUIC_NO_ERROR) { | 101 if (error != QUIC_NO_ERROR) { |
85 CloseConnectionWithDetails(error, error_details); | 102 CloseConnectionWithDetails(error, error_details); |
86 return; | 103 return; |
87 } | 104 } |
88 error = config_.ProcessFinalPeerHandshake( | 105 error = config_.ProcessFinalPeerHandshake( |
89 *scfg, CryptoUtils::PEER_PRIORITY, &negotiated_params_, | 106 *scfg, CryptoUtils::PEER_PRIORITY, &negotiated_params_, |
90 &error_details); | 107 &error_details); |
91 if (error != QUIC_NO_ERROR) { | 108 if (error != QUIC_NO_ERROR) { |
92 CloseConnectionWithDetails(error, error_details); | 109 CloseConnectionWithDetails(error, error_details); |
93 return; | 110 return; |
94 } | 111 } |
95 next_state_ = STATE_RECV_SHLO; | 112 next_state_ = STATE_RECV_SHLO; |
96 DLOG(INFO) << "Client Sending: " << out.DebugString(); | 113 DLOG(INFO) << "Client Sending: " << out.DebugString(); |
97 SendHandshakeMessage(out); | 114 SendHandshakeMessage(out); |
98 // Be prepared to decrypt with the new server write key. | 115 // Be prepared to decrypt with the new server write key. |
99 session()->connection()->PushDecrypter( | 116 session()->connection()->PushDecrypter( |
100 crypto_negotiated_params_.decrypter.release()); | 117 crypto_negotiated_params_.decrypter.release()); |
101 decrypter_pushed_ = true; | 118 decrypter_pushed_ = true; |
102 return; | 119 return; |
103 } | 120 } |
104 case STATE_RECV_REJ: | 121 case STATE_RECV_REJ: |
105 // We sent a dummy CHLO because we don't have enough information to | 122 // We sent a dummy CHLO because we didn't have enough information to |
106 // perform a handshake. Here we hope to have a REJ that contains the | 123 // perform a handshake, or we sent a full hello that the server |
107 // information that we need. | 124 // rejected. Here we hope to have a REJ that contains the information |
| 125 // that we need. |
108 if (in->tag() != kREJ) { | 126 if (in->tag() != kREJ) { |
109 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, | 127 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
110 "Expected REJ"); | 128 "Expected REJ"); |
111 return; | 129 return; |
112 } | 130 } |
113 error = crypto_config_.ProcessRejection(server_hostname_, *in, | 131 error = crypto_config_->ProcessRejection(server_hostname_, *in, |
114 &error_details); | 132 &crypto_negotiated_params_, |
| 133 &error_details); |
115 if (error != QUIC_NO_ERROR) { | 134 if (error != QUIC_NO_ERROR) { |
116 CloseConnectionWithDetails(error, error_details); | 135 CloseConnectionWithDetails(error, error_details); |
117 return; | 136 return; |
118 } | 137 } |
119 // Clear any new server write key that we may have set before. | 138 // Clear any new server write key that we may have set before. |
120 if (decrypter_pushed_) { | 139 if (decrypter_pushed_) { |
121 session()->connection()->PopDecrypter(); | 140 session()->connection()->PopDecrypter(); |
122 decrypter_pushed_ = false; | 141 decrypter_pushed_ = false; |
123 } | 142 } |
124 next_state_ = STATE_SEND_CHLO; | 143 next_state_ = STATE_SEND_CHLO; |
125 break; | 144 break; |
126 case STATE_RECV_SHLO: | 145 case STATE_RECV_SHLO: |
127 // We sent a CHLO that we expected to be accepted and now we're hoping | 146 // We sent a CHLO that we expected to be accepted and now we're hoping |
128 // for a SHLO from the server to confirm that. | 147 // for a SHLO from the server to confirm that. |
| 148 if (in->tag() == kREJ) { |
| 149 next_state_ = STATE_RECV_REJ; |
| 150 break; |
| 151 } |
129 if (in->tag() != kSHLO) { | 152 if (in->tag() != kSHLO) { |
130 // TODO(agl): in the future we would attempt the handshake again. | |
131 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, | 153 CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, |
132 "Expected SHLO"); | 154 "Expected SHLO or REJ"); |
133 return; | 155 return; |
134 } | 156 } |
135 // Receiving SHLO implies the server must have processed our full | 157 // Receiving SHLO implies the server must have processed our full |
136 // CHLO and is ready to decrypt with the new client write key. We | 158 // CHLO and is ready to decrypt with the new client write key. We |
137 // can start to encrypt with the new client write key. | 159 // can start to encrypt with the new client write key. |
138 // TODO(wtc): when we support 0-RTT, we will need to change the | 160 // TODO(wtc): when we support 0-RTT, we will need to change the |
139 // encrypter when we send a full CHLO because we will be sending | 161 // encrypter when we send a full CHLO because we will be sending |
140 // application data immediately after. | 162 // application data immediately after. |
141 session()->connection()->ChangeEncrypter( | 163 session()->connection()->ChangeEncrypter( |
142 crypto_negotiated_params_.encrypter.release()); | 164 crypto_negotiated_params_.encrypter.release()); |
143 SetHandshakeComplete(QUIC_NO_ERROR); | 165 SetHandshakeComplete(QUIC_NO_ERROR); |
144 return; | 166 return; |
145 case STATE_IDLE: | 167 case STATE_IDLE: |
146 // This means that the peer sent us a message that we weren't expecting. | 168 // This means that the peer sent us a message that we weren't expecting. |
147 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); | 169 CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); |
148 return; | 170 return; |
149 } | 171 } |
150 } | 172 } |
151 } | 173 } |
152 | 174 |
153 } // namespace net | 175 } // namespace net |
OLD | NEW |