OLD | NEW |
(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 ¶ms->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 ¶ms->aead, |
| 442 NULL) || |
| 443 !CryptoUtils::FindMutualTag(config->kexs, |
| 444 their_key_exchanges, num_their_key_exchanges, |
| 445 CryptoUtils::LOCAL_PRIORITY, |
| 446 ¶ms->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, ¶ms->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 |
OLD | NEW |