Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: net/quic/crypto/crypto_utils.cc

Issue 2193073003: Move shared files in net/quic/ into net/quic/core/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: io_thread_unittest.cc Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/quic/crypto/crypto_utils.h ('k') | net/quic/crypto/crypto_utils_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013 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/crypto/crypto_utils.h"
6
7 #include <memory>
8
9 #include "crypto/hkdf.h"
10 #include "crypto/secure_hash.h"
11 #include "net/base/url_util.h"
12 #include "net/quic/crypto/crypto_handshake.h"
13 #include "net/quic/crypto/crypto_protocol.h"
14 #include "net/quic/crypto/quic_decrypter.h"
15 #include "net/quic/crypto/quic_encrypter.h"
16 #include "net/quic/crypto/quic_random.h"
17 #include "net/quic/quic_bug_tracker.h"
18 #include "net/quic/quic_time.h"
19 #include "net/quic/quic_utils.h"
20 #include "url/url_canon.h"
21
22 using base::StringPiece;
23 using std::numeric_limits;
24 using std::string;
25
26 namespace net {
27
28 // static
29 void CryptoUtils::GenerateNonce(QuicWallTime now,
30 QuicRandom* random_generator,
31 StringPiece orbit,
32 string* nonce) {
33 // a 4-byte timestamp + 28 random bytes.
34 nonce->reserve(kNonceSize);
35 nonce->resize(kNonceSize);
36
37 uint32_t gmt_unix_time = static_cast<uint32_t>(now.ToUNIXSeconds());
38 // The time in the nonce must be encoded in big-endian because the
39 // strike-register depends on the nonces being ordered by time.
40 (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24);
41 (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16);
42 (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8);
43 (*nonce)[3] = static_cast<char>(gmt_unix_time);
44 size_t bytes_written = 4;
45
46 if (orbit.size() == 8) {
47 memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size());
48 bytes_written += orbit.size();
49 }
50
51 random_generator->RandBytes(&(*nonce)[bytes_written],
52 kNonceSize - bytes_written);
53 }
54
55 // static
56 bool CryptoUtils::IsValidSNI(StringPiece sni) {
57 // TODO(rtenneti): Support RFC2396 hostname.
58 // NOTE: Microsoft does NOT enforce this spec, so if we throw away hostnames
59 // based on the above spec, we may be losing some hostnames that windows
60 // would consider valid. By far the most common hostname character NOT
61 // accepted by the above spec is '_'.
62 url::CanonHostInfo host_info;
63 string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info));
64 return !host_info.IsIPAddress() &&
65 IsCanonicalizedHostCompliant(canonicalized_host) &&
66 sni.find_last_of('.') != string::npos;
67 }
68
69 // static
70 string CryptoUtils::NormalizeHostname(const char* hostname) {
71 url::CanonHostInfo host_info;
72 string host(CanonicalizeHost(hostname, &host_info));
73
74 // Walk backwards over the string, stopping at the first trailing dot.
75 size_t host_end = host.length();
76 while (host_end != 0 && host[host_end - 1] == '.') {
77 host_end--;
78 }
79
80 // Erase the trailing dots.
81 if (host_end != host.length()) {
82 host.erase(host_end, host.length() - host_end);
83 }
84 return host;
85 }
86
87 // static
88 bool CryptoUtils::DeriveKeys(StringPiece premaster_secret,
89 QuicTag aead,
90 StringPiece client_nonce,
91 StringPiece server_nonce,
92 const string& hkdf_input,
93 Perspective perspective,
94 Diversification diversification,
95 CrypterPair* crypters,
96 string* subkey_secret) {
97 crypters->encrypter.reset(QuicEncrypter::Create(aead));
98 crypters->decrypter.reset(QuicDecrypter::Create(aead));
99 size_t key_bytes = crypters->encrypter->GetKeySize();
100 size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize();
101 size_t subkey_secret_bytes =
102 subkey_secret == nullptr ? 0 : premaster_secret.length();
103
104 StringPiece nonce = client_nonce;
105 string nonce_storage;
106 if (!server_nonce.empty()) {
107 nonce_storage = client_nonce.as_string() + server_nonce.as_string();
108 nonce = nonce_storage;
109 }
110
111 crypto::HKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes,
112 nonce_prefix_bytes, subkey_secret_bytes);
113
114 // Key derivation depends on the key diversification method being employed.
115 // both the client and the server support never doing key diversification.
116 // The server also supports immediate diversification, and the client
117 // supports pending diversification.
118 switch (diversification.mode()) {
119 case Diversification::NEVER: {
120 if (perspective == Perspective::IS_SERVER) {
121 if (!crypters->encrypter->SetKey(hkdf.server_write_key()) ||
122 !crypters->encrypter->SetNoncePrefix(hkdf.server_write_iv()) ||
123 !crypters->decrypter->SetKey(hkdf.client_write_key()) ||
124 !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv())) {
125 return false;
126 }
127 } else {
128 if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
129 !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
130 !crypters->decrypter->SetKey(hkdf.server_write_key()) ||
131 !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
132 return false;
133 }
134 }
135 break;
136 }
137 case Diversification::PENDING: {
138 if (perspective == Perspective::IS_SERVER) {
139 QUIC_BUG << "Pending diversification is only for clients.";
140 return false;
141 }
142
143 if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
144 !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
145 !crypters->decrypter->SetPreliminaryKey(hkdf.server_write_key()) ||
146 !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
147 return false;
148 }
149 break;
150 }
151 case Diversification::NOW: {
152 if (perspective == Perspective::IS_CLIENT) {
153 QUIC_BUG << "Immediate diversification is only for servers.";
154 return false;
155 }
156
157 string key, nonce_prefix;
158 QuicDecrypter::DiversifyPreliminaryKey(
159 hkdf.server_write_key(), hkdf.server_write_iv(),
160 *diversification.nonce(), key_bytes, nonce_prefix_bytes, &key,
161 &nonce_prefix);
162 if (!crypters->decrypter->SetKey(hkdf.client_write_key()) ||
163 !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
164 !crypters->encrypter->SetKey(key) ||
165 !crypters->encrypter->SetNoncePrefix(nonce_prefix)) {
166 return false;
167 }
168 break;
169 }
170 default:
171 DCHECK(false);
172 }
173
174 if (subkey_secret != nullptr) {
175 hkdf.subkey_secret().CopyToString(subkey_secret);
176 }
177
178 return true;
179 }
180
181 // static
182 bool CryptoUtils::ExportKeyingMaterial(StringPiece subkey_secret,
183 StringPiece label,
184 StringPiece context,
185 size_t result_len,
186 string* result) {
187 for (size_t i = 0; i < label.length(); i++) {
188 if (label[i] == '\0') {
189 LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs";
190 return false;
191 }
192 }
193 // Create HKDF info input: null-terminated label + length-prefixed context
194 if (context.length() >= numeric_limits<uint32_t>::max()) {
195 LOG(ERROR) << "Context value longer than 2^32";
196 return false;
197 }
198 uint32_t context_length = static_cast<uint32_t>(context.length());
199 string info = label.as_string();
200 info.push_back('\0');
201 info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length));
202 info.append(context.data(), context.length());
203
204 crypto::HKDF hkdf(subkey_secret, StringPiece() /* no salt */, info,
205 result_len, 0 /* no fixed IV */, 0 /* no subkey secret */);
206 hkdf.client_write_key().CopyToString(result);
207 return true;
208 }
209
210 // static
211 uint64_t CryptoUtils::ComputeLeafCertHash(const std::string& cert) {
212 return QuicUtils::FNV1a_64_Hash(cert.data(), cert.size());
213 }
214
215 QuicErrorCode CryptoUtils::ValidateServerHello(
216 const CryptoHandshakeMessage& server_hello,
217 const QuicVersionVector& negotiated_versions,
218 string* error_details) {
219 DCHECK(error_details != nullptr);
220
221 if (server_hello.tag() != kSHLO) {
222 *error_details = "Bad tag";
223 return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
224 }
225
226 const QuicTag* supported_version_tags;
227 size_t num_supported_versions;
228
229 if (server_hello.GetTaglist(kVER, &supported_version_tags,
230 &num_supported_versions) != QUIC_NO_ERROR) {
231 *error_details = "server hello missing version list";
232 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
233 }
234 if (!negotiated_versions.empty()) {
235 bool mismatch = num_supported_versions != negotiated_versions.size();
236 for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
237 mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
238 negotiated_versions[i];
239 }
240 // The server sent a list of supported versions, and the connection
241 // reports that there was a version negotiation during the handshake.
242 // Ensure that these two lists are identical.
243 if (mismatch) {
244 *error_details = "Downgrade attack detected";
245 return QUIC_VERSION_NEGOTIATION_MISMATCH;
246 }
247 }
248 return QUIC_NO_ERROR;
249 }
250
251 QuicErrorCode CryptoUtils::ValidateClientHello(
252 const CryptoHandshakeMessage& client_hello,
253 QuicVersion version,
254 const QuicVersionVector& supported_versions,
255 string* error_details) {
256 if (client_hello.tag() != kCHLO) {
257 *error_details = "Bad tag";
258 return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
259 }
260
261 // If the client's preferred version is not the version we are currently
262 // speaking, then the client went through a version negotiation. In this
263 // case, we need to make sure that we actually do not support this version
264 // and that it wasn't a downgrade attack.
265 QuicTag client_version_tag;
266 if (client_hello.GetUint32(kVER, &client_version_tag) != QUIC_NO_ERROR) {
267 *error_details = "client hello missing version list";
268 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
269 }
270 QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
271 if (client_version != version) {
272 // Just because client_version is a valid version enum doesn't mean that
273 // this server actually supports that version, so we check to see if
274 // it's actually in the supported versions list.
275 for (size_t i = 0; i < supported_versions.size(); ++i) {
276 if (client_version == supported_versions[i]) {
277 *error_details = "Downgrade attack detected";
278 return QUIC_VERSION_NEGOTIATION_MISMATCH;
279 }
280 }
281 }
282 return QUIC_NO_ERROR;
283 }
284
285 #define RETURN_STRING_LITERAL(x) \
286 case x: \
287 return #x
288
289 // Returns the name of the HandshakeFailureReason as a char*
290 // static
291 const char* CryptoUtils::HandshakeFailureReasonToString(
292 HandshakeFailureReason reason) {
293 switch (reason) {
294 RETURN_STRING_LITERAL(HANDSHAKE_OK);
295 RETURN_STRING_LITERAL(CLIENT_NONCE_UNKNOWN_FAILURE);
296 RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_FAILURE);
297 RETURN_STRING_LITERAL(CLIENT_NONCE_NOT_UNIQUE_FAILURE);
298 RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_ORBIT_FAILURE);
299 RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_TIME_FAILURE);
300 RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT);
301 RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_FAILURE);
302
303 RETURN_STRING_LITERAL(SERVER_NONCE_DECRYPTION_FAILURE);
304 RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_FAILURE);
305 RETURN_STRING_LITERAL(SERVER_NONCE_NOT_UNIQUE_FAILURE);
306 RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_TIME_FAILURE);
307 RETURN_STRING_LITERAL(SERVER_NONCE_REQUIRED_FAILURE);
308
309 RETURN_STRING_LITERAL(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
310 RETURN_STRING_LITERAL(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
311
312 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_INVALID_FAILURE);
313 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE);
314 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_PARSE_FAILURE);
315 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE);
316 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE);
317 RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE);
318
319 RETURN_STRING_LITERAL(INVALID_EXPECTED_LEAF_CERTIFICATE);
320 RETURN_STRING_LITERAL(MAX_FAILURE_REASON);
321 }
322 // Return a default value so that we return this when |reason| doesn't match
323 // any HandshakeFailureReason.. This can happen when the message by the peer
324 // (attacker) has invalid reason.
325 return "INVALID_HANDSHAKE_FAILURE_REASON";
326 }
327
328 // static
329 void CryptoUtils::HashHandshakeMessage(const CryptoHandshakeMessage& message,
330 string* output) {
331 const QuicData& serialized = message.GetSerialized();
332 std::unique_ptr<crypto::SecureHash> hash(
333 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
334 hash->Update(serialized.data(), serialized.length());
335 uint8_t digest[32];
336 hash->Finish(digest, sizeof(digest));
337 output->assign(reinterpret_cast<const char*>(&digest), sizeof(digest));
338 }
339
340 } // namespace net
OLDNEW
« no previous file with comments | « net/quic/crypto/crypto_utils.h ('k') | net/quic/crypto/crypto_utils_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698