| 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 =
|
|
|