diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-17 17:57:01 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-17 17:57:01 +0000 |
commit | ef95114d4d73b9f561e09c4f7688b5f7c18330ba (patch) | |
tree | d8d77e0efffa6b3a0a2c392b42e94c7cf36253c9 /net/quic | |
parent | f467442c9e0db18557ee0e1a5060bd25827f9ed9 (diff) | |
download | chromium_src-ef95114d4d73b9f561e09c4f7688b5f7c18330ba.zip chromium_src-ef95114d4d73b9f561e09c4f7688b5f7c18330ba.tar.gz chromium_src-ef95114d4d73b9f561e09c4f7688b5f7c18330ba.tar.bz2 |
Land Recent QUIC Changes
QUIC crypto: move config objects.
Currently the client and server configs are setup and torn-down for each
connection. Since they are supposed to be per-client and per-server objects,
this change makes them parameters that are passed into the connection
Merge internal change: 44269387
QUIC crypto steps 6 and 7: per-server strike register.
This change adds a per-server strike-register that allows the server to
complete 0-RTT connections if the client has enough information cached.
Due to the fact that the per-server and per-client objects
(QuicCryptoServerConfig and QuicCryptoClientConfig) are currently setup and
torn down for each connection, there's no tests in this change for a 0-RTT
handshake because we can't do one yet. The next change will move these objects
into the right place so that 0-RTT handshakes can be tested.
This change also reminded me why I had a server nonce: without it the server
cannot terminate any connections if the strike-register fails. So the server
nonce is firmly back.
Merge internal change: 44228897
R=rch@chromium.org
Review URL: https://codereview.chromium.org/13976007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194634 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/quic')
33 files changed, 584 insertions, 311 deletions
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 18840b4..1d9b01b 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -23,6 +23,8 @@ #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" using base::StringPiece; @@ -419,10 +421,6 @@ const string& QuicCryptoClientConfig::CachedState::source_address_token() return source_address_token_; } -const string& QuicCryptoClientConfig::CachedState::orbit() const { - return orbit_; -} - void QuicCryptoClientConfig::CachedState::set_source_address_token( StringPiece token) { source_address_token_ = token.as_string(); @@ -443,7 +441,7 @@ void QuicCryptoClientConfig::SetDefaults() { } const QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::Lookup( - const string& server_hostname) { + const string& server_hostname) const { map<string, CachedState*>::const_iterator it = cached_states_.find(server_hostname); if (it == cached_states_.end()) { @@ -455,7 +453,7 @@ const QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::Lookup( void QuicCryptoClientConfig::FillInchoateClientHello( const string& server_hostname, const CachedState* cached, - CryptoHandshakeMessage* out) { + CryptoHandshakeMessage* out) const { out->set_tag(kCHLO); // Server name indication. @@ -480,7 +478,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( QuicRandom* rand, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out, - string* error_details) { + string* error_details) const { FillInchoateClientHello(server_hostname, cached, out); const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); @@ -552,8 +550,18 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + StringPiece orbit; + if (!scfg->GetStringPiece(kORBT, &orbit) || + orbit.size() != kOrbitSize) { + if (error_details) { + *error_details = "SCFG missing OBIT"; + } + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + string nonce; - CryptoUtils::GenerateNonce(clock, rand, cached->orbit(), &nonce); + CryptoUtils::GenerateNonce(clock->NowAsDeltaSinceUnixEpoch(), rand, orbit, + &nonce); out->SetStringPiece(kNONC, nonce); scoped_ptr<KeyExchange> key_exchange; @@ -599,6 +607,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( QuicErrorCode QuicCryptoClientConfig::ProcessRejection( const string& server_hostname, const CryptoHandshakeMessage& rej, + QuicCryptoNegotiatedParameters* out_params, string* error_details) { CachedState* cached; @@ -631,6 +640,12 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( cached->set_source_address_token(token); } + StringPiece nonce; + if (rej.GetStringPiece(kNONC, &nonce) && + nonce.size() == kNonceSize) { + out_params->server_nonce = nonce.as_string(); + } + return QUIC_NO_ERROR; } @@ -666,7 +681,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( // // TODO(agl): switch to an encrypter with a larger nonce space (i.e. // Salsa20+Poly1305). - : source_address_token_encrypter_(new Aes128GcmEncrypter), + : 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", @@ -681,7 +697,7 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() { } // static -QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( +QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( QuicRandom* rand, const QuicClock* clock, const CryptoHandshakeMessage& extra_tags) { @@ -722,6 +738,10 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::ConfigForTesting( 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)); @@ -778,6 +798,20 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( 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 @@ -866,11 +900,11 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( return msg.release(); } -CryptoHandshakeMessage* QuicCryptoServerConfig::AddTestingConfig( +CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( QuicRandom* rand, const QuicClock* clock, const CryptoHandshakeMessage& extra_tags) { - scoped_ptr<QuicServerConfigProtobuf> config(ConfigForTesting( + scoped_ptr<QuicServerConfigProtobuf> config(DefaultConfig( rand, clock, extra_tags)); return AddConfig(config.get()); } @@ -881,12 +915,18 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const IPEndPoint& client_ip, QuicTime::Delta now_since_unix_epoch, QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, CryptoHandshakeMessage* out, - QuicCryptoNegotiatedParameters *out_params, - string* error_details) { + string* error_details) const { CHECK(!configs_.empty()); // FIXME(agl): we should use the client's SCID, not just the active config. - const Config* config(configs_[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; @@ -898,10 +938,42 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( 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) { + !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. @@ -909,6 +981,14 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( 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; } @@ -931,12 +1011,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( if (!CryptoUtils::FindMutualTag(config->aead, their_aeads, num_their_aeads, CryptoUtils::LOCAL_PRIORITY, - &out_params->aead, + ¶ms->aead, NULL) || !CryptoUtils::FindMutualTag(config->kexs, their_key_exchanges, num_their_key_exchanges, CryptoUtils::LOCAL_PRIORITY, - &out_params->key_exchange, + ¶ms->key_exchange, &key_exchange_index)) { if (error_details) { *error_details = "Unsupported AEAD or KEXS"; @@ -953,22 +1033,14 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( } if (!config->key_exchanges[key_exchange_index]->CalculateSharedKey( - public_value, &out_params->premaster_secret)) { + public_value, ¶ms->premaster_secret)) { if (error_details) { *error_details = "Invalid public value"; } return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } - out_params->server_config_id = scid.as_string(); - - StringPiece client_nonce; - if (!client_hello.GetStringPiece(kNONC, &client_nonce)) { - if (error_details) { - *error_details = "Invalid nonce"; - } - 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)); @@ -978,7 +1050,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( client_hello_serialized.length()); hkdf_input.append(config->serialized); - CryptoUtils::DeriveKeys(out_params, client_nonce, hkdf_input, + CryptoUtils::DeriveKeys(params, client_nonce, hkdf_input, CryptoUtils::SERVER); out->set_tag(kSHLO); @@ -989,7 +1061,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( string QuicCryptoServerConfig::NewSourceAddressToken( const IPEndPoint& ip, QuicRandom* rand, - QuicTime::Delta now_since_epoch) { + 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()); @@ -1020,7 +1092,7 @@ string QuicCryptoServerConfig::NewSourceAddressToken( bool QuicCryptoServerConfig::ValidateSourceAddressToken( StringPiece token, const IPEndPoint& ip, - QuicTime::Delta now_since_epoch) { + QuicTime::Delta now_since_epoch) const { char nonce[12]; DCHECK_EQ(sizeof(nonce), source_address_token_encrypter_->GetNoncePrefixSize() + diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index fa6efd3..c597b74 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -11,6 +11,7 @@ #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" @@ -24,6 +25,7 @@ class QuicDecrypter; class QuicEncrypter; class QuicRandom; class QuicServerConfigProtobuf; +class StrikeRegister; namespace test { class QuicCryptoServerConfigPeer; @@ -221,6 +223,7 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { scoped_ptr<QuicEncrypter> encrypter; scoped_ptr<QuicDecrypter> decrypter; std::string server_config_id; + std::string server_nonce; }; // QuicCryptoConfig contains common configuration between clients and servers. @@ -242,7 +245,8 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { }; // QuicCryptoClientConfig contains crypto-related configuration settings for a -// client. +// client. Note that this object isn't thread-safe. It's designed to be used on +// a single thread at a time. class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { public: // A CachedState contains the information that the client needs in order to @@ -269,7 +273,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { const std::string& server_config() const; const std::string& source_address_token() const; - const std::string& orbit() const; void set_source_address_token(base::StringPiece token); @@ -277,7 +280,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { std::string server_config_id_; // An opaque id from the server. std::string server_config_; // A serialized handshake message. std::string source_address_token_; // An opaque proof of IP ownership. - std::string orbit_; // An opaque server-id used in nonce generation. // scfg contains the cached, parsed value of |server_config|. mutable scoped_ptr<CryptoHandshakeMessage> scfg_; @@ -291,14 +293,14 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // Lookup returns a CachedState for the given hostname, or NULL if no // information is known. - const CachedState* Lookup(const std::string& server_hostname); + const CachedState* Lookup(const std::string& server_hostname) const; // FillInchoateClientHello sets |out| to be a CHLO message that elicits a // source-address token or SCFG from a server. If |cached| is non-NULL, the // source-address token will be taken from it. void FillInchoateClientHello(const std::string& server_hostname, const CachedState* cached, - CryptoHandshakeMessage* out); + CryptoHandshakeMessage* out) const; // FillClientHello sets |out| to be a CHLO message based on the configuration // of this object. This object must have cached enough information about @@ -315,13 +317,16 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { QuicRandom* rand, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out, - std::string* error_details); + std::string* error_details) const; // ProcessRejection processes a REJ message from a server and updates the // cached information about that server. After this, |is_complete| may return - // true for that server's CachedState. + // true for that server's CachedState. If the rejection message contains + // state about a future handshake (i.e. an nonce value from the server), then + // it will be saved in |out_params|. QuicErrorCode ProcessRejection(const std::string& server_hostname, const CryptoHandshakeMessage& rej, + QuicCryptoNegotiatedParameters* out_params, std::string* error_details); // ProcessServerHello processes the message in |server_hello|, writes the @@ -348,7 +353,7 @@ 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. + // into a KDF before use. In tests, use TESTING. explicit QuicCryptoServerConfig( base::StringPiece source_address_token_secret); ~QuicCryptoServerConfig(); @@ -356,10 +361,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // TESTING is a magic parameter for passing to the constructor in tests. static const char TESTING[]; - // ConfigForTesting generates a QuicServerConfigProtobuf protobuf suitable + // 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* ConfigForTesting( + static QuicServerConfigProtobuf* DefaultConfig( QuicRandom* rand, const QuicClock* clock, const CryptoHandshakeMessage& extra_tags); @@ -369,9 +374,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // takes ownership of the CryptoHandshakeMessage. CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf); - // AddTestingConfig creates a test config and then calls AddConfig to add it. - // Any tags in |extra_tags| will be copied into the config. - CryptoHandshakeMessage* AddTestingConfig( + // 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); @@ -389,17 +394,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // 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) - // out_params: the state of the handshake // 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, - QuicCryptoNegotiatedParameters* out_params, - std::string* error_details); + std::string* error_details) const; private: friend class test::QuicCryptoServerConfigPeer; @@ -415,6 +422,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { 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 @@ -432,19 +442,24 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // IP address. std::string NewSourceAddressToken(const IPEndPoint& ip, QuicRandom* rand, - QuicTime::Delta now_since_epoch); + 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); + 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_; diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index 5909cd9..154e364 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -45,7 +45,7 @@ TEST(QuicCryptoServerConfigTest, ServerConfig) { CryptoHandshakeMessage extra_tags; scoped_ptr<CryptoHandshakeMessage>( - server.AddTestingConfig(QuicRandom::GetInstance(), &clock, extra_tags)); + server.AddDefaultConfig(QuicRandom::GetInstance(), &clock, extra_tags)); } TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 0b84a98..f24dcbe 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -57,11 +57,14 @@ const CryptoTag kSNI = MAKE_TAG('S', 'N', 'I', '\0'); // Server name 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 size_t kMaxEntries = 16; // Max number of entries in a message. const size_t kNonceSize = 32; // Size in bytes of the connection nonce. +const size_t kOrbitSize = 8; // Number of bytes in an orbit value. + } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc index b6ae58b..6e52898 100644 --- a/net/quic/crypto/crypto_utils.cc +++ b/net/quic/crypto/crypto_utils.cc @@ -10,7 +10,7 @@ #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_clock.h" +#include "net/quic/quic_time.h" using base::StringPiece; using std::string; @@ -64,14 +64,13 @@ bool CryptoUtils::FindMutualTag(const CryptoTagVector& our_tags_vector, return false; } -void CryptoUtils::GenerateNonce(const QuicClock* clock, +void CryptoUtils::GenerateNonce(QuicTime::Delta now, QuicRandom* random_generator, - const string& orbit, + StringPiece orbit, string* nonce) { // a 4-byte timestamp + 28 random bytes. nonce->reserve(kNonceSize); nonce->resize(kNonceSize); - QuicTime::Delta now = clock->NowAsDeltaSinceUnixEpoch(); uint32 gmt_unix_time = now.ToSeconds(); memcpy(&(*nonce)[0], &gmt_unix_time, sizeof(gmt_unix_time)); @@ -85,7 +84,7 @@ void CryptoUtils::GenerateNonce(const QuicClock* clock, } void CryptoUtils::DeriveKeys(QuicCryptoNegotiatedParameters* params, - StringPiece nonce, + StringPiece client_nonce, const string& hkdf_input, Perspective perspective) { params->encrypter.reset(QuicEncrypter::Create(params->aead)); @@ -93,6 +92,13 @@ void CryptoUtils::DeriveKeys(QuicCryptoNegotiatedParameters* params, size_t key_bytes = params->encrypter->GetKeySize(); size_t nonce_prefix_bytes = params->encrypter->GetNoncePrefixSize(); + StringPiece nonce = client_nonce; + string nonce_storage; + if (!params->server_nonce.empty()) { + nonce_storage = client_nonce.as_string() + params->server_nonce; + nonce = nonce_storage; + } + crypto::HKDF hkdf(params->premaster_secret, nonce, hkdf_input, key_bytes, nonce_prefix_bytes); if (perspective == SERVER) { diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h index 442830b..4ad4149 100644 --- a/net/quic/crypto/crypto_utils.h +++ b/net/quic/crypto/crypto_utils.h @@ -16,7 +16,7 @@ namespace net { -class QuicClock; +class QuicTime; class QuicRandom; struct QuicCryptoNegotiatedParameters; @@ -51,17 +51,19 @@ class NET_EXPORT_PRIVATE CryptoUtils { // <4 bytes> current time // <8 bytes> |orbit| (or random if |orbit| is empty) // <20 bytes> random - static void GenerateNonce(const QuicClock* clock, + static void GenerateNonce(QuicTime::Delta now, QuicRandom* random_generator, - const std::string& orbit, + base::StringPiece orbit, std::string* nonce); - // DeriveKeys populates the |encrypter| and |decrypter| members of |params| - // given the contents of |premaster_secret|, |nonce| and |hkdf_input|. - // |perspective| controls whether the server's keys are assigned to - // |encrypter| or |decrypter|. + // DeriveKeys populates |params->encrypter| and |params->decrypter| given the + // contents of |params->premaster_secret|, |client_nonce|, + // |params->server_nonce| and |hkdf_input|. |perspective| controls whether + // the server's keys are assigned to |encrypter| or |decrypter|. + // |params->server_nonce| is optional and, if non-empty, is mixed into the + // key derivation. static void DeriveKeys(QuicCryptoNegotiatedParameters* params, - base::StringPiece nonce, + base::StringPiece client_nonce, const std::string& hkdf_input, Perspective perspective); }; diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index 7bd4078..5e1e80f 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc @@ -23,14 +23,10 @@ QuicClientSession::QuicClientSession( QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, const string& server_hostname, + QuicCryptoClientConfig* crypto_config, NetLog* net_log) : QuicSession(connection, false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), - ALLOW_THIS_IN_INITIALIZER_LIST(crypto_stream_( - crypto_client_stream_factory ? - crypto_client_stream_factory->CreateQuicCryptoClientStream( - this, server_hostname) : - new QuicCryptoClientStream(this, server_hostname))), stream_factory_(stream_factory), socket_(socket), read_buffer_(new IOBufferWithSize(kMaxPacketSize)), @@ -38,6 +34,14 @@ QuicClientSession::QuicClientSession( num_total_streams_(0), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), logger_(net_log_) { + config_.SetDefaults(); + crypto_stream_.reset( + crypto_client_stream_factory ? + crypto_client_stream_factory->CreateQuicCryptoClientStream( + server_hostname, config_, this, crypto_config) : + new QuicCryptoClientStream( + server_hostname, config_, this, crypto_config)); + connection->set_debug_visitor(&logger_); // TODO(rch): pass in full host port proxy pair net_log_.BeginEvent( diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h index cdda311..2bf6e78 100644 --- a/net/quic/quic_client_session.h +++ b/net/quic/quic_client_session.h @@ -36,6 +36,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, const std::string& server_hostname, + QuicCryptoClientConfig* crypto_config, NetLog* net_log); virtual ~QuicClientSession(); @@ -70,6 +71,9 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { void OnReadComplete(int result); base::WeakPtrFactory<QuicClientSession> weak_factory_; + // config_ contains non-crypto configuration options negotiated in the crypto + // handshake. + QuicConfig config_; scoped_ptr<QuicCryptoClientStream> crypto_stream_; QuicStreamFactory* stream_factory_; scoped_ptr<DatagramClientSocket> socket_; diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 1ca0080..6d26541 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -30,7 +30,9 @@ class QuicClientSessionTest : public ::testing::Test { QuicClientSessionTest() : guid_(1), connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), - session_(connection_, NULL, NULL, NULL, kServerHostname, &net_log_) { + session_(connection_, NULL, NULL, NULL, kServerHostname, + &crypto_config_, &net_log_) { + crypto_config_.SetDefaults(); } void CompleteCryptoHandshake() { @@ -49,6 +51,8 @@ class QuicClientSessionTest : public ::testing::Test { MockRandom random_; QuicConnectionVisitorInterface* visitor_; TestCompletionCallback callback_; + QuicConfig* config_; + QuicCryptoClientConfig crypto_config_; }; TEST_F(QuicClientSessionTest, CryptoConnect) { diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc new file mode 100644 index 0000000..f16f06f --- /dev/null +++ b/net/quic/quic_config.cc @@ -0,0 +1,135 @@ +// 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/quic_config.h" + +using std::string; + +namespace net { + +QuicNegotiatedParameters::QuicNegotiatedParameters() + : idle_connection_state_lifetime(QuicTime::Delta::Zero()), + keepalive_timeout(QuicTime::Delta::Zero()) { +} + +QuicConfig::QuicConfig() + : idle_connection_state_lifetime_(QuicTime::Delta::Zero()), + keepalive_timeout_(QuicTime::Delta::Zero()) { +} + +QuicConfig::~QuicConfig() { +} + +void QuicConfig::SetDefaults() { + idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(300); + keepalive_timeout_ = QuicTime::Delta::Zero(); + congestion_control_.clear(); + congestion_control_.push_back(kQBIC); +} + +bool QuicConfig::SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg) { + const CryptoTag* cgst; + size_t num_cgst; + QuicErrorCode error; + + error = scfg.GetTaglist(kCGST, &cgst, &num_cgst); + if (error != QUIC_NO_ERROR) { + return false; + } + + congestion_control_.assign(cgst, cgst + num_cgst); + + uint32 idle; + error = scfg.GetUint32(kICSL, &idle); + if (error != QUIC_NO_ERROR) { + return false; + } + idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(idle); + + keepalive_timeout_ = QuicTime::Delta::Zero(); + + uint32 keepalive; + error = scfg.GetUint32(kKATO, &keepalive); + // KATO is optional. + if (error == QUIC_NO_ERROR) { + keepalive_timeout_ = QuicTime::Delta::FromSeconds(keepalive); + } + + return true; +} + +void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + out->SetValue( + kICSL, static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds())); + out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout_.ToSeconds())); + out->SetVector(kCGST, congestion_control_); +} + +QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( + const CryptoHandshakeMessage& msg, + CryptoUtils::Priority priority, + QuicNegotiatedParameters* out_params, + string* error_details) const { + const CryptoTag* their_congestion_controls; + size_t num_their_congestion_controls; + QuicErrorCode error; + + error = msg.GetTaglist(kCGST, &their_congestion_controls, + &num_their_congestion_controls); + if (error != QUIC_NO_ERROR) { + if (error_details) { + *error_details = "Missing CGST"; + } + return error; + } + + if (!CryptoUtils::FindMutualTag(congestion_control_, + their_congestion_controls, + num_their_congestion_controls, + priority, + &out_params->congestion_control, + NULL)) { + if (error_details) { + *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"; + } + return error; + } + + out_params->idle_connection_state_lifetime = QuicTime::Delta::FromSeconds( + std::min(static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()), + idle)); + + uint32 keepalive; + error = msg.GetUint32(kKATO, &keepalive); + switch (error) { + case QUIC_NO_ERROR: + out_params->keepalive_timeout = QuicTime::Delta::FromSeconds( + std::min(static_cast<uint32>(keepalive_timeout_.ToSeconds()), + keepalive)); + break; + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + // KATO is optional. + out_params->keepalive_timeout = QuicTime::Delta::Zero(); + break; + default: + if (error_details) { + *error_details = "Bad KATO"; + } + return error; + } + + return QUIC_NO_ERROR; +} + +} // namespace net + diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h new file mode 100644 index 0000000..17441d7 --- /dev/null +++ b/net/quic/quic_config.h @@ -0,0 +1,66 @@ +// 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_QUIC_CONFIG_H_ +#define NET_QUIC_QUIC_CONFIG_H_ + +#include <string> + +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/quic_time.h" + +namespace net { + +// QuicNegotiatedParameters contains non-crypto parameters that are agreed upon +// during the crypto handshake. +class NET_EXPORT_PRIVATE QuicNegotiatedParameters { + public: + QuicNegotiatedParameters(); + + CryptoTag congestion_control; + QuicTime::Delta idle_connection_state_lifetime; + QuicTime::Delta keepalive_timeout; +}; + +// QuicConfig contains non-crypto configuration options that are negotiated in +// the crypto handshake. +class NET_EXPORT_PRIVATE QuicConfig { + public: + QuicConfig(); + ~QuicConfig(); + + // SetDefaults sets the members to sensible, default values. + void SetDefaults(); + + // SetFromMessage extracts the non-crypto configuration from |msg| and sets + // the members of this object to match. This is expected to be called in the + // case of a server which is loading a server config. The server config + // contains the non-crypto parameters and so the server will need to keep its + // QuicConfig in sync with the server config that it'll be sending to + // clients. + bool SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg); + + // ToHandshakeMessage serializes the settings in this object as a series of + // tags /value pairs and adds them to |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + QuicErrorCode ProcessFinalPeerHandshake( + const CryptoHandshakeMessage& peer_handshake, + CryptoUtils::Priority priority, + QuicNegotiatedParameters* out_params, + std::string* error_details) const; + + private: + // Congestion control feedback type. + CryptoTagVector congestion_control_; + // Idle connection state lifetime + QuicTime::Delta idle_connection_state_lifetime_; + // Keepalive timeout, or 0 to turn off keepalive probes + QuicTime::Delta keepalive_timeout_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONFIG_H_ diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 6449ee1..efb3e96 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -1162,7 +1162,8 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) { void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, const string& details) { DLOG(INFO) << ENDPOINT << "Force closing with error " - << QuicUtils::ErrorToString(error) << " (" << error << ")"; + << QuicUtils::ErrorToString(error) << " (" << error << ") " + << details; QuicConnectionCloseFrame frame; frame.error_code = error; frame.error_details = details; diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index 63a4f44..aa43b6d 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -11,14 +11,18 @@ namespace net { -QuicCryptoClientStream::QuicCryptoClientStream(QuicSession* session, - const string& server_hostname) +QuicCryptoClientStream::QuicCryptoClientStream( + const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) : QuicCryptoStream(session), next_state_(STATE_IDLE), + num_client_hellos_(0), + config_(config), + crypto_config_(crypto_config), decrypter_pushed_(false), server_hostname_(server_hostname) { - config_.SetDefaults(); - crypto_config_.SetDefaults(); } QuicCryptoClientStream::~QuicCryptoClientStream() { @@ -45,6 +49,13 @@ QuicCryptoClientStream::crypto_negotiated_params() const { return crypto_negotiated_params_; } +// 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. +// * One failure due the server's certificate chain being unavailible and the +// server being unwilling to send it without a valid source-address token. +static const int kMaxClientHellos = 3; + void QuicCryptoClientStream::DoHandshakeLoop( const CryptoHandshakeMessage* in) { CryptoHandshakeMessage out; @@ -60,11 +71,17 @@ void QuicCryptoClientStream::DoHandshakeLoop( next_state_ = STATE_IDLE; switch (state) { case STATE_SEND_CHLO: { + if (num_client_hellos_ > kMaxClientHellos) { + CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS); + return; + } + num_client_hellos_++; + const QuicCryptoClientConfig::CachedState* cached = - crypto_config_.Lookup(server_hostname_); + crypto_config_->Lookup(server_hostname_); if (!cached || !cached->is_complete()) { - crypto_config_.FillInchoateClientHello(server_hostname_, cached, - &out); + crypto_config_->FillInchoateClientHello(server_hostname_, cached, + &out); next_state_ = STATE_RECV_REJ; DLOG(INFO) << "Client Sending: " << out.DebugString(); SendHandshakeMessage(out); @@ -72,7 +89,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( } const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); config_.ToHandshakeMessage(&out); - error = crypto_config_.FillClientHello( + error = crypto_config_->FillClientHello( server_hostname_, session()->connection()->guid(), cached, @@ -102,16 +119,18 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } case STATE_RECV_REJ: - // We sent a dummy CHLO because we don't have enough information to - // perform a handshake. Here we hope to have a REJ that contains the - // information that we need. + // We sent a dummy CHLO because we didn't have enough information to + // perform a handshake, or we sent a full hello that the server + // rejected. Here we hope to have a REJ that contains the information + // that we need. if (in->tag() != kREJ) { CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ"); return; } - error = crypto_config_.ProcessRejection(server_hostname_, *in, - &error_details); + error = crypto_config_->ProcessRejection(server_hostname_, *in, + &crypto_negotiated_params_, + &error_details); if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails(error, error_details); return; @@ -126,10 +145,13 @@ void QuicCryptoClientStream::DoHandshakeLoop( case STATE_RECV_SHLO: // We sent a CHLO that we expected to be accepted and now we're hoping // for a SHLO from the server to confirm that. + if (in->tag() == kREJ) { + next_state_ = STATE_RECV_REJ; + break; + } if (in->tag() != kSHLO) { - // TODO(agl): in the future we would attempt the handshake again. CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, - "Expected SHLO"); + "Expected SHLO or REJ"); return; } // Receiving SHLO implies the server must have processed our full diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index fb67ee4..2ad122c 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -8,10 +8,12 @@ #include <string> #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" namespace net { +class QuicConfig; class QuicSession; namespace test { @@ -20,7 +22,10 @@ class CryptoTestUtils; class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { public: - QuicCryptoClientStream(QuicSession* session, const string& server_hostname); + QuicCryptoClientStream(const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config); virtual ~QuicCryptoClientStream(); // CryptoFramerVisitorInterface implementation @@ -50,13 +55,20 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { void DoHandshakeLoop(const CryptoHandshakeMessage* in); State next_state_; + // num_client_hellos_ contains the number of client hello messages that this + // connection has sent. + int num_client_hellos_; - QuicConfig config_; - QuicCryptoClientConfig crypto_config_; + const QuicConfig& config_; + QuicCryptoClientConfig* const crypto_config_; QuicNegotiatedParameters negotiated_params_; QuicCryptoNegotiatedParameters crypto_negotiated_params_; + // decrypter_pushed_ is true if we have installed a QuicDecrypter in the + // connection. We need to track this because, in the event of a handshake + // failure, we have to remove any previous decrypters as they will have the + // wrong keys. bool decrypter_pushed_; // Client's connection nonce (4-byte timestamp + 28 random bytes) diff --git a/net/quic/quic_crypto_client_stream_factory.h b/net/quic/quic_crypto_client_stream_factory.h index abfcbb4..4d0eb1e 100644 --- a/net/quic/quic_crypto_client_stream_factory.h +++ b/net/quic/quic_crypto_client_stream_factory.h @@ -21,7 +21,10 @@ class NET_EXPORT QuicCryptoClientStreamFactory { virtual ~QuicCryptoClientStreamFactory() {} virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( - QuicSession* session, const std::string& server_hostname) = 0; + const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) = 0; }; } // namespace net diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 251a8ef..d8d2392 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -66,7 +66,9 @@ class QuicCryptoClientStreamTest : public ::testing::Test { : addr_(), connection_(new PacketSavingConnection(1, addr_, true)), session_(connection_, true), - stream_(&session_, kServerHostname) { + stream_(kServerHostname, config_, &session_, &crypto_config_) { + config_.SetDefaults(); + crypto_config_.SetDefaults(); } void CompleteCryptoHandshake() { @@ -85,6 +87,8 @@ class QuicCryptoClientStreamTest : public ::testing::Test { QuicCryptoClientStream stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; + QuicConfig config_; + QuicCryptoClientConfig crypto_config_; }; TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index d1c21ff..a0104e4 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -6,37 +6,19 @@ #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" namespace net { -QuicCryptoServerStream::QuicCryptoServerStream(QuicSession* session) +QuicCryptoServerStream::QuicCryptoServerStream( + const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicSession* session) : QuicCryptoStream(session), - // TODO(agl): use real secret. - crypto_config_("secret") { - config_.SetDefaults(); - // Use hardcoded crypto parameters for now. - CryptoHandshakeMessage extra_tags; - config_.ToHandshakeMessage(&extra_tags); - - // TODO(agl): AddTestingConfig generates a new, random config. In the future - // this will be replaced with a real source of configs. - scoped_ptr<CryptoHandshakeMessage> scfg( - crypto_config_.AddTestingConfig(session->connection()->random_generator(), - session->connection()->clock(), - extra_tags)); - // If we were using the same config in many servers then we would have to - // parse a QuicConfig from config_tags here. - - // Our non-crypto configuration is also expressed in the SCFG because it's - // signed. Thus |config_| needs to be consistent with that. - if (!config_.SetFromHandshakeMessage(*scfg)) { - // TODO(agl): when we aren't generating testing configs then this can be a - // CHECK at startup time. - LOG(WARNING) << "SCFG could not be parsed by QuicConfig."; - DCHECK(false); - } + config_(config), + crypto_config_(crypto_config) { } QuicCryptoServerStream::~QuicCryptoServerStream() { @@ -62,7 +44,7 @@ void QuicCryptoServerStream::OnHandshakeMessage( session()->connection()->peer_address(), session()->connection()->clock()->NowAsDeltaSinceUnixEpoch(), session()->connection()->random_generator(), - &reply, &crypto_negotiated_params_, &error_details); + &crypto_negotiated_params_, &reply, &error_details); if (reply.tag() == kSHLO) { // If we are returning a SHLO then we accepted the handshake. diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index 7841172..3e4264a 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -8,10 +8,14 @@ #include <string> #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" namespace net { +class CryptoHandshakeMessage; +class QuicCryptoServerConfig; +class QuicNegotiatedParameters; class QuicSession; namespace test { @@ -20,6 +24,9 @@ class CryptoTestUtils; class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { public: + QuicCryptoServerStream(const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicSession* session); explicit QuicCryptoServerStream(QuicSession* session); virtual ~QuicCryptoServerStream(); @@ -35,10 +42,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // config_ contains non-crypto parameters that are negotiated in the crypto // handshake. - QuicConfig config_; + const QuicConfig& config_; // crypto_config_ contains crypto parameters for the handshake. - QuicCryptoServerConfig crypto_config_; - std::string server_nonce_; + const QuicCryptoServerConfig& crypto_config_; QuicNegotiatedParameters negotiated_params_; QuicCryptoNegotiatedParameters crypto_negotiated_params_; diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 173122d8..b567f85 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -71,7 +71,11 @@ class QuicCryptoServerStreamTest : public ::testing::Test { ip_ : IPAddressNumber(), 1), connection_(new PacketSavingConnection(guid_, addr_, true)), session_(connection_, true), - stream_(&session_) { + crypto_config_(QuicCryptoServerConfig::TESTING), + stream_(config_, crypto_config_, &session_) { + CryptoTestUtils::SetupCryptoServerConfigForTest( + connection_->clock(), connection_->random_generator(), &config_, + &crypto_config_); } void ConstructHandshakeMessage() { @@ -89,6 +93,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { IPEndPoint addr_; PacketSavingConnection* connection_; TestSession session_; + QuicConfig config_; + QuicCryptoServerConfig crypto_config_; QuicCryptoServerStream stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index f033af8..9167e6f 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -61,128 +61,4 @@ void QuicCryptoStream::SendHandshakeMessage( WriteData(string(data.data(), data.length()), false); } -QuicNegotiatedParameters::QuicNegotiatedParameters() - : idle_connection_state_lifetime(QuicTime::Delta::Zero()), - keepalive_timeout(QuicTime::Delta::Zero()) { -} - -QuicConfig::QuicConfig() - : idle_connection_state_lifetime_(QuicTime::Delta::Zero()), - keepalive_timeout_(QuicTime::Delta::Zero()) { -} - -QuicConfig::~QuicConfig() { -} - -void QuicConfig::SetDefaults() { - idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(300); - keepalive_timeout_ = QuicTime::Delta::Zero(); - congestion_control_.clear(); - congestion_control_.push_back(kQBIC); -} - -bool QuicConfig::SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg) { - const CryptoTag* cgst; - size_t num_cgst; - QuicErrorCode error; - - error = scfg.GetTaglist(kCGST, &cgst, &num_cgst); - if (error != QUIC_NO_ERROR) { - return false; - } - - congestion_control_.assign(cgst, cgst + num_cgst); - - uint32 idle; - error = scfg.GetUint32(kICSL, &idle); - if (error != QUIC_NO_ERROR) { - return false; - } - - idle_connection_state_lifetime_ = QuicTime::Delta::FromSeconds(idle); - - keepalive_timeout_ = QuicTime::Delta::Zero(); - - uint32 keepalive; - error = scfg.GetUint32(kKATO, &keepalive); - // KATO is optional. - if (error == QUIC_NO_ERROR) { - keepalive_timeout_ = QuicTime::Delta::FromSeconds(keepalive); - } - - return true; -} - -void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { - out->SetValue( - kICSL, static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds())); - out->SetValue(kKATO, static_cast<uint32>(keepalive_timeout_.ToSeconds())); - out->SetVector(kCGST, congestion_control_); -} - -QuicErrorCode QuicConfig::ProcessFinalPeerHandshake( - const CryptoHandshakeMessage& msg, - CryptoUtils::Priority priority, - QuicNegotiatedParameters* out_params, - string* error_details) const { - const CryptoTag* their_congestion_controls; - size_t num_their_congestion_controls; - QuicErrorCode error; - - error = msg.GetTaglist(kCGST, &their_congestion_controls, - &num_their_congestion_controls); - if (error != QUIC_NO_ERROR) { - if (error_details) { - *error_details = "Missing CGST"; - } - return error; - } - - if (!CryptoUtils::FindMutualTag(congestion_control_, - their_congestion_controls, - num_their_congestion_controls, - priority, - &out_params->congestion_control, - NULL)) { - if (error_details) { - *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"; - } - return error; - } - - out_params->idle_connection_state_lifetime = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(idle_connection_state_lifetime_.ToSeconds()), - idle)); - - uint32 keepalive; - error = msg.GetUint32(kKATO, &keepalive); - switch (error) { - case QUIC_NO_ERROR: - out_params->keepalive_timeout = QuicTime::Delta::FromSeconds( - std::min(static_cast<uint32>(keepalive_timeout_.ToSeconds()), - keepalive)); - break; - case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: - // KATO is optional. - out_params->keepalive_timeout = QuicTime::Delta::Zero(); - break; - default: - if (error_details) { - *error_details = "Bad KATO"; - } - return error; - } - - return QUIC_NO_ERROR; -} - } // namespace net diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index 43667c0..b43cb38 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -58,54 +58,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream DISALLOW_COPY_AND_ASSIGN(QuicCryptoStream); }; -// QuicNegotiatedParameters contains non-crypto parameters that are agreed upon -// during the crypto handshake. -class NET_EXPORT_PRIVATE QuicNegotiatedParameters { - public: - QuicNegotiatedParameters(); - - CryptoTag congestion_control; - QuicTime::Delta idle_connection_state_lifetime; - QuicTime::Delta keepalive_timeout; -}; - -// QuicConfig contains non-crypto configuration options that are negotiated in -// the crypto handshake. -class NET_EXPORT_PRIVATE QuicConfig { - public: - QuicConfig(); - ~QuicConfig(); - - // SetDefaults sets the members to sensible, default values. - void SetDefaults(); - - // SetFromMessage extracts the non-crypto configuration from |msg| and sets - // the members of this object to match. This is expected to be called in the - // case of a server which is loading a server config. The server config - // contains the non-crypto parameters and so the server will need to keep its - // QuicConfig in sync with the server config that it'll be sending to - // clients. - bool SetFromHandshakeMessage(const CryptoHandshakeMessage& scfg); - - // ToHandshakeMessage serializes the settings in this object as a series of - // tags /value pairs and adds them to |out|. - void ToHandshakeMessage(CryptoHandshakeMessage* out) const; - - QuicErrorCode ProcessFinalPeerHandshake( - const CryptoHandshakeMessage& peer_handshake, - CryptoUtils::Priority priority, - QuicNegotiatedParameters* out_params, - string* error_details) const; - - private: - // Congestion control feedback type. - CryptoTagVector congestion_control_; - // Idle connection state lifetime - QuicTime::Delta idle_connection_state_lifetime_; - // Keepalive timeout, or 0 to turn off keepalive probes - QuicTime::Delta keepalive_timeout_; -}; - } // namespace net #endif // NET_QUIC_QUIC_CRYPTO_STREAM_H_ diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 1370ced..e5bcdcc 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -180,9 +180,11 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { connection_->set_visitor(&visitor_); connection_->SetSendAlgorithm(send_algorithm_); connection_->SetReceiveAlgorithm(receive_algorithm_); + crypto_config_.SetDefaults(); session_.reset(new QuicClientSession(connection_, socket, NULL, &crypto_client_stream_factory_, - "www.google.com", NULL)); + "www.google.com", &crypto_config_, + NULL)); session_->GetCryptoStream()->CryptoConnect(); EXPECT_TRUE(session_->IsCryptoHandshakeComplete()); QuicReliableClientStream* stream = @@ -267,6 +269,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { testing::StrictMock<MockConnectionVisitor> visitor_; scoped_ptr<QuicHttpStream> stream_; scoped_ptr<QuicClientSession> session_; + QuicConfig* config_; + QuicCryptoClientConfig crypto_config_; TestCompletionCallback callback_; HttpRequestInfo request_; HttpRequestHeaders headers_; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 48e647f..b3f11cb 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -220,6 +220,8 @@ enum QuicErrorCode { // There was no intersection between the crypto primitives supported by the // peer and ourselves. QUIC_CRYPTO_NO_SUPPORT, + // The server rejected our client hello messages too many times. + QUIC_CRYPTO_TOO_MANY_REJECTS, // No error. Used as bound while iterating. QUIC_LAST_ERROR, diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc index b5f57e5..2517bac 100644 --- a/net/quic/quic_reliable_client_stream_test.cc +++ b/net/quic/quic_reliable_client_stream_test.cc @@ -42,6 +42,8 @@ class QuicReliableClientStreamTest : public ::testing::Test { testing::StrictMock<MockDelegate> delegate_; MockSession session_; QuicReliableClientStream stream_; + QuicConfig config_; + QuicCryptoClientConfig crypto_config_; }; TEST_F(QuicReliableClientStreamTest, TerminateFromPeer) { diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index 4bb6449..fe25c50 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -198,8 +198,8 @@ scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() { int QuicStreamFactory::Job::DoConnect() { io_state_ = STATE_CONNECT_COMPLETE; - session_ = factory_->CreateSession(host_port_proxy_pair_.first.host(), - address_list_, net_log_); + session_ = factory_->CreateSession(host_port_proxy_pair_, address_list_, + net_log_); session_->StartReading(); int rv = session_->CryptoConnect( base::Bind(&QuicStreamFactory::Job::OnIOComplete, @@ -360,7 +360,7 @@ bool QuicStreamFactory::HasActiveSession( } QuicClientSession* QuicStreamFactory::CreateSession( - const std::string& host, + const HostPortProxyPair& host_port_proxy_pair, const AddressList& address_list, const BoundNetLog& net_log) { QuicGuid guid = random_generator_->RandUint64(); @@ -377,10 +377,16 @@ QuicClientSession* QuicStreamFactory::CreateSession( clock_.get(), random_generator_, socket); QuicConnection* connection = new QuicConnection(guid, addr, helper, false); + + QuicCryptoClientConfig* crypto_config = + GetOrCreateCryptoConfig(host_port_proxy_pair); + DCHECK(crypto_config); + QuicClientSession* session = new QuicClientSession(connection, socket, this, - quic_crypto_client_stream_factory_, host, - net_log.net_log()); + quic_crypto_client_stream_factory_, + host_port_proxy_pair.first.host(), + crypto_config, net_log.net_log()); all_sessions_.insert(session); // owning pointer return session; } @@ -398,5 +404,18 @@ void QuicStreamFactory::ActivateSession( session_aliases_[session].insert(host_port_proxy_pair); } +QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig( + const HostPortProxyPair& host_port_proxy_pair) { + QuicCryptoClientConfig* crypto_config; + if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) { + crypto_config = all_crypto_configs_[host_port_proxy_pair]; + DCHECK(crypto_config); + } else { + crypto_config = new QuicCryptoClientConfig(); + crypto_config->SetDefaults(); + all_crypto_configs_[host_port_proxy_pair] = crypto_config; + } + return crypto_config; +} } // namespace net diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index 48f4da0..629b835 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h @@ -14,6 +14,8 @@ #include "net/base/host_port_pair.h" #include "net/base/net_log.h" #include "net/proxy/proxy_server.h" +#include "net/quic/quic_config.h" +#include "net/quic/quic_crypto_stream.h" #include "net/quic/quic_http_stream.h" #include "net/quic/quic_protocol.h" @@ -106,6 +108,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory { typedef std::set<HostPortProxyPair> AliasSet; typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap; typedef std::set<QuicClientSession*> SessionSet; + typedef std::map<HostPortProxyPair, QuicCryptoClientConfig*> CryptoConfigMap; typedef std::map<HostPortProxyPair, Job*> JobMap; typedef std::map<QuicStreamRequest*, Job*> RequestMap; typedef std::set<QuicStreamRequest*> RequestSet; @@ -114,12 +117,16 @@ class NET_EXPORT_PRIVATE QuicStreamFactory { void OnJobComplete(Job* job, int rv); bool HasActiveSession(const HostPortProxyPair& host_port_proxy_pair); bool HasActiveJob(const HostPortProxyPair& host_port_proxy_pair); - QuicClientSession* CreateSession(const std::string& host, - const AddressList& address_list, - const BoundNetLog& net_log); + QuicClientSession* CreateSession( + const HostPortProxyPair& host_port_proxy_pair, + const AddressList& address_list, + const BoundNetLog& net_log); void ActivateSession(const HostPortProxyPair& host_port_proxy_pair, QuicClientSession* session); + QuicCryptoClientConfig* GetOrCreateCryptoConfig( + const HostPortProxyPair& host_port_proxy_pair); + HostResolver* host_resolver_; ClientSocketFactory* client_socket_factory_; QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_; @@ -133,6 +140,12 @@ class NET_EXPORT_PRIVATE QuicStreamFactory { SessionMap active_sessions_; SessionAliasMap session_aliases_; + // Contains owning pointers to QuicCryptoClientConfig. QuicCryptoClientConfig + // contains configuration and cached state about servers. + // TODO(rtenneti): Persist all_crypto_configs_ to disk and decide when to + // clear the data in the map. + CryptoConfigMap all_crypto_configs_; + JobMap active_jobs_; JobRequestsMap job_requests_map_; RequestMap active_requests_; diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 616e809..ac46977 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -73,6 +73,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_PEER_GOING_AWAY); RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER); RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES); + RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_REJECTS); RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH) RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR); diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 5086044..c2d5988 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -5,8 +5,11 @@ #include "net/quic/test_tools/crypto_test_utils.h" #include "base/strings/string_piece.h" +#include "net/quic/crypto/crypto_handshake.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/quic_clock.h" #include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_crypto_server_stream.h" #include "net/quic/quic_crypto_stream.h" @@ -75,7 +78,15 @@ void CryptoTestUtils::HandshakeWithFakeServer( PacketSavingConnection* server_conn = new PacketSavingConnection(guid, addr, true); TestSession server_session(server_conn, true); - QuicCryptoServerStream server(&server_session); + + QuicConfig config; + QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING); + SetupCryptoServerConfigForTest( + server_session.connection()->clock(), + server_session.connection()->random_generator(), + &config, &crypto_config); + + QuicCryptoServerStream server(config, crypto_config, &server_session); // The client's handshake must have been started already. CHECK_NE(0u, client_conn->packets_.size()); @@ -96,7 +107,13 @@ void CryptoTestUtils::HandshakeWithFakeClient( PacketSavingConnection* client_conn = new PacketSavingConnection(guid, addr, false); TestSession client_session(client_conn, true); - QuicCryptoClientStream client(&client_session, "test.example.com"); + QuicConfig config; + QuicCryptoClientConfig crypto_config; + + config.SetDefaults(); + crypto_config.SetDefaults(); + QuicCryptoClientStream client("test.example.com", config, &client_session, + &crypto_config); CHECK(client.CryptoConnect()); CHECK_EQ(1u, client_conn->packets_.size()); @@ -107,6 +124,23 @@ void CryptoTestUtils::HandshakeWithFakeClient( } // static +void CryptoTestUtils::SetupCryptoServerConfigForTest( + const QuicClock* clock, + QuicRandom* rand, + QuicConfig* config, + QuicCryptoServerConfig* crypto_config) { + config->SetDefaults(); + CryptoHandshakeMessage extra_tags; + config->ToHandshakeMessage(&extra_tags); + + scoped_ptr<CryptoHandshakeMessage> scfg( + crypto_config->AddDefaultConfig(rand, clock, extra_tags)); + if (!config->SetFromHandshakeMessage(*scfg)) { + CHECK(false) << "Crypto config could not be parsed by QuicConfig."; + } +} + +// static string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message, CryptoTag tag) { CryptoTagValueMap::const_iterator it = message.tag_value_map().find(tag); diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index 5ece1db..73ff480 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -14,8 +14,12 @@ namespace net { +class QuicClock; +class QuicConfig; class QuicCryptoClientStream; +class QuicCryptoServerConfig; class QuicCryptoServerStream; +class QuicRandom; namespace test { @@ -29,6 +33,14 @@ class CryptoTestUtils { static void HandshakeWithFakeClient(PacketSavingConnection* server_conn, QuicCryptoServerStream* server); + // SetupCryptoServerConfigForTest configures |config| and |crypto_config| + // with sensible defaults for testing. + static void SetupCryptoServerConfigForTest( + const QuicClock* clock, + QuicRandom* rand, + QuicConfig* config, + QuicCryptoServerConfig* crypto_config); + // Returns the value for the tag |tag| in the tag value map of |message|. static std::string GetValueForTag(const CryptoHandshakeMessage& message, CryptoTag tag); diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc index 5f2d06b..c63d0f0 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/net/quic/test_tools/mock_crypto_client_stream.cc @@ -6,9 +6,12 @@ namespace net { -MockCryptoClientStream::MockCryptoClientStream(QuicSession* session, - const string& server_hostname) - : QuicCryptoClientStream(session, server_hostname) { +MockCryptoClientStream::MockCryptoClientStream( + const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) + : QuicCryptoClientStream(server_hostname, config, session, crypto_config) { } MockCryptoClientStream::~MockCryptoClientStream() { diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h index a115e15..2d9fdc3 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.h +++ b/net/quic/test_tools/mock_crypto_client_stream.h @@ -15,8 +15,10 @@ namespace net { class MockCryptoClientStream : public QuicCryptoClientStream { public: - MockCryptoClientStream(QuicSession* session, - const std::string& server_hostname); + MockCryptoClientStream(const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config); virtual ~MockCryptoClientStream(); // CryptoFramerVisitorInterface implementation. diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/net/quic/test_tools/mock_crypto_client_stream_factory.cc index 153ddd4..f774328 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.cc +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.cc @@ -14,9 +14,12 @@ namespace net { QuicCryptoClientStream* MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( + const string& server_hostname, + const QuicConfig& config, QuicSession* session, - const string& server_hostname) { - return new MockCryptoClientStream(session, server_hostname); + QuicCryptoClientConfig* crypto_config) { + return new MockCryptoClientStream(server_hostname, config, session, + crypto_config); } } // namespace net diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.h b/net/quic/test_tools/mock_crypto_client_stream_factory.h index 1ed81f5..445e766 100644 --- a/net/quic/test_tools/mock_crypto_client_stream_factory.h +++ b/net/quic/test_tools/mock_crypto_client_stream_factory.h @@ -18,7 +18,10 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { virtual ~MockCryptoClientStreamFactory() {} virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( - QuicSession* session, const std::string& server_hostname) OVERRIDE; + const string& server_hostname, + const QuicConfig& config, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) OVERRIDE; }; } // namespace net |