Index: net/quic/crypto/crypto_handshake.cc |
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc |
index 18840b440d20beca4baf111e9ff8a2c77ace5deb..1d9b01b809f06d3c018d88ba7f726bb0a743c309 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() + |