Index: net/quic/crypto/quic_crypto_server_config.cc |
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc |
index f752a9e6a0aae60e5e54839eec7b0eb3f8bd0172..4a5dc8a75806da293e0f8948080064d720228354 100644 |
--- a/net/quic/crypto/quic_crypto_server_config.cc |
+++ b/net/quic/crypto/quic_crypto_server_config.cc |
@@ -50,6 +50,8 @@ namespace net { |
namespace { |
+const size_t kMaxTokenAddresses = 4; |
+ |
string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) { |
crypto::HKDF hkdf(source_address_token_secret, |
StringPiece() /* no salt */, |
@@ -765,12 +767,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( |
(QuicVersionToQuicTag(supported_versions[i])); |
} |
out->SetVector(kVER, supported_version_tags); |
- out->SetStringPiece(kSourceAddressTokenTag, |
- NewSourceAddressToken(*requested_config.get(), |
- client_address, |
- rand, |
- info.now, |
- nullptr)); |
+ out->SetStringPiece( |
+ kSourceAddressTokenTag, |
+ NewSourceAddressToken(*requested_config.get(), info.source_address_tokens, |
+ client_address, rand, info.now, nullptr)); |
QuicSocketAddressCoder address_coder(client_address); |
out->SetStringPiece(kCADR, address_coder.Encode()); |
out->SetStringPiece(kPUBS, forward_secure_public_value); |
@@ -940,12 +940,20 @@ void QuicCryptoServerConfig::EvaluateClientHello( |
HandshakeFailureReason source_address_token_error; |
StringPiece srct; |
if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) { |
- source_address_token_error = |
- ValidateSourceAddressToken(*requested_config.get(), |
- srct, |
- info->client_ip, |
- info->now, |
- &client_hello_state->cached_network_params); |
+ if (!FLAGS_quic_use_multiple_address_in_source_tokens) { |
+ source_address_token_error = ValidateSourceAddressToken( |
+ *requested_config.get(), srct, info->client_ip, info->now, |
+ &client_hello_state->cached_network_params); |
+ } else { |
+ source_address_token_error = ParseSourceAddressToken( |
+ *requested_config.get(), srct, &info->source_address_tokens); |
+ |
+ if (source_address_token_error == HANDSHAKE_OK) { |
+ source_address_token_error = ValidateSourceAddressTokens( |
+ info->source_address_tokens, info->client_ip, info->now, |
+ &client_hello_state->cached_network_params); |
+ } |
+ } |
info->valid_source_address_token = |
(source_address_token_error == HANDSHAKE_OK); |
} else { |
@@ -1035,6 +1043,7 @@ void QuicCryptoServerConfig::EvaluateClientHello( |
} |
bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( |
+ const SourceAddressTokens& previous_source_address_tokens, |
const IPEndPoint& server_ip, |
const IPEndPoint& client_ip, |
const QuicClock* clock, |
@@ -1045,12 +1054,11 @@ bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( |
base::AutoLock locked(configs_lock_); |
out->set_tag(kSCUP); |
out->SetStringPiece(kSCFG, primary_config_->serialized); |
- out->SetStringPiece(kSourceAddressTokenTag, |
- NewSourceAddressToken(*primary_config_.get(), |
- client_ip, |
- rand, |
- clock->WallNow(), |
- cached_network_params)); |
+ out->SetStringPiece( |
+ kSourceAddressTokenTag, |
+ NewSourceAddressToken(*primary_config_.get(), |
+ previous_source_address_tokens, client_ip, rand, |
+ clock->WallNow(), cached_network_params)); |
if (proof_source_ == nullptr) { |
// Insecure QUIC, can send SCFG without proof. |
@@ -1086,13 +1094,10 @@ void QuicCryptoServerConfig::BuildRejection( |
CryptoHandshakeMessage* out) const { |
out->set_tag(kREJ); |
out->SetStringPiece(kSCFG, config.serialized); |
- out->SetStringPiece(kSourceAddressTokenTag, |
- NewSourceAddressToken( |
- config, |
- info.client_ip, |
- rand, |
- info.now, |
- &cached_network_params)); |
+ out->SetStringPiece( |
+ kSourceAddressTokenTag, |
+ NewSourceAddressToken(config, info.source_address_tokens, info.client_ip, |
+ rand, info.now, &cached_network_params)); |
if (replay_protection_) { |
out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); |
} |
@@ -1412,6 +1417,7 @@ void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb( |
string QuicCryptoServerConfig::NewSourceAddressToken( |
const Config& config, |
+ const SourceAddressTokens& previous_tokens, |
const IPEndPoint& ip, |
QuicRandom* rand, |
QuicWallTime now, |
@@ -1420,21 +1426,80 @@ string QuicCryptoServerConfig::NewSourceAddressToken( |
if (ip.GetSockAddrFamily() == AF_INET) { |
ip_address = ConvertIPv4NumberToIPv6Number(ip_address); |
} |
- SourceAddressToken source_address_token; |
- source_address_token.set_ip(IPAddressToPackedString(ip_address)); |
- source_address_token.set_timestamp(now.ToUNIXSeconds()); |
+ SourceAddressTokens source_address_tokens; |
+ SourceAddressToken* source_address_token = source_address_tokens.add_tokens(); |
+ source_address_token->set_ip(IPAddressToPackedString(ip_address)); |
+ source_address_token->set_timestamp(now.ToUNIXSeconds()); |
if (cached_network_params != nullptr) { |
- source_address_token.set_cached_network_parameters(*cached_network_params); |
+ *(source_address_token->mutable_cached_network_parameters()) = |
+ *cached_network_params; |
+ } |
+ |
+ if (!FLAGS_quic_use_multiple_address_in_source_tokens) { |
+ return config.source_address_token_boxer->Box( |
+ rand, source_address_token->SerializeAsString()); |
+ } |
+ |
+ // Append previous tokens. |
+ for (size_t i = 0; i < previous_tokens.tokens_size(); i++) { |
+ const SourceAddressToken& token = previous_tokens.tokens(i); |
+ if (source_address_tokens.tokens_size() > kMaxTokenAddresses) { |
+ break; |
+ } |
+ |
+ if (token.ip() == source_address_token->ip()) { |
+ // It's for the same IP address. |
+ continue; |
+ } |
+ |
+ if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) { |
+ continue; |
+ } |
+ |
+ *(source_address_tokens.add_tokens()) = token; |
} |
return config.source_address_token_boxer->Box( |
- rand, source_address_token.SerializeAsString()); |
+ rand, source_address_tokens.SerializeAsString()); |
} |
bool QuicCryptoServerConfig::HasProofSource() const { |
return proof_source_ != nullptr; |
} |
+HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken( |
+ const Config& config, |
+ StringPiece token, |
+ SourceAddressTokens* tokens) const { |
+ string storage; |
+ StringPiece plaintext; |
+ if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) { |
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE; |
+ } |
+ |
+ if (!FLAGS_quic_use_multiple_address_in_source_tokens) { |
+ SourceAddressToken token; |
+ if (!token.ParseFromArray(plaintext.data(), plaintext.size())) { |
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; |
+ } |
+ *(tokens->add_tokens()) = token; |
+ return HANDSHAKE_OK; |
+ } |
+ |
+ if (!tokens->ParseFromArray(plaintext.data(), plaintext.size())) { |
+ // Some clients might still be using the old source token format so |
+ // attempt to parse that format. |
+ // TODO(rch): remove this code once the new format is ubiquitous. |
+ SourceAddressToken token; |
+ if (!token.ParseFromArray(plaintext.data(), plaintext.size())) { |
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; |
+ } |
+ *tokens->add_tokens() = token; |
+ } |
+ |
+ return HANDSHAKE_OK; |
+} |
+ |
HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken( |
const Config& config, |
StringPiece token, |
@@ -1483,6 +1548,63 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken( |
return HANDSHAKE_OK; |
} |
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens( |
+ const SourceAddressTokens& source_address_tokens, |
+ const IPEndPoint& ip, |
+ QuicWallTime now, |
+ CachedNetworkParameters* cached_network_params) const { |
+ HandshakeFailureReason reason = |
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; |
+ for (size_t i = 0; i < source_address_tokens.tokens_size(); i++) { |
+ const SourceAddressToken& token = source_address_tokens.tokens(i); |
+ reason = ValidateSingleSourceAddressToken(token, ip, now); |
+ if (reason == HANDSHAKE_OK) { |
+ if (token.has_cached_network_parameters()) { |
+ *cached_network_params = token.cached_network_parameters(); |
+ } |
+ break; |
+ } |
+ } |
+ return reason; |
+} |
+ |
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken( |
+ const SourceAddressToken& source_address_token, |
+ const IPEndPoint& ip, |
+ QuicWallTime now) const { |
+ IPAddressNumber ip_address = ip.address(); |
+ if (ip.GetSockAddrFamily() == AF_INET) { |
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address); |
+ } |
+ if (source_address_token.ip() != IPAddressToPackedString(ip_address)) { |
+ // It's for a different IP address. |
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; |
+ } |
+ |
+ return ValidateSourceAddressTokenTimestamp(source_address_token, now); |
+} |
+ |
+HandshakeFailureReason |
+QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp( |
+ const SourceAddressToken& source_address_token, |
+ QuicWallTime now) const { |
+ const QuicWallTime timestamp( |
+ QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); |
+ const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); |
+ |
+ if (now.IsBefore(timestamp) && |
+ delta.ToSeconds() > source_address_token_future_secs_) { |
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE; |
+ } |
+ |
+ if (now.IsAfter(timestamp) && |
+ delta.ToSeconds() > source_address_token_lifetime_secs_) { |
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE; |
+ } |
+ |
+ return HANDSHAKE_OK; |
+} |
+ |
// kServerNoncePlaintextSize is the number of bytes in an unencrypted server |
// nonce. |
static const size_t kServerNoncePlaintextSize = |