diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-23 20:18:55 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-23 20:18:55 +0000 |
commit | fe053f9301b7003d0bbe82baf9001d5b95564b9e (patch) | |
tree | c5356ff0f9fa4754f00d30944f60544edbf20c46 /net | |
parent | 7252963941dffa25e28168c82ab262ae889ffe1d (diff) | |
download | chromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.zip chromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.tar.gz chromium_src-fe053f9301b7003d0bbe82baf9001d5b95564b9e.tar.bz2 |
Land Recent QUIC Changes
QUIC: step 8, server certificate support.
Merge internal change: 44460951
Returning early from framer callbacks on error.
Merge internal change: 44428665
Rename QUIC_VERSION_NOT_SUPPORTED to QUIC_CRYPTO_VERSION_NOT_SUPPORTED.
Merge internal change: 44422561
QUIC: split the server config into its own file.
This change moves QuicCryptoServerConfig into a separate file so that Chromium
need only link it into tests.
Merge internal change: 44397707
QUIC: remove ifs around error_details
They were never needed and clutter up the code.
Merge internal change: 44275147
QUIC: add tests for 0-RTT handshaking using strike-register.
This change fixes a couple of issues and adds a test that performs a 0-RTT
handshake.
Merge internal change: 44272981
R=rch@chromium.org
Review URL: https://codereview.chromium.org/14411004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195897 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
33 files changed, 1298 insertions, 857 deletions
diff --git a/net/net.gyp b/net/net.gyp index 0a6560d..3a78c4d 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -712,6 +712,10 @@ 'quic/crypto/crypto_handshake.cc', 'quic/crypto/crypto_handshake.h', 'quic/crypto/crypto_protocol.h', + 'quic/crypto/crypto_server_config.cc', + 'quic/crypto/crypto_server_config.h', + 'quic/crypto/crypto_server_config_protobuf.cc', + 'quic/crypto/crypto_server_config_protobuf.h', 'quic/crypto/crypto_utils.cc', 'quic/crypto/crypto_utils.h', 'quic/crypto/curve25519_key_exchange.cc', @@ -724,6 +728,7 @@ 'quic/crypto/p256_key_exchange.h', 'quic/crypto/p256_key_exchange_nss.cc', 'quic/crypto/p256_key_exchange_openssl.cc', + 'quic/crypto/proof_source.h', 'quic/crypto/quic_decrypter.cc', 'quic/crypto/quic_decrypter.h', 'quic/crypto/quic_encrypter.cc', @@ -733,6 +738,8 @@ 'quic/crypto/scoped_evp_cipher_ctx.h', 'quic/crypto/strike_register.cc', 'quic/crypto/strike_register.h', + 'quic/crypto/source_address_token.cc', + 'quic/crypto/source_address_token.h', 'quic/quic_bandwidth.cc', 'quic/quic_bandwidth.h', 'quic/quic_blocked_writer_interface.h', @@ -2516,6 +2523,7 @@ 'type': 'static_library', 'dependencies': [ '../base/base.gyp:base', + '../crypto/crypto.gyp:crypto', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../build/temp_gyp/googleurl.gyp:googleurl', '../third_party/openssl/openssl.gyp:openssl', @@ -2587,6 +2595,7 @@ 'type': '<(gtest_target_type)', 'dependencies': [ '../base/base.gyp:test_support_base', + '../crypto/crypto.gyp:crypto', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', 'net', diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 1d9b01b..59ecd42 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -10,11 +10,8 @@ #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" -#include "crypto/hkdf.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" -#include "net/quic/crypto/aes_128_gcm_decrypter.h" -#include "net/quic/crypto/aes_128_gcm_encrypter.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/curve25519_key_exchange.h" @@ -23,7 +20,6 @@ #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" -#include "net/quic/crypto/strike_register.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_protocol.h" @@ -35,23 +31,6 @@ using std::vector; namespace net { -// kVersion contains the one (and, for the moment, only) version number that we -// implement. -static const uint16 kVersion = 0; - -// kLabel is constant that is used in key derivation to tie the resulting key -// to this protocol. -static const char kLabel[] = "QUIC key expansion"; - -using crypto::SecureHash; - -QuicServerConfigProtobuf::QuicServerConfigProtobuf() { -} - -QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { - STLDeleteElements(&keys_); -} - CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0) {} CryptoHandshakeMessage::CryptoHandshakeMessage( @@ -284,6 +263,7 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { case kKEXS: case kAEAD: case kCGST: + case kPDMD: // tag lists if (it->second.size() % sizeof(CryptoTag) == 0) { for (size_t j = 0; j < it->second.size(); j += sizeof(CryptoTag)) { @@ -324,35 +304,6 @@ string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { return ret; } -SourceAddressToken::SourceAddressToken() { -} - -SourceAddressToken::~SourceAddressToken() { -} - -string SourceAddressToken::SerializeAsString() const { - return ip_ + " " + base::Int64ToString(timestamp_); -} - -bool SourceAddressToken::ParseFromArray(unsigned char* plaintext, - size_t plaintext_length) { - string data(reinterpret_cast<const char*>(plaintext), plaintext_length); - std::vector<std::string> results; - base::SplitString(data, ' ', &results); - if (results.size() < 2) { - return false; - } - - int64 timestamp; - if (!base::StringToInt64(results[1], ×tamp)) { - return false; - } - - ip_ = results[0]; - timestamp_ = timestamp; - return true; -} - QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : version(0), key_exchange(0), @@ -363,6 +314,9 @@ QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() { } +// static +const char QuicCryptoConfig::kLabel[] = "QUIC key expansion"; + QuicCryptoConfig::QuicCryptoConfig() : version(0) { } @@ -428,7 +382,7 @@ void QuicCryptoClientConfig::CachedState::set_source_address_token( void QuicCryptoClientConfig::SetDefaults() { // Version must be 0. - version = kVersion; + version = QuicCryptoConfig::CONFIG_VERSION; // Key exchange methods. kexs.resize(2); @@ -468,6 +422,8 @@ void QuicCryptoClientConfig::FillInchoateClientHello( if (cached && !cached->source_address_token().empty()) { out->SetStringPiece(kSRCT, cached->source_address_token()); } + + out->SetTaglist(kPDMD, kX509, 0); } QuicErrorCode QuicCryptoClientConfig::FillClientHello( @@ -479,34 +435,30 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out, string* error_details) const { + DCHECK(error_details != NULL); + FillInchoateClientHello(server_hostname, cached, out); const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); if (!scfg) { // This should never happen as our caller should have checked // cached->is_complete() before calling this function. - if (error_details) { - *error_details = "Handshake not ready"; - } + *error_details = "Handshake not ready"; return QUIC_CRYPTO_INTERNAL_ERROR; } StringPiece scid; if (!scfg->GetStringPiece(kSCID, &scid)) { - if (error_details) { - *error_details = "SCFG missing SCID"; - } + *error_details = "SCFG missing SCID"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } out->SetStringPiece(kSCID, scid); // Calculate the mutual algorithms that the connection is going to use. if (scfg->GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR || - out_params->version != kVersion) { - if (error_details) { - *error_details = "Bad version"; - } - return QUIC_VERSION_NOT_SUPPORTED; + out_params->version != QuicCryptoConfig::CONFIG_VERSION) { + *error_details = "Bad version"; + return QUIC_CRYPTO_VERSION_NOT_SUPPORTED; } const CryptoTag* their_aeads; @@ -516,9 +468,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( &num_their_aeads) != QUIC_NO_ERROR || scfg->GetTaglist(kKEXS, &their_key_exchanges, &num_their_key_exchanges) != QUIC_NO_ERROR) { - if (error_details) { - *error_details = "Missing AEAD or KEXS"; - } + *error_details = "Missing AEAD or KEXS"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } @@ -533,9 +483,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( CryptoUtils::PEER_PRIORITY, &out_params->key_exchange, &key_exchange_index)) { - if (error_details) { - *error_details = "Unsupported AEAD or KEXS"; - } + *error_details = "Unsupported AEAD or KEXS"; return QUIC_CRYPTO_NO_SUPPORT; } out->SetTaglist(kAEAD, out_params->aead, 0); @@ -544,18 +492,14 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( StringPiece public_value; if (scfg->GetNthValue16(kPUBS, key_exchange_index, &public_value) != QUIC_NO_ERROR) { - if (error_details) { - *error_details = "Missing public value"; - } + *error_details = "Missing public value"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } StringPiece orbit; if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { - if (error_details) { - *error_details = "SCFG missing OBIT"; - } + *error_details = "SCFG missing OBIT"; return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } @@ -576,22 +520,19 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( break; default: DCHECK(false); - if (error_details) { - *error_details = "Configured to support an unknown key exchange"; - } + *error_details = "Configured to support an unknown key exchange"; return QUIC_CRYPTO_INTERNAL_ERROR; } if (!key_exchange->CalculateSharedKey(public_value, &out_params->premaster_secret)) { - if (error_details) { - *error_details = "Key exchange failure"; - } + *error_details = "Key exchange failure"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } out->SetStringPiece(kPUBS, key_exchange->public_value()); - string hkdf_input(kLabel, arraysize(kLabel)); + string hkdf_input(QuicCryptoConfig::kLabel, + strlen(QuicCryptoConfig::kLabel) + 1); hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); const QuicData& client_hello_serialized = out->GetSerialized(); @@ -609,8 +550,9 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( const CryptoHandshakeMessage& rej, QuicCryptoNegotiatedParameters* out_params, string* error_details) { - CachedState* cached; + DCHECK(error_details != NULL); + CachedState* cached; map<string, CachedState*>::const_iterator it = cached_states_.find(server_hostname); if (it == cached_states_.end()) { @@ -622,16 +564,12 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( StringPiece scfg; if (!rej.GetStringPiece(kSCFG, &scfg)) { - if (error_details) { - *error_details = "Missing SCFG"; - } + *error_details = "Missing SCFG"; return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } if (!cached->SetServerConfig(scfg)) { - if (error_details) { - *error_details = "Invalid SCFG"; - } + *error_details = "Invalid SCFG"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } @@ -654,6 +592,8 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( const string& nonce, QuicCryptoNegotiatedParameters* out_params, string* error_details) { + DCHECK(error_details != NULL); + if (server_hello.tag() != kSHLO) { *error_details = "Bad tag"; return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; @@ -666,495 +606,4 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( return QUIC_NO_ERROR; } -// static -const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; - -QuicCryptoServerConfig::QuicCryptoServerConfig( - StringPiece source_address_token_secret) - // AES-GCM is used to encrypt and authenticate source address tokens. The - // full, 96-bit nonce is used but we must ensure that an attacker cannot - // obtain two source address tokens with the same nonce. This occurs with - // probability 0.5 after 2**48 values. We assume that obtaining 2**48 - // source address tokens is not possible: at a rate of 10M packets per - // second, it would still take the attacker a year to obtain the needed - // number of packets. - // - // TODO(agl): switch to an encrypter with a larger nonce space (i.e. - // Salsa20+Poly1305). - : strike_register_lock_(), - source_address_token_encrypter_(new Aes128GcmEncrypter), - source_address_token_decrypter_(new Aes128GcmDecrypter) { - crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, - "QUIC source address token key", - source_address_token_encrypter_->GetKeySize(), - 0 /* no fixed IV needed */); - source_address_token_encrypter_->SetKey(hkdf.server_write_key()); - source_address_token_decrypter_->SetKey(hkdf.server_write_key()); -} - -QuicCryptoServerConfig::~QuicCryptoServerConfig() { - STLDeleteValues(&configs_); -} - -// static -QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags) { - CryptoHandshakeMessage msg; - - const string curve25519_private_key = - Curve25519KeyExchange::NewPrivateKey(rand); - scoped_ptr<Curve25519KeyExchange> curve25519( - Curve25519KeyExchange::New(curve25519_private_key)); - StringPiece curve25519_public_value = curve25519->public_value(); - - const string p256_private_key = - P256KeyExchange::NewPrivateKey(); - scoped_ptr<P256KeyExchange> p256( - P256KeyExchange::New(p256_private_key)); - StringPiece p256_public_value = p256->public_value(); - - string encoded_public_values; - // First two bytes encode the length of the public value. - encoded_public_values.push_back(curve25519_public_value.size()); - encoded_public_values.push_back(curve25519_public_value.size() >> 8); - encoded_public_values.append(curve25519_public_value.data(), - curve25519_public_value.size()); - encoded_public_values.push_back(p256_public_value.size()); - encoded_public_values.push_back(p256_public_value.size() >> 8); - encoded_public_values.append(p256_public_value.data(), - p256_public_value.size()); - - msg.set_tag(kSCFG); - msg.SetTaglist(kKEXS, kC255, kP256, 0); - msg.SetTaglist(kAEAD, kAESG, 0); - msg.SetValue(kVERS, static_cast<uint16>(0)); - msg.SetStringPiece(kPUBS, encoded_public_values); - msg.Insert(extra_tags.tag_value_map().begin(), - extra_tags.tag_value_map().end()); - - char scid_bytes[16]; - rand->RandBytes(scid_bytes, sizeof(scid_bytes)); - msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); - - char orbit_bytes[kOrbitSize]; - rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); - msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes))); - - scoped_ptr<QuicData> serialized( - CryptoFramer::ConstructHandshakeMessage(msg)); - - scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf); - config->set_config(serialized->AsStringPiece()); - QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); - curve25519_key->set_tag(kC255); - curve25519_key->set_private_key(curve25519_private_key); - QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); - p256_key->set_tag(kP256); - p256_key->set_private_key(p256_private_key); - - return config.release(); -} - -CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( - QuicServerConfigProtobuf* protobuf) { - scoped_ptr<CryptoHandshakeMessage> msg( - CryptoFramer::ParseMessage(protobuf->config())); - - if (!msg.get()) { - LOG(WARNING) << "Failed to parse server config message"; - return NULL; - } - if (msg->tag() != kSCFG) { - LOG(WARNING) << "Server config message has tag " - << msg->tag() << " expected " - << kSCFG; - return NULL; - } - - scoped_ptr<Config> config(new Config); - config->serialized = protobuf->config(); - - StringPiece scid; - if (!msg->GetStringPiece(kSCID, &scid)) { - LOG(WARNING) << "Server config message is missing SCID"; - return NULL; - } - config->id = scid.as_string(); - - const CryptoTag* aead_tags; - size_t aead_len; - if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing AEAD"; - return NULL; - } - config->aead = vector<CryptoTag>(aead_tags, aead_tags + aead_len); - - const CryptoTag* kexs_tags; - size_t kexs_len; - if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing KEXS"; - return NULL; - } - - StringPiece orbit; - if (!msg->GetStringPiece(kORBT, &orbit)) { - LOG(WARNING) << "Server config message is missing OBIT"; - return NULL; - } - - if (orbit.size() != kOrbitSize) { - LOG(WARNING) << "Orbit value in server config is the wrong length." - " Got " << orbit.size() << " want " << kOrbitSize; - return NULL; - } - COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size); - memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); - - if (kexs_len != protobuf->key_size()) { - LOG(WARNING) << "Server config has " - << kexs_len - << " key exchange methods configured, but " - << protobuf->key_size() - << " private keys"; - return NULL; - } - - for (size_t i = 0; i < kexs_len; i++) { - const CryptoTag tag = kexs_tags[i]; - string private_key; - - config->kexs.push_back(tag); - - for (size_t j = 0; j < protobuf->key_size(); j++) { - const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i); - if (key.tag() == tag) { - private_key = key.private_key(); - break; - } - } - - if (private_key.empty()) { - LOG(WARNING) << "Server config contains key exchange method without " - "corresponding private key: " - << tag; - return NULL; - } - - scoped_ptr<KeyExchange> ka; - switch (tag) { - case kC255: - ka.reset(Curve25519KeyExchange::New(private_key)); - if (!ka.get()) { - LOG(WARNING) << "Server config contained an invalid curve25519" - " private key."; - return NULL; - } - break; - case kP256: - ka.reset(P256KeyExchange::New(private_key)); - if (!ka.get()) { - LOG(WARNING) << "Server config contained an invalid P-256" - " private key."; - return NULL; - } - break; - default: - LOG(WARNING) << "Server config message contains unknown key exchange " - "method: " - << tag; - return NULL; - } - - for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin(); - i != config->key_exchanges.end(); ++i) { - if ((*i)->tag() == tag) { - LOG(WARNING) << "Duplicate key exchange in config: " << tag; - return NULL; - } - } - - config->key_exchanges.push_back(ka.release()); - } - - if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) { - LOG(WARNING) << "Server config message is missing version"; - return NULL; - } - - if (config->version != kVersion) { - LOG(WARNING) << "Server config specifies an unsupported version"; - return NULL; - } - - scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); - sha256->Update(protobuf->config().data(), protobuf->config().size()); - char id_bytes[16]; - sha256->Finish(id_bytes, sizeof(id_bytes)); - const string id(id_bytes, sizeof(id_bytes)); - - configs_[id] = config.release(); - active_config_ = id; - - return msg.release(); -} - -CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags) { - scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig( - rand, clock, extra_tags)); - return AddConfig(config.get()); -} - -QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, - QuicGuid guid, - const IPEndPoint& client_ip, - QuicTime::Delta now_since_unix_epoch, - QuicRandom* rand, - QuicCryptoNegotiatedParameters *params, - CryptoHandshakeMessage* out, - string* error_details) const { - CHECK(!configs_.empty()); - // FIXME(agl): we should use the client's SCID, not just the active config. - map<ServerConfigID, Config*>::const_iterator it = - configs_.find(active_config_); - if (it == configs_.end()) { - *error_details = "No valid server config loaded"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - const Config* const config(it->second); - - bool valid_source_address_token = false; - StringPiece srct; - if (client_hello.GetStringPiece(kSRCT, &srct) && - ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) { - valid_source_address_token = true; - } - - const string fresh_source_address_token = - NewSourceAddressToken(client_ip, rand, now_since_unix_epoch); - - // If we previously sent a REJ to this client then we may have stored a - // server nonce in |params|. In which case, we know that the connection - // is unique because the server nonce will be mixed into the key generation. - bool unique_by_server_nonce = !params->server_nonce.empty(); - // If we can't ensure uniqueness by a server nonce, then we will try and use - // the strike register. - bool unique_by_strike_register = false; - - StringPiece client_nonce; - bool client_nonce_well_formed = false; - if (client_hello.GetStringPiece(kNONC, &client_nonce) && - client_nonce.size() == kNonceSize) { - client_nonce_well_formed = true; - if (!unique_by_server_nonce) { - base::AutoLock auto_lock(strike_register_lock_); - - if (strike_register_.get() == NULL) { - strike_register_.reset(new StrikeRegister( - // TODO(agl): these magic numbers should come from config. - 1024 /* max entries */, - static_cast<uint32>(now_since_unix_epoch.ToSeconds()), - 600 /* window secs */, config->orbit)); - } - unique_by_strike_register = strike_register_->Insert( - reinterpret_cast<const uint8*>(client_nonce.data()), - static_cast<uint32>(now_since_unix_epoch.ToSeconds())); - } - } - - StringPiece scid; - if (!client_hello.GetStringPiece(kSCID, &scid) || - scid.as_string() != config->id || - !valid_source_address_token || - !client_nonce_well_formed || - (!unique_by_strike_register && - !unique_by_server_nonce)) { - // If the client didn't provide a server config ID, or gave the wrong one, - // then the handshake cannot possibly complete. We reject the handshake and - // give the client enough information to do better next time. - out->Clear(); - out->set_tag(kREJ); - out->SetStringPiece(kSCFG, config->serialized); - out->SetStringPiece(kSRCT, fresh_source_address_token); - if (params->server_nonce.empty()) { - CryptoUtils::GenerateNonce( - now_since_unix_epoch, rand, - StringPiece(reinterpret_cast<const char*>(config->orbit), - sizeof(config->orbit)), - ¶ms->server_nonce); - } - out->SetStringPiece(kNONC, params->server_nonce); - return QUIC_NO_ERROR; - } - - const CryptoTag* their_aeads; - const CryptoTag* their_key_exchanges; - size_t num_their_aeads, num_their_key_exchanges; - if (client_hello.GetTaglist(kAEAD, &their_aeads, - &num_their_aeads) != QUIC_NO_ERROR || - client_hello.GetTaglist(kKEXS, &their_key_exchanges, - &num_their_key_exchanges) != QUIC_NO_ERROR || - num_their_aeads != 1 || - num_their_key_exchanges != 1) { - if (error_details) { - *error_details = "Missing or invalid AEAD or KEXS"; - } - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - size_t key_exchange_index; - if (!CryptoUtils::FindMutualTag(config->aead, - their_aeads, num_their_aeads, - CryptoUtils::LOCAL_PRIORITY, - ¶ms->aead, - NULL) || - !CryptoUtils::FindMutualTag(config->kexs, - their_key_exchanges, num_their_key_exchanges, - CryptoUtils::LOCAL_PRIORITY, - ¶ms->key_exchange, - &key_exchange_index)) { - if (error_details) { - *error_details = "Unsupported AEAD or KEXS"; - } - return QUIC_CRYPTO_NO_SUPPORT; - } - - StringPiece public_value; - if (!client_hello.GetStringPiece(kPUBS, &public_value)) { - if (error_details) { - *error_details = "Missing public value"; - } - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey( - public_value, ¶ms->premaster_secret)) { - if (error_details) { - *error_details = "Invalid public value"; - } - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - params->server_config_id = scid.as_string(); - - string hkdf_input(kLabel, arraysize(kLabel)); - hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); - - const QuicData& client_hello_serialized = client_hello.GetSerialized(); - hkdf_input.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_input.append(config->serialized); - - CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input, - CryptoUtils::SERVER); - - out->set_tag(kSHLO); - out->SetStringPiece(kSRCT, fresh_source_address_token); - return QUIC_NO_ERROR; -} - -string QuicCryptoServerConfig::NewSourceAddressToken( - const IPEndPoint& ip, - QuicRandom* rand, - QuicTime::Delta now_since_epoch) const { - SourceAddressToken source_address_token; - source_address_token.set_ip(ip.ToString()); - source_address_token.set_timestamp(now_since_epoch.ToSeconds()); - - string plaintext = source_address_token.SerializeAsString(); - char nonce[12]; - DCHECK_EQ(sizeof(nonce), - source_address_token_encrypter_->GetNoncePrefixSize() + - sizeof(QuicPacketSequenceNumber)); - rand->RandBytes(nonce, sizeof(nonce)); - - size_t ciphertext_size = - source_address_token_encrypter_->GetCiphertextSize(plaintext.size()); - string result; - result.resize(sizeof(nonce) + ciphertext_size); - memcpy(&result[0], &nonce, sizeof(nonce)); - - if (!source_address_token_encrypter_->Encrypt( - StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext, - reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) { - DCHECK(false); - return string(); - } - - return result; -} - -bool QuicCryptoServerConfig::ValidateSourceAddressToken( - StringPiece token, - const IPEndPoint& ip, - QuicTime::Delta now_since_epoch) const { - char nonce[12]; - DCHECK_EQ(sizeof(nonce), - source_address_token_encrypter_->GetNoncePrefixSize() + - sizeof(QuicPacketSequenceNumber)); - - if (token.size() <= sizeof(nonce)) { - return false; - } - memcpy(&nonce, token.data(), sizeof(nonce)); - token.remove_prefix(sizeof(nonce)); - - unsigned char plaintext_stack[128]; - scoped_ptr<unsigned char[]> plaintext_heap; - unsigned char* plaintext; - if (token.size() <= sizeof(plaintext_stack)) { - plaintext = plaintext_stack; - } else { - plaintext_heap.reset(new unsigned char[token.size()]); - plaintext = plaintext_heap.get(); - } - size_t plaintext_length; - - if (!source_address_token_decrypter_->Decrypt( - StringPiece(nonce, sizeof(nonce)), StringPiece(), token, - plaintext, &plaintext_length)) { - return false; - } - - SourceAddressToken source_address_token; - if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) { - return false; - } - - if (source_address_token.ip() != ip.ToString()) { - // It's for a different IP address. - return false; - } - - const QuicTime::Delta delta(now_since_epoch.Subtract( - QuicTime::Delta::FromSeconds(source_address_token.timestamp()))); - const int64 delta_secs = delta.ToSeconds(); - - // TODO(agl): consider whether and how these magic values should be moved to - // a config. - if (delta_secs < -3600) { - // We only allow timestamps to be from an hour in the future. - return false; - } - - if (delta_secs > 86400) { - // We allow one day into the past. - return false; - } - - return true; -} - -QuicCryptoServerConfig::Config::Config() { -} - -QuicCryptoServerConfig::Config::~Config() { - STLDeleteElements(&key_exchanges); -} - } // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index c597b74..0be50d0 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -11,8 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" -#include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_time.h" @@ -24,12 +22,6 @@ class QuicClock; class QuicDecrypter; class QuicEncrypter; class QuicRandom; -class QuicServerConfigProtobuf; -class StrikeRegister; - -namespace test { -class QuicCryptoServerConfigPeer; -} // namespace test // An intermediate format of a handshake message that's convenient for a // CryptoFramer to serialize from or parse into. @@ -126,90 +118,6 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { mutable scoped_ptr<QuicData> serialized_; }; -// TODO(rch): sync with server more rationally -class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { - public: - class NET_EXPORT_PRIVATE PrivateKey { - public: - CryptoTag tag() const { - return tag_; - } - void set_tag(CryptoTag tag) { - tag_ = tag; - } - std::string private_key() const { - return private_key_; - } - void set_private_key(std::string key) { - private_key_ = key; - } - - private: - CryptoTag tag_; - std::string private_key_; - }; - - QuicServerConfigProtobuf(); - ~QuicServerConfigProtobuf(); - - size_t key_size() const { - return keys_.size(); - } - - const PrivateKey& key(size_t i) const { - DCHECK_GT(keys_.size(), i); - return *keys_[i]; - } - - std::string config() const { - return config_; - } - - void set_config(base::StringPiece config) { - config_ = config.as_string(); - } - - QuicServerConfigProtobuf::PrivateKey* add_key() { - keys_.push_back(new PrivateKey); - return keys_.back(); - } - - private: - std::vector<PrivateKey*> keys_; - std::string config_; -}; - -// TODO(rtenneti): sync with server more rationally. -class NET_EXPORT_PRIVATE SourceAddressToken { - public: - SourceAddressToken(); - ~SourceAddressToken(); - - std::string SerializeAsString() const; - - bool ParseFromArray(unsigned char* plaintext, size_t plaintext_length); - - std::string ip() const { - return ip_; - } - - int64 timestamp() const { - return timestamp_; - } - - void set_ip(base::StringPiece ip) { - ip_ = ip.as_string(); - } - - void set_timestamp(int64 timestamp) { - timestamp_ = timestamp; - } - - private: - std::string ip_; - int64 timestamp_; -}; - // Parameters negotiated by the crypto handshake. struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // Initializes the members to 0 or empty values. @@ -229,6 +137,16 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // QuicCryptoConfig contains common configuration between clients and servers. class NET_EXPORT_PRIVATE QuicCryptoConfig { public: + enum { + // CONFIG_VERSION is the one (and, for the moment, only) version number that + // we implement. + CONFIG_VERSION = 0, + }; + + // kLabel is constant that is used in key derivation to tie the resulting key + // to this protocol. + static const char kLabel[]; + QuicCryptoConfig(); ~QuicCryptoConfig(); @@ -344,128 +262,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { std::map<std::string, CachedState*> cached_states_; }; -// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. -// Unlike a client, a QUIC server can have multiple configurations active in -// order to support clients resuming with a previous configuration. -// TODO(agl): when adding configurations at runtime is added, this object will -// need to consider locking. -class NET_EXPORT_PRIVATE QuicCryptoServerConfig { - public: - // |source_address_token_secret|: secret key material used for encrypting and - // decrypting source address tokens. It can be of any length as it is fed - // into a KDF before use. In tests, use TESTING. - explicit QuicCryptoServerConfig( - base::StringPiece source_address_token_secret); - ~QuicCryptoServerConfig(); - - // TESTING is a magic parameter for passing to the constructor in tests. - static const char TESTING[]; - - // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable - // for using in tests. |extra_tags| contains additional key/value pairs that - // will be inserted into the config. - static QuicServerConfigProtobuf* DefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags); - - // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. - // It returns the SCFG message from the config if successful. The caller - // takes ownership of the CryptoHandshakeMessage. - CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf); - - // AddDefaultConfig creates a config and then calls AddConfig to - // add it. Any tags in |extra_tags| will be copied into the config. - CryptoHandshakeMessage* AddDefaultConfig( - QuicRandom* rand, - const QuicClock* clock, - const CryptoHandshakeMessage& extra_tags); - - // ProcessClientHello processes |client_hello| and decides whether to accept - // or reject the connection. If the connection is to be accepted, |out| is - // set to the contents of the ServerHello, |out_params| is completed and - // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and - // an error code is returned. - // - // client_hello: the incoming client hello message. - // guid: the GUID for the connection, which is used in key derivation. - // client_ip: the IP address of the client, which is used to generate and - // validate source-address tokens. - // now_since_epoch: the current time, as a delta since the unix epoch, - // which is used to validate client nonces. - // rand: an entropy source - // params: the state of the handshake. This may be updated with a server - // nonce when we send a rejection. After a successful handshake, this will - // contain the state of the connection. - // out: the resulting handshake message (either REJ or SHLO) - // error_details: used to store a string describing any error. - QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, - QuicGuid guid, - const IPEndPoint& client_ip, - QuicTime::Delta now_since_epoch, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* params, - CryptoHandshakeMessage* out, - std::string* error_details) const; - - private: - friend class test::QuicCryptoServerConfigPeer; - - // Config represents a server config: a collection of preferences and - // Diffie-Hellman public values. - struct Config : public QuicCryptoConfig { - Config(); - ~Config(); - - // serialized contains the bytes of this server config, suitable for sending - // on the wire. - std::string serialized; - // id contains the SCID of this server config. - std::string id; - // orbit contains the orbit value for this config: an opaque identifier - // used to identify clusters of server frontends. - unsigned char orbit[kOrbitSize]; - - // key_exchanges contains key exchange objects with the private keys - // already loaded. The values correspond, one-to-one, with the tags in - // |kexs| from the parent class. - std::vector<KeyExchange*> key_exchanges; - - // tag_value_map contains the raw key/value pairs for the config. - CryptoTagValueMap tag_value_map; - - private: - DISALLOW_COPY_AND_ASSIGN(Config); - }; - - // NewSourceAddressToken returns a fresh source address token for the given - // IP address. - std::string NewSourceAddressToken(const IPEndPoint& ip, - QuicRandom* rand, - QuicTime::Delta now_since_epoch) const; - - // ValidateSourceAddressToken returns true if the source address token in - // |token| is a valid and timely token for the IP address |ip| given that the - // current time is |now|. - bool ValidateSourceAddressToken(base::StringPiece token, - const IPEndPoint& ip, - QuicTime::Delta now_since_epoch) const; - - std::map<ServerConfigID, Config*> configs_; - - ServerConfigID active_config_; - - mutable base::Lock strike_register_lock_; - // strike_register_ contains a data structure that keeps track of previously - // observed client nonces in order to prevent replay attacks. - mutable scoped_ptr<StrikeRegister> strike_register_; - - // These members are used to encrypt and decrypt the source address tokens - // that we receive from and send to clients. - scoped_ptr<QuicEncrypter> source_address_token_encrypter_; - scoped_ptr<QuicDecrypter> source_address_token_decrypter_; -}; - } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index 154e364..7ff0b1f 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -5,6 +5,7 @@ #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_time.h" #include "net/quic/test_tools/mock_clock.h" diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index f24dcbe..dd684400 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -40,6 +40,9 @@ const CryptoTag kAESG = MAKE_TAG('A', 'E', 'S', 'G'); // AES128 + GCM const CryptoTag kQBIC = MAKE_TAG('Q', 'B', 'I', 'C'); // TCP cubic const CryptoTag kINAR = MAKE_TAG('I', 'N', 'A', 'R'); // Inter arrival +// Proof types (i.e. certificate types) +const CryptoTag kX509 = MAKE_TAG('X', '5', '0', '9'); // X.509 certificate + // Client hello tags const CryptoTag kVERS = MAKE_TAG('V', 'E', 'R', 'S'); // Version const CryptoTag kNONC = MAKE_TAG('N', 'O', 'N', 'C'); // The connection nonce @@ -58,6 +61,9 @@ const CryptoTag kPUBS = MAKE_TAG('P', 'U', 'B', 'S'); // Public key values const CryptoTag kSCID = MAKE_TAG('S', 'C', 'I', 'D'); // Server config id const CryptoTag kSRCT = MAKE_TAG('S', 'R', 'C', 'T'); // Source-address token const CryptoTag kORBT = MAKE_TAG('O', 'B', 'I', 'T'); // Server orbit. +const CryptoTag kPDMD = MAKE_TAG('P', 'D', 'M', 'D'); // Proof demand. +const CryptoTag kCERT = MAKE_TAG('C', 'E', 'R', 'T'); // Certificate chain +const CryptoTag kPROF = MAKE_TAG('P', 'R', 'O', 'F'); // Proof (signature). const size_t kMaxEntries = 16; // Max number of entries in a message. diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc new file mode 100644 index 0000000..10dcf73 --- /dev/null +++ b/net/quic/crypto/crypto_server_config.cc @@ -0,0 +1,586 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_server_config.h" + +#include "base/stl_util.h" +#include "crypto/hkdf.h" +#include "crypto/secure_hash.h" +#include "net/quic/crypto/aes_128_gcm_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_encrypter.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_server_config_protobuf.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/curve25519_key_exchange.h" +#include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/p256_key_exchange.h" +#include "net/quic/crypto/proof_source.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/crypto/source_address_token.h" +#include "net/quic/crypto/strike_register.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_protocol.h" + +using base::StringPiece; +using crypto::SecureHash; +using std::map; +using std::string; +using std::vector; + +namespace net { + +// static +const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; + +QuicCryptoServerConfig::QuicCryptoServerConfig( + StringPiece source_address_token_secret) + // AES-GCM is used to encrypt and authenticate source address tokens. The + // full, 96-bit nonce is used but we must ensure that an attacker cannot + // obtain two source address tokens with the same nonce. This occurs with + // probability 0.5 after 2**48 values. We assume that obtaining 2**48 + // source address tokens is not possible: at a rate of 10M packets per + // second, it would still take the attacker a year to obtain the needed + // number of packets. + // + // TODO(agl): switch to an encrypter with a larger nonce space (i.e. + // Salsa20+Poly1305). + : strike_register_lock_(), + source_address_token_encrypter_(new Aes128GcmEncrypter), + source_address_token_decrypter_(new Aes128GcmDecrypter) { + crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, + "QUIC source address token key", + source_address_token_encrypter_->GetKeySize(), + 0 /* no fixed IV needed */); + source_address_token_encrypter_->SetKey(hkdf.server_write_key()); + source_address_token_decrypter_->SetKey(hkdf.server_write_key()); +} + +QuicCryptoServerConfig::~QuicCryptoServerConfig() { + STLDeleteValues(&configs_); +} + +// static +QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const CryptoHandshakeMessage& extra_tags) { + CryptoHandshakeMessage msg; + + const string curve25519_private_key = + Curve25519KeyExchange::NewPrivateKey(rand); + scoped_ptr<Curve25519KeyExchange> curve25519( + Curve25519KeyExchange::New(curve25519_private_key)); + StringPiece curve25519_public_value = curve25519->public_value(); + + const string p256_private_key = + P256KeyExchange::NewPrivateKey(); + scoped_ptr<P256KeyExchange> p256( + P256KeyExchange::New(p256_private_key)); + StringPiece p256_public_value = p256->public_value(); + + string encoded_public_values; + // First two bytes encode the length of the public value. + encoded_public_values.push_back(curve25519_public_value.size()); + encoded_public_values.push_back(curve25519_public_value.size() >> 8); + encoded_public_values.append(curve25519_public_value.data(), + curve25519_public_value.size()); + encoded_public_values.push_back(p256_public_value.size()); + encoded_public_values.push_back(p256_public_value.size() >> 8); + encoded_public_values.append(p256_public_value.data(), + p256_public_value.size()); + + msg.set_tag(kSCFG); + msg.SetTaglist(kKEXS, kC255, kP256, 0); + msg.SetTaglist(kAEAD, kAESG, 0); + msg.SetValue(kVERS, static_cast<uint16>(0)); + msg.SetStringPiece(kPUBS, encoded_public_values); + msg.Insert(extra_tags.tag_value_map().begin(), + extra_tags.tag_value_map().end()); + + char scid_bytes[16]; + rand->RandBytes(scid_bytes, sizeof(scid_bytes)); + msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); + + char orbit_bytes[kOrbitSize]; + rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); + msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes))); + + scoped_ptr<QuicData> serialized( + CryptoFramer::ConstructHandshakeMessage(msg)); + + scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf); + config->set_config(serialized->AsStringPiece()); + QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key(); + curve25519_key->set_tag(kC255); + curve25519_key->set_private_key(curve25519_private_key); + QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key(); + p256_key->set_tag(kP256); + p256_key->set_private_key(p256_private_key); + + return config.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( + QuicServerConfigProtobuf* protobuf) { + scoped_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(protobuf->config())); + + if (!msg.get()) { + LOG(WARNING) << "Failed to parse server config message"; + return NULL; + } + if (msg->tag() != kSCFG) { + LOG(WARNING) << "Server config message has tag " + << msg->tag() << " expected " + << kSCFG; + return NULL; + } + + scoped_ptr<Config> config(new Config); + config->serialized = protobuf->config(); + + StringPiece scid; + if (!msg->GetStringPiece(kSCID, &scid)) { + LOG(WARNING) << "Server config message is missing SCID"; + return NULL; + } + config->id = scid.as_string(); + + const CryptoTag* aead_tags; + size_t aead_len; + if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing AEAD"; + return NULL; + } + config->aead = vector<CryptoTag>(aead_tags, aead_tags + aead_len); + + const CryptoTag* kexs_tags; + size_t kexs_len; + if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing KEXS"; + return NULL; + } + + StringPiece orbit; + if (!msg->GetStringPiece(kORBT, &orbit)) { + LOG(WARNING) << "Server config message is missing OBIT"; + return NULL; + } + + if (orbit.size() != kOrbitSize) { + LOG(WARNING) << "Orbit value in server config is the wrong length." + " Got " << orbit.size() << " want " << kOrbitSize; + return NULL; + } + COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size); + memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); + + if (kexs_len != protobuf->key_size()) { + LOG(WARNING) << "Server config has " + << kexs_len + << " key exchange methods configured, but " + << protobuf->key_size() + << " private keys"; + return NULL; + } + + for (size_t i = 0; i < kexs_len; i++) { + const CryptoTag tag = kexs_tags[i]; + string private_key; + + config->kexs.push_back(tag); + + for (size_t j = 0; j < protobuf->key_size(); j++) { + const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i); + if (key.tag() == tag) { + private_key = key.private_key(); + break; + } + } + + if (private_key.empty()) { + LOG(WARNING) << "Server config contains key exchange method without " + "corresponding private key: " + << tag; + return NULL; + } + + scoped_ptr<KeyExchange> ka; + switch (tag) { + case kC255: + ka.reset(Curve25519KeyExchange::New(private_key)); + if (!ka.get()) { + LOG(WARNING) << "Server config contained an invalid curve25519" + " private key."; + return NULL; + } + break; + case kP256: + ka.reset(P256KeyExchange::New(private_key)); + if (!ka.get()) { + LOG(WARNING) << "Server config contained an invalid P-256" + " private key."; + return NULL; + } + break; + default: + LOG(WARNING) << "Server config message contains unknown key exchange " + "method: " + << tag; + return NULL; + } + + for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin(); + i != config->key_exchanges.end(); ++i) { + if ((*i)->tag() == tag) { + LOG(WARNING) << "Duplicate key exchange in config: " << tag; + return NULL; + } + } + + config->key_exchanges.push_back(ka.release()); + } + + if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing version"; + return NULL; + } + + if (config->version != QuicCryptoConfig::CONFIG_VERSION) { + LOG(WARNING) << "Server config specifies an unsupported version"; + return NULL; + } + + scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); + sha256->Update(protobuf->config().data(), protobuf->config().size()); + char id_bytes[16]; + sha256->Finish(id_bytes, sizeof(id_bytes)); + const string id(id_bytes, sizeof(id_bytes)); + + configs_[id] = config.release(); + active_config_ = id; + + return msg.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const CryptoHandshakeMessage& extra_tags) { + scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig( + rand, clock, extra_tags)); + return AddConfig(config.get()); +} + +QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + QuicGuid guid, + const IPEndPoint& client_ip, + QuicTime::Delta now_since_unix_epoch, + QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details); + + CHECK(!configs_.empty()); + + // FIXME(agl): we should use the client's SCID, not just the active config. + map<ServerConfigID, Config*>::const_iterator it = + configs_.find(active_config_); + if (it == configs_.end()) { + *error_details = "No valid server config loaded"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + const Config* const config(it->second); + + bool valid_source_address_token = false; + StringPiece srct; + if (client_hello.GetStringPiece(kSRCT, &srct) && + ValidateSourceAddressToken(srct, client_ip, now_since_unix_epoch)) { + valid_source_address_token = true; + } + + const string fresh_source_address_token = + NewSourceAddressToken(client_ip, rand, now_since_unix_epoch); + + // If we previously sent a REJ to this client then we may have stored a + // server nonce in |params|. In which case, we know that the connection + // is unique because the server nonce will be mixed into the key generation. + bool unique_by_server_nonce = !params->server_nonce.empty(); + // If we can't ensure uniqueness by a server nonce, then we will try and use + // the strike register. + bool unique_by_strike_register = false; + + StringPiece client_nonce; + bool client_nonce_well_formed = false; + if (client_hello.GetStringPiece(kNONC, &client_nonce) && + client_nonce.size() == kNonceSize) { + client_nonce_well_formed = true; + base::AutoLock auto_lock(strike_register_lock_); + + if (strike_register_.get() == NULL) { + strike_register_.reset(new StrikeRegister( + // TODO(agl): these magic numbers should come from config. + 1024 /* max entries */, + static_cast<uint32>(now_since_unix_epoch.ToSeconds()), + 600 /* window secs */, config->orbit)); + } + unique_by_strike_register = strike_register_->Insert( + reinterpret_cast<const uint8*>(client_nonce.data()), + static_cast<uint32>(now_since_unix_epoch.ToSeconds())); + } + + out->Clear(); + + StringPiece sni; + client_hello.GetStringPiece(kSNI, &sni); + + StringPiece scid; + if (!client_hello.GetStringPiece(kSCID, &scid) || + scid.as_string() != config->id || + !valid_source_address_token || + !client_nonce_well_formed || + (!unique_by_strike_register && + !unique_by_server_nonce)) { + // If the client didn't provide a server config ID, or gave the wrong one, + // then the handshake cannot possibly complete. We reject the handshake and + // give the client enough information to do better next time. + out->set_tag(kREJ); + out->SetStringPiece(kSCFG, config->serialized); + out->SetStringPiece(kSRCT, fresh_source_address_token); + if (params->server_nonce.empty()) { + CryptoUtils::GenerateNonce( + now_since_unix_epoch, rand, + StringPiece(reinterpret_cast<const char*>(config->orbit), + sizeof(config->orbit)), + ¶ms->server_nonce); + } + out->SetStringPiece(kNONC, params->server_nonce); + + // The client may have requested a certificate chain. + const CryptoTag* their_proof_demands; + size_t num_their_proof_demands; + + if (valid_source_address_token && + proof_source_.get() != NULL && + !sni.empty() && + client_hello.GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) == QUIC_NO_ERROR) { + for (size_t i = 0; i < num_their_proof_demands; i++) { + if (their_proof_demands[i] != kX509) { + continue; + } + + // TODO(agl): in the future we will hopefully have a cached-info like + // mechanism where we can omit certificates that the client already has. + // In that case, the certificate chain may be small enough to include + // without a source-address token. But, for now, we always send the full + // chain and we always need a valid source-address token. + + const vector<string>* certs; + string signature; + if (!proof_source_->GetProof(sni.as_string(), config->serialized, + &certs, &signature)) { + break; + } + + // TODO(agl): compress and omit certificates where possible based on + // the client's cached certificates. + size_t cert_bytes = 0; + for (vector<string>::const_iterator i = certs->begin(); + i != certs->end(); ++i) { + cert_bytes += i->size(); + } + // There's a three byte length-prefix for each certificate. + cert_bytes += certs->size()*3; + scoped_ptr<char[]> buf(new char[cert_bytes]); + + size_t j = 0; + for (vector<string>::const_iterator i = certs->begin(); + i != certs->end(); ++i) { + size_t len = i->size(); + buf[j++] = len; + buf[j++] = len >> 8; + buf[j++] = len >> 16; + memcpy(&buf[j], i->data(), i->size()); + j += i->size(); + } + + DCHECK_EQ(j, cert_bytes); + + out->SetStringPiece(kCERT, StringPiece(buf.get(), cert_bytes)); + out->SetStringPiece(kPROF, signature); + break; + } + } + + return QUIC_NO_ERROR; + } + + const CryptoTag* their_aeads; + const CryptoTag* their_key_exchanges; + size_t num_their_aeads, num_their_key_exchanges; + if (client_hello.GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + client_hello.GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR || + num_their_aeads != 1 || + num_their_key_exchanges != 1) { + *error_details = "Missing or invalid AEAD or KEXS"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + size_t key_exchange_index; + if (!CryptoUtils::FindMutualTag(config->aead, + their_aeads, num_their_aeads, + CryptoUtils::LOCAL_PRIORITY, + ¶ms->aead, + NULL) || + !CryptoUtils::FindMutualTag(config->kexs, + their_key_exchanges, num_their_key_exchanges, + CryptoUtils::LOCAL_PRIORITY, + ¶ms->key_exchange, + &key_exchange_index)) { + *error_details = "Unsupported AEAD or KEXS"; + return QUIC_CRYPTO_NO_SUPPORT; + } + + StringPiece public_value; + if (!client_hello.GetStringPiece(kPUBS, &public_value)) { + *error_details = "Missing public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey( + public_value, ¶ms->premaster_secret)) { + *error_details = "Invalid public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + params->server_config_id = scid.as_string(); + + string hkdf_input(QuicCryptoConfig::kLabel, + strlen(QuicCryptoConfig::kLabel) + 1); + hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + + const QuicData& client_hello_serialized = client_hello.GetSerialized(); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(config->serialized); + + CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input, + CryptoUtils::SERVER); + + out->set_tag(kSHLO); + out->SetStringPiece(kSRCT, fresh_source_address_token); + return QUIC_NO_ERROR; +} + +void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) { + proof_source_.reset(proof_source); +} + +string QuicCryptoServerConfig::NewSourceAddressToken( + const IPEndPoint& ip, + QuicRandom* rand, + QuicTime::Delta now_since_epoch) const { + SourceAddressToken source_address_token; + source_address_token.set_ip(ip.ToString()); + source_address_token.set_timestamp(now_since_epoch.ToSeconds()); + + string plaintext = source_address_token.SerializeAsString(); + char nonce[12]; + DCHECK_EQ(sizeof(nonce), + source_address_token_encrypter_->GetNoncePrefixSize() + + sizeof(QuicPacketSequenceNumber)); + rand->RandBytes(nonce, sizeof(nonce)); + + size_t ciphertext_size = + source_address_token_encrypter_->GetCiphertextSize(plaintext.size()); + string result; + result.resize(sizeof(nonce) + ciphertext_size); + memcpy(&result[0], &nonce, sizeof(nonce)); + + if (!source_address_token_encrypter_->Encrypt( + StringPiece(nonce, sizeof(nonce)), StringPiece(), plaintext, + reinterpret_cast<unsigned char*>(&result[sizeof(nonce)]))) { + DCHECK(false); + return string(); + } + + return result; +} + +bool QuicCryptoServerConfig::ValidateSourceAddressToken( + StringPiece token, + const IPEndPoint& ip, + QuicTime::Delta now_since_epoch) const { + char nonce[12]; + DCHECK_EQ(sizeof(nonce), + source_address_token_encrypter_->GetNoncePrefixSize() + + sizeof(QuicPacketSequenceNumber)); + + if (token.size() <= sizeof(nonce)) { + return false; + } + memcpy(&nonce, token.data(), sizeof(nonce)); + token.remove_prefix(sizeof(nonce)); + + unsigned char plaintext_stack[128]; + scoped_ptr<unsigned char[]> plaintext_heap; + unsigned char* plaintext; + if (token.size() <= sizeof(plaintext_stack)) { + plaintext = plaintext_stack; + } else { + plaintext_heap.reset(new unsigned char[token.size()]); + plaintext = plaintext_heap.get(); + } + size_t plaintext_length; + + if (!source_address_token_decrypter_->Decrypt( + StringPiece(nonce, sizeof(nonce)), StringPiece(), token, + plaintext, &plaintext_length)) { + return false; + } + + SourceAddressToken source_address_token; + if (!source_address_token.ParseFromArray(plaintext, plaintext_length)) { + return false; + } + + if (source_address_token.ip() != ip.ToString()) { + // It's for a different IP address. + return false; + } + + const QuicTime::Delta delta(now_since_epoch.Subtract( + QuicTime::Delta::FromSeconds(source_address_token.timestamp()))); + const int64 delta_secs = delta.ToSeconds(); + + // TODO(agl): consider whether and how these magic values should be moved to + // a config. + if (delta_secs < -3600) { + // We only allow timestamps to be from an hour in the future. + return false; + } + + if (delta_secs > 86400) { + // We allow one day into the past. + return false; + } + + return true; +} + +QuicCryptoServerConfig::Config::Config() { +} + +QuicCryptoServerConfig::Config::~Config() { + STLDeleteElements(&key_exchanges); +} + +} // namespace net diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h new file mode 100644 index 0000000..4986603 --- /dev/null +++ b/net/quic/crypto/crypto_server_config.h @@ -0,0 +1,164 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ +#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +class KeyExchange; +class ProofSource; +class QuicClock; +class QuicDecrypter; +class QuicEncrypter; +class QuicRandom; +class QuicServerConfigProtobuf; +class StrikeRegister; + +namespace test { +class QuicCryptoServerConfigPeer; +} // namespace test + +// QuicCryptoServerConfig contains the crypto configuration of a QUIC server. +// Unlike a client, a QUIC server can have multiple configurations active in +// order to support clients resuming with a previous configuration. +// TODO(agl): when adding configurations at runtime is added, this object will +// need to consider locking. +class NET_EXPORT_PRIVATE QuicCryptoServerConfig { + public: + // |source_address_token_secret|: secret key material used for encrypting and + // decrypting source address tokens. It can be of any length as it is fed + // into a KDF before use. In tests, use TESTING. + explicit QuicCryptoServerConfig( + base::StringPiece source_address_token_secret); + ~QuicCryptoServerConfig(); + + // TESTING is a magic parameter for passing to the constructor in tests. + static const char TESTING[]; + + // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable + // for using in tests. |extra_tags| contains additional key/value pairs that + // will be inserted into the config. + static QuicServerConfigProtobuf* DefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const CryptoHandshakeMessage& extra_tags); + + // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. + // It returns the SCFG message from the config if successful. The caller + // takes ownership of the CryptoHandshakeMessage. + CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf); + + // AddDefaultConfig creates a config and then calls AddConfig to + // add it. Any tags in |extra_tags| will be copied into the config. + CryptoHandshakeMessage* AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const CryptoHandshakeMessage& extra_tags); + + // ProcessClientHello processes |client_hello| and decides whether to accept + // or reject the connection. If the connection is to be accepted, |out| is + // set to the contents of the ServerHello, |out_params| is completed and + // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and + // an error code is returned. + // + // client_hello: the incoming client hello message. + // guid: the GUID for the connection, which is used in key derivation. + // client_ip: the IP address of the client, which is used to generate and + // validate source-address tokens. + // now_since_epoch: the current time, as a delta since the unix epoch, + // which is used to validate client nonces. + // rand: an entropy source + // params: the state of the handshake. This may be updated with a server + // nonce when we send a rejection. After a successful handshake, this will + // contain the state of the connection. + // out: the resulting handshake message (either REJ or SHLO) + // error_details: used to store a string describing any error. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + QuicGuid guid, + const IPEndPoint& client_ip, + QuicTime::Delta now_since_epoch, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* params, + CryptoHandshakeMessage* out, + std::string* error_details) const; + + // SetProofSource installs |proof_source| as the ProofSource for handshakes. + // This object takes ownership of |proof_source|. + void SetProofSource(ProofSource* proof_source); + + private: + friend class test::QuicCryptoServerConfigPeer; + + // Config represents a server config: a collection of preferences and + // Diffie-Hellman public values. + struct Config : public QuicCryptoConfig { + Config(); + ~Config(); + + // serialized contains the bytes of this server config, suitable for sending + // on the wire. + std::string serialized; + // id contains the SCID of this server config. + std::string id; + // orbit contains the orbit value for this config: an opaque identifier + // used to identify clusters of server frontends. + unsigned char orbit[kOrbitSize]; + + // key_exchanges contains key exchange objects with the private keys + // already loaded. The values correspond, one-to-one, with the tags in + // |kexs| from the parent class. + std::vector<KeyExchange*> key_exchanges; + + // tag_value_map contains the raw key/value pairs for the config. + CryptoTagValueMap tag_value_map; + + private: + DISALLOW_COPY_AND_ASSIGN(Config); + }; + + // NewSourceAddressToken returns a fresh source address token for the given + // IP address. + std::string NewSourceAddressToken(const IPEndPoint& ip, + QuicRandom* rand, + QuicTime::Delta now_since_epoch) const; + + // ValidateSourceAddressToken returns true if the source address token in + // |token| is a valid and timely token for the IP address |ip| given that the + // current time is |now|. + bool ValidateSourceAddressToken(base::StringPiece token, + const IPEndPoint& ip, + QuicTime::Delta now_since_epoch) const; + + std::map<ServerConfigID, Config*> configs_; + + ServerConfigID active_config_; + + mutable base::Lock strike_register_lock_; + // strike_register_ contains a data structure that keeps track of previously + // observed client nonces in order to prevent replay attacks. + mutable scoped_ptr<StrikeRegister> strike_register_; + + // These members are used to encrypt and decrypt the source address tokens + // that we receive from and send to clients. + scoped_ptr<QuicEncrypter> source_address_token_encrypter_; + scoped_ptr<QuicDecrypter> source_address_token_decrypter_; + + // proof_source_ contains an object that can provide certificate chains and + // signatures. + scoped_ptr<ProofSource> proof_source_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ diff --git a/net/quic/crypto/crypto_server_config_protobuf.cc b/net/quic/crypto/crypto_server_config_protobuf.cc new file mode 100644 index 0000000..f2a1a2d --- /dev/null +++ b/net/quic/crypto/crypto_server_config_protobuf.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_server_config_protobuf.h" + +#include "base/stl_util.h" + +namespace net { + +QuicServerConfigProtobuf::QuicServerConfigProtobuf() { +} + +QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { + STLDeleteElements(&keys_); +} + +} // namespace net diff --git a/net/quic/crypto/crypto_server_config_protobuf.h b/net/quic/crypto/crypto_server_config_protobuf.h new file mode 100644 index 0000000..ee555ab --- /dev/null +++ b/net/quic/crypto/crypto_server_config_protobuf.h @@ -0,0 +1,71 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_ +#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_ + +#include <string> +#include <vector> + +#include "base/strings/string_piece.h" +#include "net/quic/crypto/crypto_protocol.h" + +namespace net { + +// TODO(rch): sync with server more rationally. +class QuicServerConfigProtobuf { + public: + class PrivateKey { + public: + CryptoTag tag() const { + return tag_; + } + void set_tag(CryptoTag tag) { + tag_ = tag; + } + std::string private_key() const { + return private_key_; + } + void set_private_key(std::string key) { + private_key_ = key; + } + + private: + CryptoTag tag_; + std::string private_key_; + }; + + QuicServerConfigProtobuf(); + ~QuicServerConfigProtobuf(); + + size_t key_size() const { + return keys_.size(); + } + + const PrivateKey& key(size_t i) const { + DCHECK_GT(keys_.size(), i); + return *keys_[i]; + } + + std::string config() const { + return config_; + } + + void set_config(base::StringPiece config) { + config_ = config.as_string(); + } + + QuicServerConfigProtobuf::PrivateKey* add_key() { + keys_.push_back(new PrivateKey); + return keys_.back(); + } + + private: + std::vector<PrivateKey*> keys_; + std::string config_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_ diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index 6e52898..e7a2376 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -72,7 +72,12 @@ void CryptoUtils::GenerateNonce(QuicTime::Delta now, nonce->reserve(kNonceSize); nonce->resize(kNonceSize); uint32 gmt_unix_time = now.ToSeconds(); - memcpy(&(*nonce)[0], &gmt_unix_time, sizeof(gmt_unix_time)); + // The time in the nonce must be encoded in big-endian because the + // strike-register depends on the nonces being ordered by time. + (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24); + (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16); + (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8); + (*nonce)[3] = static_cast<char>(gmt_unix_time); size_t bytes_written = sizeof(gmt_unix_time); if (orbit.size() == 8) { diff --git a/net/quic/crypto/proof_source.h b/net/quic/crypto/proof_source.h new file mode 100644 index 0000000..75b2ba0 --- /dev/null +++ b/net/quic/crypto/proof_source.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_PROOF_SOURCE_H_ +#define NET_QUIC_CRYPTO_PROOF_SOURCE_H_ + +#include <string> +#include <vector> + +#include "net/base/net_export.h" + +namespace net { + +// ProofSource is an interface by which a QUIC server can obtain certificate +// chains and signatures that prove its identity. +class NET_EXPORT_PRIVATE ProofSource { + public: + virtual ~ProofSource() {} + + // GetProof finds a certificate chain for |hostname|, sets |out_certs| to + // point to it (in leaf-first order), calculates a signature of + // |server_config| using that chain and puts the result in |out_signature|. + // + // The signature uses SHA-256 as the hash function and PSS padding when the + // key is RSA. + // + // |out_certs| is a pointer to a pointer, not a pointer to an array. + // + // The number of certificate chains is expected to be small and fixed thus + // the ProofSource retains ownership of the contents of |out_certs|. The + // expectation is that they will be cached forever. + // + // The signature values should be cached because |server_config| will be + // somewhat static. However, since they aren't bounded, the ProofSource may + // wish to evicit entries from that cache, thus the caller takes ownership of + // |*out_signature|. + // + // This function may be called concurrently. + virtual bool GetProof(const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>** out_certs, + std::string* out_signature) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_PROOF_SOURCE_H_ diff --git a/net/quic/crypto/source_address_token.cc b/net/quic/crypto/source_address_token.cc new file mode 100644 index 0000000..d664a08 --- /dev/null +++ b/net/quic/crypto/source_address_token.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/source_address_token.h" + +#include <vector> + +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" + +using std::string; +using std::vector; + +namespace net { + +SourceAddressToken::SourceAddressToken() { +} + +SourceAddressToken::~SourceAddressToken() { +} + +string SourceAddressToken::SerializeAsString() const { + return ip_ + " " + base::Int64ToString(timestamp_); +} + +bool SourceAddressToken::ParseFromArray(unsigned char* plaintext, + size_t plaintext_length) { + string data(reinterpret_cast<const char*>(plaintext), plaintext_length); + vector<string> results; + base::SplitString(data, ' ', &results); + if (results.size() < 2) { + return false; + } + + int64 timestamp; + if (!base::StringToInt64(results[1], ×tamp)) { + return false; + } + + ip_ = results[0]; + timestamp_ = timestamp; + return true; +} + +} // namespace net diff --git a/net/quic/crypto/source_address_token.h b/net/quic/crypto/source_address_token.h new file mode 100644 index 0000000..5f8388b --- /dev/null +++ b/net/quic/crypto/source_address_token.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_ +#define NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/strings/string_piece.h" + +namespace net { + +// TODO(rtenneti): sync with server more rationally. +class SourceAddressToken { + public: + SourceAddressToken(); + ~SourceAddressToken(); + + std::string SerializeAsString() const; + + bool ParseFromArray(unsigned char* plaintext, size_t plaintext_length); + + std::string ip() const { + return ip_; + } + + int64 timestamp() const { + return timestamp_; + } + + void set_ip(base::StringPiece ip) { + ip_ = ip.as_string(); + } + + void set_timestamp(int64 timestamp) { + timestamp_ = timestamp; + } + + private: + std::string ip_; + int64 timestamp_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_ diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index f16f06f..70e0b45 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -71,6 +71,8 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( CryptoUtils::Priority priority, QuicNegotiatedParameters* out_params, string* error_details) const { + DCHECK(error_details != NULL); + const CryptoTag* their_congestion_controls; size_t num_their_congestion_controls; QuicErrorCode error; @@ -78,9 +80,7 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( error = msg.GetTaglist(kCGST, &their_congestion_controls, &num_their_congestion_controls); if (error != QUIC_NO_ERROR) { - if (error_details) { - *error_details = "Missing CGST"; - } + *error_details = "Missing CGST"; return error; } @@ -90,18 +90,14 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( priority, &out_params->congestion_control, NULL)) { - if (error_details) { - *error_details = "Unsuported CGST"; - } + *error_details = "Unsuported CGST"; return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; } uint32 idle; error = msg.GetUint32(kICSL, &idle); if (error != QUIC_NO_ERROR) { - if (error_details) { - *error_details = "Missing ICSL"; - } + *error_details = "Missing ICSL"; return error; } @@ -122,9 +118,7 @@ QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( out_params->keepalive_timeout = QuicTime::Delta::Zero(); break; default: - if (error_details) { - *error_details = "Bad KATO"; - } + *error_details = "Bad KATO"; return error; } diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index efb3e96..cb3033b 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -180,6 +180,7 @@ bool QuicConnection::OnProtocolVersionMismatch( LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. " << "Closing connection."; CloseConnection(QUIC_INTERNAL_ERROR, false); + return false; } DCHECK_NE(quic_version_, received_version); @@ -229,6 +230,7 @@ void QuicConnection::OnVersionNegotiationPacket( LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server." << "Closing connection."; CloseConnection(QUIC_INTERNAL_ERROR, false); + return; } if (debug_visitor_) { debug_visitor_->OnVersionNegotiationPacket(packet); diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index aa43b6d..ffef124 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -49,6 +49,10 @@ QuicCryptoClientStream::crypto_negotiated_params() const { return crypto_negotiated_params_; } +int QuicCryptoClientStream::num_sent_client_hellos() const { + return num_client_hellos_; +} + // kMaxClientHellos is the maximum number of times that we'll send a client // hello. The value 3 accounts for: // * One failure due to an incorrect or missing source-address token. diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 2ad122c..3381c8a 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -40,6 +40,11 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { const QuicNegotiatedParameters& negotiated_params() const; const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; + // num_sent_client_hellos returns the number of client hello messages that + // have been sent. If the handshake has completed then this is one greater + // than the number of round-trips needed for the handshake. + int num_sent_client_hellos() const; + private: friend class test::CryptoTestUtils; diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index a0104e4..5fa33e1 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_crypto_server_stream.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index b567f85..c9b779c 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -12,9 +12,12 @@ #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_crypto_client_stream.h" +#include "net/quic/quic_crypto_server_stream.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" #include "net/quic/test_tools/crypto_test_utils.h" @@ -73,6 +76,12 @@ class QuicCryptoServerStreamTest : public ::testing::Test { session_(connection_, true), crypto_config_(QuicCryptoServerConfig::TESTING), stream_(config_, crypto_config_, &session_) { + // We advance the clock initially because the default time is zero and the + // strike register worries that we've just overflowed a uint32 time. + connection_->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); + // TODO(rtenneti): Enable testing of ProofSource. + // crypto_config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); + CryptoTestUtils::SetupCryptoServerConfigForTest( connection_->clock(), connection_->random_generator(), &config_, &crypto_config_); @@ -83,8 +92,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { message_data_.reset(framer.ConstructHandshakeMessage(message_)); } - void CompleteCryptoHandshake() { - CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_); + int CompleteCryptoHandshake() { + return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_); } protected: @@ -115,10 +124,81 @@ TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { return; } - CompleteCryptoHandshake(); + EXPECT_EQ(2, CompleteCryptoHandshake()); EXPECT_TRUE(stream_.handshake_complete()); } +TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { + if (!Aes128GcmEncrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + QuicGuid guid(1); + IPAddressNumber ip; + ParseIPLiteralToNumber("127.0.0.1", &ip); + IPEndPoint addr(ip, 0); + PacketSavingConnection* client_conn = + new PacketSavingConnection(guid, addr, false); + PacketSavingConnection* server_conn = + new PacketSavingConnection(guid, addr, false); + client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + + scoped_ptr<TestSession> client_session(new TestSession(client_conn, true)); + scoped_ptr<TestSession> server_session(new TestSession(server_conn, true)); + + QuicConfig client_config; + QuicCryptoClientConfig client_crypto_config; + + client_config.SetDefaults(); + client_crypto_config.SetDefaults(); + + scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream( + "test.example.com", client_config, client_session.get(), + &client_crypto_config)); + + // Do a first handshake in order to prime the client config with the server's + // information. + CHECK(client->CryptoConnect()); + CHECK_EQ(1u, client_conn->packets_.size()); + + scoped_ptr<QuicCryptoServerStream> server( + new QuicCryptoServerStream(config_, crypto_config_, + server_session.get())); + + CryptoTestUtils::CommunicateHandshakeMessages( + client_conn, client.get(), server_conn, server.get()); + EXPECT_EQ(2, client->num_sent_client_hellos()); + + // Now do another handshake, hopefully in 0-RTT. + LOG(INFO) << "Resetting for 0-RTT handshake attempt"; + + client_conn = new PacketSavingConnection(guid, addr, false); + server_conn = new PacketSavingConnection(guid, addr, false); + // We need to advance time past the strike-server window so that it's + // authoritative in this time span. + client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1002000)); + server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1002000)); + + // This causes the client's nonce to be different and thus stops the + // strike-register from rejecting the repeated nonce. + client_conn->random_generator()->Reseed(NULL, 0); + client_session.reset(new TestSession(client_conn, true)); + server_session.reset(new TestSession(server_conn, true)); + client.reset(new QuicCryptoClientStream( + "test.example.com", client_config, client_session.get(), + &client_crypto_config)); + server.reset(new QuicCryptoServerStream(config_, crypto_config_, + server_session.get())); + + CHECK(client->CryptoConnect()); + + CryptoTestUtils::CommunicateHandshakeMessages( + client_conn, client.get(), server_conn, server.get()); + EXPECT_EQ(1, client->num_sent_client_hellos()); +} + TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { if (!Aes128GcmEncrypter::IsSupported()) { LOG(INFO) << "AES GCM not supported. Test skipped."; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index b3f11cb..d373203 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -216,7 +216,7 @@ enum QuicErrorCode { // An internal error occured in crypto processing. QUIC_CRYPTO_INTERNAL_ERROR, // A crypto handshake message specified an unsupported version. - QUIC_VERSION_NOT_SUPPORTED, + QUIC_CRYPTO_VERSION_NOT_SUPPORTED, // There was no intersection between the crypto primitives supported by the // peer and ourselves. QUIC_CRYPTO_NO_SUPPORT, diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index ac46977..aa2019b 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -77,7 +77,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH) RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR); - RETURN_STRING_LITERAL(QUIC_VERSION_NOT_SUPPORTED); + RETURN_STRING_LITERAL(QUIC_CRYPTO_VERSION_NOT_SUPPORTED); RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT); RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER); diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index c2d5988..4c59d1f 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -6,6 +6,7 @@ #include "base/strings/string_piece.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" @@ -18,6 +19,7 @@ using base::StringPiece; using std::string; +using std::vector; namespace net { namespace test { @@ -36,39 +38,95 @@ class TestSession : public QuicSession { MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*()); }; -// CommunicateHandshakeMessages moves messages from |a| to |b| and back until -// |a|'s handshake has completed. -void CommunicateHandshakeMessages( - PacketSavingConnection* a_conn, - QuicCryptoStream* a, - PacketSavingConnection* b_conn, - QuicCryptoStream* b) { - scoped_ptr<SimpleQuicFramer> framer; +// CryptoFramerVisitor is a framer visitor that records handshake messages. +class CryptoFramerVisitor : public CryptoFramerVisitorInterface { + public: + CryptoFramerVisitor() + : error_(false) { + } + + void OnError(CryptoFramer* framer) { + error_ = true; + } + + void OnHandshakeMessage(const CryptoHandshakeMessage& message) { + messages_.push_back(message); + } + + bool error() const { + return error_; + } + + const vector<CryptoHandshakeMessage>& messages() const { + return messages_; + } - for (size_t i = 0; !a->handshake_complete(); i++) { - framer.reset(new SimpleQuicFramer); + private: + bool error_; + vector<CryptoHandshakeMessage> messages_; +}; + +// MovePackets parses crypto handshake messages from packet number +// |*inout_packet_index| through to the last packet and has |dest_stream| +// process them. |*inout_packet_index| is updated with an index one greater +// than the last packet processed. +void MovePackets(PacketSavingConnection* source_conn, + size_t *inout_packet_index, + QuicCryptoStream* dest_stream) { + SimpleQuicFramer framer; + CryptoFramer crypto_framer; + CryptoFramerVisitor crypto_visitor; - ASSERT_LT(i, a_conn->packets_.size()); - ASSERT_TRUE(framer->ProcessPacket(*a_conn->packets_[i])); - ASSERT_EQ(1u, framer->stream_frames().size()); + crypto_framer.set_visitor(&crypto_visitor); - scoped_ptr<CryptoHandshakeMessage> a_msg(framer->HandshakeMessage(0)); - b->OnHandshakeMessage(*(a_msg.get())); + size_t index = *inout_packet_index; + for (; index < source_conn->packets_.size(); index++) { + ASSERT_TRUE(framer.ProcessPacket(*source_conn->packets_[index])); + for (vector<QuicStreamFrame>::const_iterator + i = framer.stream_frames().begin(); + i != framer.stream_frames().end(); ++i) { + ASSERT_TRUE(crypto_framer.ProcessInput(i->data)); + ASSERT_FALSE(crypto_visitor.error()); + } + } + *inout_packet_index = index; - framer.reset(new SimpleQuicFramer); - ASSERT_LT(i, b_conn->packets_.size()); - ASSERT_TRUE(framer->ProcessPacket(*b_conn->packets_[i])); - ASSERT_EQ(1u, framer->stream_frames().size()); + ASSERT_EQ(0u, crypto_framer.InputBytesRemaining()); - scoped_ptr<CryptoHandshakeMessage> b_msg(framer->HandshakeMessage(0)); - a->OnHandshakeMessage(*(b_msg.get())); + for (vector<CryptoHandshakeMessage>::const_iterator + i = crypto_visitor.messages().begin(); + i != crypto_visitor.messages().end(); ++i) { + dest_stream->OnHandshakeMessage(*i); } } } // anonymous namespace // static -void CryptoTestUtils::HandshakeWithFakeServer( +void CryptoTestUtils::CommunicateHandshakeMessages( + PacketSavingConnection* a_conn, + QuicCryptoStream* a, + PacketSavingConnection* b_conn, + QuicCryptoStream* b) { + size_t a_i = 0, b_i = 0; + while (!a->handshake_complete()) { + ASSERT_GT(a_conn->packets_.size(), a_i); + LOG(INFO) << "Processing " << a_conn->packets_.size() - a_i + << " packets a->b"; + MovePackets(a_conn, &a_i, b); + + ASSERT_GT(b_conn->packets_.size(), b_i); + LOG(INFO) << "Processing " << b_conn->packets_.size() - b_i + << " packets b->a"; + if (b_conn->packets_.size() - b_i == 2) { + LOG(INFO) << "here"; + } + MovePackets(b_conn, &b_i, a); + } +} + +// static +int CryptoTestUtils::HandshakeWithFakeServer( PacketSavingConnection* client_conn, QuicCryptoClientStream* client) { QuicGuid guid(1); @@ -94,10 +152,12 @@ void CryptoTestUtils::HandshakeWithFakeServer( CommunicateHandshakeMessages(client_conn, client, server_conn, &server); CompareClientAndServerKeys(client, &server); + + return client->num_sent_client_hellos(); } // static -void CryptoTestUtils::HandshakeWithFakeClient( +int CryptoTestUtils::HandshakeWithFakeClient( PacketSavingConnection* server_conn, QuicCryptoServerStream* server) { QuicGuid guid(1); @@ -121,6 +181,8 @@ void CryptoTestUtils::HandshakeWithFakeClient( CommunicateHandshakeMessages(client_conn, &client, server_conn, server); CompareClientAndServerKeys(&client, server); + + return client.num_sent_client_hellos(); } // static diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index 73ff480..28202ad 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -14,11 +14,13 @@ namespace net { +class ProofSource; class QuicClock; class QuicConfig; class QuicCryptoClientStream; class QuicCryptoServerConfig; class QuicCryptoServerStream; +class QuicCryptoStream; class QuicRandom; namespace test { @@ -27,11 +29,13 @@ class PacketSavingConnection; class CryptoTestUtils { public: - static void HandshakeWithFakeServer(PacketSavingConnection* client_conn, - QuicCryptoClientStream* client); + // returns: the number of client hellos that the client sent. + static int HandshakeWithFakeServer(PacketSavingConnection* client_conn, + QuicCryptoClientStream* client); - static void HandshakeWithFakeClient(PacketSavingConnection* server_conn, - QuicCryptoServerStream* server); + // returns: the number of client hellos that the client sent. + static int HandshakeWithFakeClient(PacketSavingConnection* server_conn, + QuicCryptoServerStream* server); // SetupCryptoServerConfigForTest configures |config| and |crypto_config| // with sensible defaults for testing. @@ -41,10 +45,20 @@ class CryptoTestUtils { QuicConfig* config, QuicCryptoServerConfig* crypto_config); + // CommunicateHandshakeMessages moves messages from |a| to |b| and back until + // |a|'s handshake has completed. + static void CommunicateHandshakeMessages(PacketSavingConnection* a_conn, + QuicCryptoStream* a, + PacketSavingConnection* b_conn, + QuicCryptoStream* b); + // Returns the value for the tag |tag| in the tag value map of |message|. static std::string GetValueForTag(const CryptoHandshakeMessage& message, CryptoTag tag); + // Returns a |ProofSource| that serves up test certificates. + static ProofSource* ProofSourceForTesting(); + private: static void CompareClientAndServerKeys(QuicCryptoClientStream* client, QuicCryptoServerStream* server); diff --git a/net/quic/test_tools/mock_random.cc b/net/quic/test_tools/mock_random.cc index f7b8ea0..a8b8956 100644 --- a/net/quic/test_tools/mock_random.cc +++ b/net/quic/test_tools/mock_random.cc @@ -6,12 +6,16 @@ namespace net { +MockRandom::MockRandom() + : increment_(0) { +} + void MockRandom::RandBytes(void* data, size_t len) { - memset(data, 'r', len); + memset(data, 'r' + increment_, len); } uint64 MockRandom::RandUint64() { - return 0xDEADBEEF; + return 0xDEADBEEF + increment_; } bool MockRandom::RandBool() { @@ -19,6 +23,7 @@ bool MockRandom::RandBool() { } void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) { + increment_++; } } // namespace net diff --git a/net/quic/test_tools/mock_random.h b/net/quic/test_tools/mock_random.h index b583182..1278297 100644 --- a/net/quic/test_tools/mock_random.h +++ b/net/quic/test_tools/mock_random.h @@ -12,16 +12,23 @@ namespace net { class MockRandom : public QuicRandom { public: + MockRandom(); + // QuicRandom: - // Fills the |data| buffer with 'r'. + // Fills the |data| buffer with a repeating byte, initially 'r'. virtual void RandBytes(void* data, size_t len) OVERRIDE; - // Returns 0xDEADBEEF. + // Returns 0xDEADBEEF + the current increment. virtual uint64 RandUint64() OVERRIDE; // Returns false. virtual bool RandBool() OVERRIDE; - // Does nothing. + // Reseed advances |increment_| which causes the value returned by + // |RandUint64| to increment and the byte that |RandBytes| fills with, to + // advance. virtual void Reseed(const void* additional_entropy, size_t entropy_len) OVERRIDE; + + private: + uint8 increment_; }; } // namespace net diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index 8196373..aa8eb37 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -127,22 +127,34 @@ QuicRandom* MockHelper::GetRandomGenerator() { return &random_generator_; } +void MockHelper::AdvanceTime(QuicTime::Delta delta) { + clock_.AdvanceTime(delta); +} + MockConnection::MockConnection(QuicGuid guid, IPEndPoint address, bool is_server) - : QuicConnection(guid, address, new MockHelper(), is_server) { + : QuicConnection(guid, address, new MockHelper(), is_server), + has_mock_helper_(true) { } MockConnection::MockConnection(QuicGuid guid, IPEndPoint address, QuicConnectionHelperInterface* helper, bool is_server) - : QuicConnection(guid, address, helper, is_server) { + : QuicConnection(guid, address, helper, is_server), + has_mock_helper_(false) { } MockConnection::~MockConnection() { } +void MockConnection::AdvanceTime(QuicTime::Delta delta) { + CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being" + " used"; + static_cast<MockHelper*>(helper())->AdvanceTime(delta); +} + PacketSavingConnection::PacketSavingConnection(QuicGuid guid, IPEndPoint address, bool is_server) diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 2f1f382..73a3cd7 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -184,6 +184,7 @@ class MockHelper : public QuicConnectionHelperInterface { MOCK_METHOD1(SetConnection, void(QuicConnection* connection)); const QuicClock* GetClock() const; QuicRandom* GetRandomGenerator(); + void AdvanceTime(QuicTime::Delta delta); MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet, int* error)); MOCK_METHOD0(IsWriteBlockedDataBuffered, bool()); @@ -196,7 +197,7 @@ class MockHelper : public QuicConnectionHelperInterface { MOCK_METHOD0(UnregisterSendAlarmIfRegistered, void()); MOCK_METHOD0(ClearAckAlarm, void()); private: - const MockClock clock_; + MockClock clock_; MockRandom random_generator_; }; @@ -210,6 +211,10 @@ class MockConnection : public QuicConnection { bool is_server); virtual ~MockConnection(); + // If the constructor that uses a MockHelper has been used then this method + // will advance the time of the MockClock. + void AdvanceTime(QuicTime::Delta delta); + MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet)); @@ -234,6 +239,8 @@ class MockConnection : public QuicConnection { } private: + const bool has_mock_helper_; + DISALLOW_COPY_AND_ASSIGN(MockConnection); }; diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc index a007186..898cf35 100644 --- a/net/quic/test_tools/simple_quic_framer.cc +++ b/net/quic/test_tools/simple_quic_framer.cc @@ -173,13 +173,6 @@ const vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames() const { return visitor_->rst_stream_frames(); } -CryptoHandshakeMessage* SimpleQuicFramer::HandshakeMessage(size_t index) const { - if (index >= visitor_->stream_frames().size()) { - return NULL; - } - return CryptoFramer::ParseMessage(visitor_->stream_frames()[index].data); -} - const vector<QuicCongestionFeedbackFrame>& SimpleQuicFramer::feedback_frames() const { return visitor_->feedback_frames(); diff --git a/net/quic/test_tools/simple_quic_framer.h b/net/quic/test_tools/simple_quic_framer.h index 07a443f..bda2b89 100644 --- a/net/quic/test_tools/simple_quic_framer.h +++ b/net/quic/test_tools/simple_quic_framer.h @@ -43,10 +43,6 @@ class SimpleQuicFramer { const std::vector<QuicRstStreamFrame>& rst_stream_frames() const; const std::vector<QuicStreamFrame>& stream_frames() const; const QuicFecData& fec_data() const; - // HandshakeMessage returns the index'th stream frame as a freshly allocated - // handshake message which the caller takes ownership of. If parsing fails - // then NULL is returned. - CryptoHandshakeMessage* HandshakeMessage(size_t index) const; private: QuicFramer framer_; diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index e0824bb..ba77cbd 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc @@ -8,6 +8,7 @@ #include "base/strings/string_piece.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/tools/flip_server/epoll_server.h" diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index 28ea551..99a9d7f 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" +#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_framer.h" #include "net/tools/flip_server/epoll_server.h" diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc index 152d3aa..980d6bf 100644 --- a/net/tools/quic/test_tools/quic_test_utils.cc +++ b/net/tools/quic/test_tools/quic_test_utils.cc @@ -41,6 +41,12 @@ MockConnection::MockConnection(QuicGuid guid, MockConnection::~MockConnection() { } +void MockConnection::AdvanceTime(QuicTime::Delta delta) { + CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being" + " used"; + static_cast<MockHelper*>(helper())->AdvanceTime(delta); +} + } // namespace test } // namespace tools } // namespace net diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h index 6e0fe72..8af1f45 100644 --- a/net/tools/quic/test_tools/quic_test_utils.h +++ b/net/tools/quic/test_tools/quic_test_utils.h @@ -31,6 +31,10 @@ class MockConnection : public QuicConnection { QuicConnectionHelperInterface* helper, bool is_server); virtual ~MockConnection(); + // If the constructor that uses a MockHelper has been used then this method + // will advance the time of the MockClock. + void AdvanceTime(QuicTime::Delta delta); + MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet)); |