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

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

Issue 14411004: Land Recent QUIC Changes (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use CONFIG_VERSION insteaf of VERSION Created 7 years, 8 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 | Annotate | Revision Log
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_server_config.h"
6
7 #include "base/stl_util.h"
8 #include "crypto/hkdf.h"
9 #include "crypto/secure_hash.h"
10 #include "net/quic/crypto/aes_128_gcm_decrypter.h"
11 #include "net/quic/crypto/aes_128_gcm_encrypter.h"
12 #include "net/quic/crypto/crypto_framer.h"
13 #include "net/quic/crypto/crypto_server_config_protobuf.h"
14 #include "net/quic/crypto/crypto_utils.h"
15 #include "net/quic/crypto/curve25519_key_exchange.h"
16 #include "net/quic/crypto/key_exchange.h"
17 #include "net/quic/crypto/p256_key_exchange.h"
18 #include "net/quic/crypto/proof_source.h"
19 #include "net/quic/crypto/quic_decrypter.h"
20 #include "net/quic/crypto/quic_encrypter.h"
21 #include "net/quic/crypto/quic_random.h"
22 #include "net/quic/crypto/source_address_token.h"
23 #include "net/quic/crypto/strike_register.h"
24 #include "net/quic/quic_clock.h"
25 #include "net/quic/quic_protocol.h"
26
27 using base::StringPiece;
28 using crypto::SecureHash;
29 using std::map;
30 using std::string;
31 using std::vector;
32
33 namespace net {
34
35 // static
36 const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
37
38 QuicCryptoServerConfig::QuicCryptoServerConfig(
39 StringPiece source_address_token_secret)
40 // AES-GCM is used to encrypt and authenticate source address tokens. The
41 // full, 96-bit nonce is used but we must ensure that an attacker cannot
42 // obtain two source address tokens with the same nonce. This occurs with
43 // probability 0.5 after 2**48 values. We assume that obtaining 2**48
44 // source address tokens is not possible: at a rate of 10M packets per
45 // second, it would still take the attacker a year to obtain the needed
46 // number of packets.
47 //
48 // TODO(agl): switch to an encrypter with a larger nonce space (i.e.
49 // Salsa20+Poly1305).
50 : strike_register_lock_(),
51 source_address_token_encrypter_(new Aes128GcmEncrypter),
52 source_address_token_decrypter_(new Aes128GcmDecrypter) {
53 crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
54 "QUIC source address token key",
55 source_address_token_encrypter_->GetKeySize(),
56 0 /* no fixed IV needed */);
57 source_address_token_encrypter_->SetKey(hkdf.server_write_key());
58 source_address_token_decrypter_->SetKey(hkdf.server_write_key());
59 }
60
61 QuicCryptoServerConfig::~QuicCryptoServerConfig() {
62 STLDeleteValues(&configs_);
63 }
64
65 // static
66 QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig(
67 QuicRandom* rand,
68 const QuicClock* clock,
69 const CryptoHandshakeMessage& extra_tags) {
70 CryptoHandshakeMessage msg;
71
72 const string curve25519_private_key =
73 Curve25519KeyExchange::NewPrivateKey(rand);
74 scoped_ptr<Curve25519KeyExchange> curve25519(
75 Curve25519KeyExchange::New(curve25519_private_key));
76 StringPiece curve25519_public_value = curve25519->public_value();
77
78 const string p256_private_key =
79 P256KeyExchange::NewPrivateKey();
80 scoped_ptr<P256KeyExchange> p256(
81 P256KeyExchange::New(p256_private_key));
82 StringPiece p256_public_value = p256->public_value();
83
84 string encoded_public_values;
85 // First two bytes encode the length of the public value.
86 encoded_public_values.push_back(curve25519_public_value.size());
87 encoded_public_values.push_back(curve25519_public_value.size() >> 8);
88 encoded_public_values.append(curve25519_public_value.data(),
89 curve25519_public_value.size());
90 encoded_public_values.push_back(p256_public_value.size());
91 encoded_public_values.push_back(p256_public_value.size() >> 8);
92 encoded_public_values.append(p256_public_value.data(),
93 p256_public_value.size());
94
95 msg.set_tag(kSCFG);
96 msg.SetTaglist(kKEXS, kC255, kP256, 0);
97 msg.SetTaglist(kAEAD, kAESG, 0);
98 msg.SetValue(kVERS, static_cast<uint16>(0));
99 msg.SetStringPiece(kPUBS, encoded_public_values);
100 msg.Insert(extra_tags.tag_value_map().begin(),
101 extra_tags.tag_value_map().end());
102
103 char scid_bytes[16];
104 rand->RandBytes(scid_bytes, sizeof(scid_bytes));
105 msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes)));
106
107 char orbit_bytes[kOrbitSize];
108 rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
109 msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes)));
110
111 scoped_ptr<QuicData> serialized(
112 CryptoFramer::ConstructHandshakeMessage(msg));
113
114 scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf);
115 config->set_config(serialized->AsStringPiece());
116 QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key();
117 curve25519_key->set_tag(kC255);
118 curve25519_key->set_private_key(curve25519_private_key);
119 QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key();
120 p256_key->set_tag(kP256);
121 p256_key->set_private_key(p256_private_key);
122
123 return config.release();
124 }
125
126 CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
127 QuicServerConfigProtobuf* protobuf) {
128 scoped_ptr<CryptoHandshakeMessage> msg(
129 CryptoFramer::ParseMessage(protobuf->config()));
130
131 if (!msg.get()) {
132 LOG(WARNING) << "Failed to parse server config message";
133 return NULL;
134 }
135 if (msg->tag() != kSCFG) {
136 LOG(WARNING) << "Server config message has tag "
137 << msg->tag() << " expected "
138 << kSCFG;
139 return NULL;
140 }
141
142 scoped_ptr<Config> config(new Config);
143 config->serialized = protobuf->config();
144
145 StringPiece scid;
146 if (!msg->GetStringPiece(kSCID, &scid)) {
147 LOG(WARNING) << "Server config message is missing SCID";
148 return NULL;
149 }
150 config->id = scid.as_string();
151
152 const CryptoTag* aead_tags;
153 size_t aead_len;
154 if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) {
155 LOG(WARNING) << "Server config message is missing AEAD";
156 return NULL;
157 }
158 config->aead = vector<CryptoTag>(aead_tags, aead_tags + aead_len);
159
160 const CryptoTag* kexs_tags;
161 size_t kexs_len;
162 if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) {
163 LOG(WARNING) << "Server config message is missing KEXS";
164 return NULL;
165 }
166
167 StringPiece orbit;
168 if (!msg->GetStringPiece(kORBT, &orbit)) {
169 LOG(WARNING) << "Server config message is missing OBIT";
170 return NULL;
171 }
172
173 if (orbit.size() != kOrbitSize) {
174 LOG(WARNING) << "Orbit value in server config is the wrong length."
175 " Got " << orbit.size() << " want " << kOrbitSize;
176 return NULL;
177 }
178 COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size);
179 memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
180
181 if (kexs_len != protobuf->key_size()) {
182 LOG(WARNING) << "Server config has "
183 << kexs_len
184 << " key exchange methods configured, but "
185 << protobuf->key_size()
186 << " private keys";
187 return NULL;
188 }
189
190 for (size_t i = 0; i < kexs_len; i++) {
191 const CryptoTag tag = kexs_tags[i];
192 string private_key;
193
194 config->kexs.push_back(tag);
195
196 for (size_t j = 0; j < protobuf->key_size(); j++) {
197 const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i);
198 if (key.tag() == tag) {
199 private_key = key.private_key();
200 break;
201 }
202 }
203
204 if (private_key.empty()) {
205 LOG(WARNING) << "Server config contains key exchange method without "
206 "corresponding private key: "
207 << tag;
208 return NULL;
209 }
210
211 scoped_ptr<KeyExchange> ka;
212 switch (tag) {
213 case kC255:
214 ka.reset(Curve25519KeyExchange::New(private_key));
215 if (!ka.get()) {
216 LOG(WARNING) << "Server config contained an invalid curve25519"
217 " private key.";
218 return NULL;
219 }
220 break;
221 case kP256:
222 ka.reset(P256KeyExchange::New(private_key));
223 if (!ka.get()) {
224 LOG(WARNING) << "Server config contained an invalid P-256"
225 " private key.";
226 return NULL;
227 }
228 break;
229 default:
230 LOG(WARNING) << "Server config message contains unknown key exchange "
231 "method: "
232 << tag;
233 return NULL;
234 }
235
236 for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin();
237 i != config->key_exchanges.end(); ++i) {
238 if ((*i)->tag() == tag) {
239 LOG(WARNING) << "Duplicate key exchange in config: " << tag;
240 return NULL;
241 }
242 }
243
244 config->key_exchanges.push_back(ka.release());
245 }
246
247 if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) {
248 LOG(WARNING) << "Server config message is missing version";
249 return NULL;
250 }
251
252 if (config->version != QuicCryptoConfig::CONFIG_VERSION) {
253 LOG(WARNING) << "Server config specifies an unsupported version";
254 return NULL;
255 }
256
257 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
258 sha256->Update(protobuf->config().data(), protobuf->config().size());
259 char id_bytes[16];
260 sha256->Finish(id_bytes, sizeof(id_bytes));
261 const string id(id_bytes, sizeof(id_bytes));
262
263 configs_[id] = config.release();
264 active_config_ = id;
265
266 return msg.release();
267 }
268
269 CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
270 QuicRandom* rand,
271 const QuicClock* clock,
272 const CryptoHandshakeMessage& extra_tags) {
273 scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig(
274 rand, clock, extra_tags));
275 return AddConfig(config.get());
276 }
277
278 QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
279 const CryptoHandshakeMessage& client_hello,
280 QuicGuid guid,
281 const IPEndPoint& client_ip,
282 QuicTime::Delta now_since_unix_epoch,
283 QuicRandom* rand,
284 QuicCryptoNegotiatedParameters *params,
285 CryptoHandshakeMessage* out,
286 string* error_details) const {
287 DCHECK(error_details);
288
289 CHECK(!configs_.empty());
290
291 // FIXME(agl): we should use the client's SCID, not just the active config.
292 map<ServerConfigID, Config*>::const_iterator it =
293 configs_.find(active_config_);
294 if (it == configs_.end()) {
295 *error_details = "No valid server config loaded";
296 return QUIC_CRYPTO_INTERNAL_ERROR;
297 }
298 const Config* const config(it->second);
299
300 bool valid_source_address_token = false;
301 StringPiece srct;
302 if (client_hello.GetStringPiece(kSRCT, &srct) &&
303 ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) {
304 valid_source_address_token = true;
305 }
306
307 const string fresh_source_address_token =
308 NewSourceAddressToken(client_ip, rand, now_since_unix_epoch);
309
310 // If we previously sent a REJ to this client then we may have stored a
311 // server nonce in |params|. In which case, we know that the connection
312 // is unique because the server nonce will be mixed into the key generation.
313 bool unique_by_server_nonce = !params->server_nonce.empty();
314 // If we can't ensure uniqueness by a server nonce, then we will try and use
315 // the strike register.
316 bool unique_by_strike_register = false;
317
318 StringPiece client_nonce;
319 bool client_nonce_well_formed = false;
320 if (client_hello.GetStringPiece(kNONC, &client_nonce) &&
321 client_nonce.size() == kNonceSize) {
322 client_nonce_well_formed = true;
323 base::AutoLock auto_lock(strike_register_lock_);
324
325 if (strike_register_.get() == NULL) {
326 strike_register_.reset(new StrikeRegister(
327 // TODO(agl): these magic numbers should come from config.
328 1024 /* max entries */,
329 static_cast<uint32>(now_since_unix_epoch.ToSeconds()),
330 600 /* window secs */, config->orbit));
331 }
332 unique_by_strike_register = strike_register_->Insert(
333 reinterpret_cast<const uint8*>(client_nonce.data()),
334 static_cast<uint32>(now_since_unix_epoch.ToSeconds()));
335 }
336
337 out->Clear();
338
339 StringPiece sni;
340 client_hello.GetStringPiece(kSNI, &sni);
341
342 StringPiece scid;
343 if (!client_hello.GetStringPiece(kSCID, &scid) ||
344 scid.as_string() != config->id ||
345 !valid_source_address_token ||
346 !client_nonce_well_formed ||
347 (!unique_by_strike_register &&
348 !unique_by_server_nonce)) {
349 // If the client didn't provide a server config ID, or gave the wrong one,
350 // then the handshake cannot possibly complete. We reject the handshake and
351 // give the client enough information to do better next time.
352 out->set_tag(kREJ);
353 out->SetStringPiece(kSCFG, config->serialized);
354 out->SetStringPiece(kSRCT, fresh_source_address_token);
355 if (params->server_nonce.empty()) {
356 CryptoUtils::GenerateNonce(
357 now_since_unix_epoch, rand,
358 StringPiece(reinterpret_cast<const char*>(config->orbit),
359 sizeof(config->orbit)),
360 &params->server_nonce);
361 }
362 out->SetStringPiece(kNONC, params->server_nonce);
363
364 // The client may have requested a certificate chain.
365 const CryptoTag* their_proof_demands;
366 size_t num_their_proof_demands;
367
368 if (valid_source_address_token &&
369 proof_source_.get() != NULL &&
370 !sni.empty() &&
371 client_hello.GetTaglist(kPDMD, &their_proof_demands,
372 &num_their_proof_demands) == QUIC_NO_ERROR) {
373 for (size_t i = 0; i < num_their_proof_demands; i++) {
374 if (their_proof_demands[i] != kX509) {
375 continue;
376 }
377
378 // TODO(agl): in the future we will hopefully have a cached-info like
379 // mechanism where we can omit certificates that the client already has.
380 // In that case, the certificate chain may be small enough to include
381 // without a source-address token. But, for now, we always send the full
382 // chain and we always need a valid source-address token.
383
384 const vector<string>* certs;
385 string signature;
386 if (!proof_source_->GetProof(sni.as_string(), config->serialized,
387 &certs, &signature)) {
388 break;
389 }
390
391 // TODO(agl): compress and omit certificates where possible based on
392 // the client's cached certificates.
393 size_t cert_bytes = 0;
394 for (vector<string>::const_iterator i = certs->begin();
395 i != certs->end(); ++i) {
396 cert_bytes += i->size();
397 }
398 // There's a three byte length-prefix for each certificate.
399 cert_bytes += certs->size()*3;
400 scoped_ptr<char[]> buf(new char[cert_bytes]);
401
402 size_t j = 0;
403 for (vector<string>::const_iterator i = certs->begin();
404 i != certs->end(); ++i) {
405 size_t len = i->size();
406 buf[j++] = len;
407 buf[j++] = len >> 8;
408 buf[j++] = len >> 16;
409 memcpy(&buf[j], i->data(), i->size());
410 j += i->size();
411 }
412
413 DCHECK_EQ(j, cert_bytes);
414
415 out->SetStringPiece(kCERT, StringPiece(buf.get(), cert_bytes));
416 out->SetStringPiece(kPROF, signature);
417 break;
418 }
419 }
420
421 return QUIC_NO_ERROR;
422 }
423
424 const CryptoTag* their_aeads;
425 const CryptoTag* their_key_exchanges;
426 size_t num_their_aeads, num_their_key_exchanges;
427 if (client_hello.GetTaglist(kAEAD, &their_aeads,
428 &num_their_aeads) != QUIC_NO_ERROR ||
429 client_hello.GetTaglist(kKEXS, &their_key_exchanges,
430 &num_their_key_exchanges) != QUIC_NO_ERROR ||
431 num_their_aeads != 1 ||
432 num_their_key_exchanges != 1) {
433 *error_details = "Missing or invalid AEAD or KEXS";
434 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
435 }
436
437 size_t key_exchange_index;
438 if (!CryptoUtils::FindMutualTag(config->aead,
439 their_aeads, num_their_aeads,
440 CryptoUtils::LOCAL_PRIORITY,
441 &params->aead,
442 NULL) ||
443 !CryptoUtils::FindMutualTag(config->kexs,
444 their_key_exchanges, num_their_key_exchanges,
445 CryptoUtils::LOCAL_PRIORITY,
446 &params->key_exchange,
447 &key_exchange_index)) {
448 *error_details = "Unsupported AEAD or KEXS";
449 return QUIC_CRYPTO_NO_SUPPORT;
450 }
451
452 StringPiece public_value;
453 if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
454 *error_details = "Missing public value";
455 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
456 }
457
458 if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey(
459 public_value, &params->premaster_secret)) {
460 *error_details = "Invalid public value";
461 return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
462 }
463
464 params->server_config_id = scid.as_string();
465
466 string hkdf_input(QuicCryptoConfig::kLabel,
467 strlen(QuicCryptoConfig::kLabel) + 1);
468 hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid));
469
470 const QuicData& client_hello_serialized = client_hello.GetSerialized();
471 hkdf_input.append(client_hello_serialized.data(),
472 client_hello_serialized.length());
473 hkdf_input.append(config->serialized);
474
475 CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input,
476 CryptoUtils::SERVER);
477
478 out->set_tag(kSHLO);
479 out->SetStringPiece(kSRCT, fresh_source_address_token);
480 return QUIC_NO_ERROR;
481 }
482
483 void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) {
484 proof_source_.reset(proof_source);
485 }
486
487 string QuicCryptoServerConfig::NewSourceAddressToken(
488 const IPEndPoint& ip,
489 QuicRandom* rand,
490 QuicTime::Delta now_since_epoch) const {
491 SourceAddressToken source_address_token;
492 source_address_token.set_ip(ip.ToString());
493 source_address_token.set_timestamp(now_since_epoch.ToSeconds());
494
495 string plaintext = source_address_token.SerializeAsString();
496 char nonce[12];
497 DCHECK_EQ(sizeof(nonce),
498 source_address_token_encrypter_->GetNoncePrefixSize() +
499 sizeof(QuicPacketSequenceNumber));
500 rand->RandBytes(nonce, sizeof(nonce));
501
502 size_t ciphertext_size =
503 source_address_token_encrypter_->GetCiphertextSize(plaintext.size());
504 string result;
505 result.resize(sizeof(nonce) + ciphertext_size);
506 memcpy(&result[0], &nonce, sizeof(nonce));
507
508 if (!source_address_token_encrypter_->Encrypt(
509 StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext,
510 reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) {
511 DCHECK(false);
512 return string();
513 }
514
515 return result;
516 }
517
518 bool QuicCryptoServerConfig::ValidateSourceAddressToken(
519 StringPiece token,
520 const IPEndPoint& ip,
521 QuicTime::Delta now_since_epoch) const {
522 char nonce[12];
523 DCHECK_EQ(sizeof(nonce),
524 source_address_token_encrypter_->GetNoncePrefixSize() +
525 sizeof(QuicPacketSequenceNumber));
526
527 if (token.size() <= sizeof(nonce)) {
528 return false;
529 }
530 memcpy(&nonce, token.data(), sizeof(nonce));
531 token.remove_prefix(sizeof(nonce));
532
533 unsigned char plaintext_stack[128];
534 scoped_ptr<unsigned char[]> plaintext_heap;
535 unsigned char* plaintext;
536 if (token.size() <= sizeof(plaintext_stack)) {
537 plaintext = plaintext_stack;
538 } else {
539 plaintext_heap.reset(new unsigned char[token.size()]);
540 plaintext = plaintext_heap.get();
541 }
542 size_t plaintext_length;
543
544 if (!source_address_token_decrypter_->Decrypt(
545 StringPiece(nonce, sizeof(nonce)), StringPiece(), token,
546 plaintext, &plaintext_length)) {
547 return false;
548 }
549
550 SourceAddressToken source_address_token;
551 if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) {
552 return false;
553 }
554
555 if (source_address_token.ip() != ip.ToString()) {
556 // It's for a different IP address.
557 return false;
558 }
559
560 const QuicTime::Delta delta(now_since_epoch.Subtract(
561 QuicTime::Delta::FromSeconds(source_address_token.timestamp())));
562 const int64 delta_secs = delta.ToSeconds();
563
564 // TODO(agl): consider whether and how these magic values should be moved to
565 // a config.
566 if (delta_secs < -3600) {
567 // We only allow timestamps to be from an hour in the future.
568 return false;
569 }
570
571 if (delta_secs > 86400) {
572 // We allow one day into the past.
573 return false;
574 }
575
576 return true;
577 }
578
579 QuicCryptoServerConfig::Config::Config() {
580 }
581
582 QuicCryptoServerConfig::Config::~Config() {
583 STLDeleteElements(&key_exchanges);
584 }
585
586 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698