| 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
|
| deleted file mode 100644
|
| index 9d8045d153720ecab25cc73fe2b8f8799780db7a..0000000000000000000000000000000000000000
|
| --- a/net/quic/crypto/quic_crypto_server_config.cc
|
| +++ /dev/null
|
| @@ -1,2066 +0,0 @@
|
| -// Copyright 2013 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "net/quic/crypto/quic_crypto_server_config.h"
|
| -
|
| -#include <stdlib.h>
|
| -
|
| -#include <algorithm>
|
| -#include <memory>
|
| -
|
| -#include "base/macros.h"
|
| -#include "base/memory/ref_counted.h"
|
| -#include "base/stl_util.h"
|
| -#include "crypto/hkdf.h"
|
| -#include "crypto/secure_hash.h"
|
| -#include "net/base/ip_address.h"
|
| -#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
|
| -#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
|
| -#include "net/quic/crypto/cert_compressor.h"
|
| -#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
|
| -#include "net/quic/crypto/channel_id.h"
|
| -#include "net/quic/crypto/crypto_framer.h"
|
| -#include "net/quic/crypto/crypto_handshake_message.h"
|
| -#include "net/quic/crypto/crypto_server_config_protobuf.h"
|
| -#include "net/quic/crypto/crypto_utils.h"
|
| -#include "net/quic/crypto/curve25519_key_exchange.h"
|
| -#include "net/quic/crypto/ephemeral_key_source.h"
|
| -#include "net/quic/crypto/key_exchange.h"
|
| -#include "net/quic/crypto/local_strike_register_client.h"
|
| -#include "net/quic/crypto/p256_key_exchange.h"
|
| -#include "net/quic/crypto/proof_source.h"
|
| -#include "net/quic/crypto/quic_decrypter.h"
|
| -#include "net/quic/crypto/quic_encrypter.h"
|
| -#include "net/quic/crypto/quic_random.h"
|
| -#include "net/quic/crypto/strike_register.h"
|
| -#include "net/quic/crypto/strike_register_client.h"
|
| -#include "net/quic/proto/source_address_token.pb.h"
|
| -#include "net/quic/quic_bug_tracker.h"
|
| -#include "net/quic/quic_clock.h"
|
| -#include "net/quic/quic_flags.h"
|
| -#include "net/quic/quic_protocol.h"
|
| -#include "net/quic/quic_socket_address_coder.h"
|
| -#include "net/quic/quic_utils.h"
|
| -
|
| -using base::StringPiece;
|
| -using crypto::SecureHash;
|
| -using std::map;
|
| -using std::sort;
|
| -using std::string;
|
| -using std::vector;
|
| -
|
| -namespace net {
|
| -
|
| -namespace {
|
| -
|
| -// kMultiplier is the multiple of the CHLO message size that a REJ message
|
| -// must stay under when the client doesn't present a valid source-address
|
| -// token. This is used to protect QUIC from amplification attacks.
|
| -// TODO(rch): Reduce this to 2 again once b/25933682 is fixed.
|
| -const size_t kMultiplier = 3;
|
| -
|
| -const int kMaxTokenAddresses = 4;
|
| -
|
| -string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) {
|
| - crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */,
|
| - "QUIC source address token key",
|
| - CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */,
|
| - 0 /* no subkey secret */);
|
| - return hkdf.server_write_key().as_string();
|
| -}
|
| -
|
| -IPAddress DualstackIPAddress(const IPAddress& ip) {
|
| - if (ip.IsIPv4()) {
|
| - return ConvertIPv4ToIPv4MappedIPv6(ip);
|
| - }
|
| - return ip;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -class ValidateClientHelloHelper {
|
| - public:
|
| - ValidateClientHelloHelper(ValidateClientHelloResultCallback::Result* result,
|
| - ValidateClientHelloResultCallback* done_cb)
|
| - : result_(result), done_cb_(done_cb) {}
|
| -
|
| - ~ValidateClientHelloHelper() {
|
| - QUIC_BUG_IF(done_cb_ != nullptr)
|
| - << "Deleting ValidateClientHelloHelper with a pending callback.";
|
| - }
|
| -
|
| - void ValidationComplete(
|
| - QuicErrorCode error_code,
|
| - const char* error_details,
|
| - std::unique_ptr<ProofSource::Details> proof_source_details) {
|
| - result_->error_code = error_code;
|
| - result_->error_details = error_details;
|
| - done_cb_->Run(result_, std::move(proof_source_details));
|
| - DetachCallback();
|
| - }
|
| -
|
| - void DetachCallback() {
|
| - QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached.";
|
| - done_cb_ = nullptr;
|
| - }
|
| -
|
| - private:
|
| - ValidateClientHelloResultCallback::Result* result_;
|
| - ValidateClientHelloResultCallback* done_cb_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ValidateClientHelloHelper);
|
| -};
|
| -
|
| -class VerifyNonceIsValidAndUniqueCallback
|
| - : public StrikeRegisterClient::ResultCallback {
|
| - public:
|
| - VerifyNonceIsValidAndUniqueCallback(
|
| - ValidateClientHelloResultCallback::Result* result,
|
| - std::unique_ptr<ProofSource::Details> proof_source_details,
|
| - ValidateClientHelloResultCallback* done_cb)
|
| - : result_(result),
|
| - proof_source_details_(std::move(proof_source_details)),
|
| - done_cb_(done_cb) {}
|
| -
|
| - protected:
|
| - void RunImpl(bool nonce_is_valid_and_unique,
|
| - InsertStatus nonce_error) override {
|
| - DVLOG(1) << "Using client nonce, unique: " << nonce_is_valid_and_unique
|
| - << " nonce_error: " << nonce_error;
|
| - if (!nonce_is_valid_and_unique) {
|
| - HandshakeFailureReason client_nonce_error;
|
| - switch (nonce_error) {
|
| - case NONCE_INVALID_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_INVALID_FAILURE;
|
| - break;
|
| - case NONCE_NOT_UNIQUE_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_NOT_UNIQUE_FAILURE;
|
| - break;
|
| - case NONCE_INVALID_ORBIT_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_INVALID_ORBIT_FAILURE;
|
| - break;
|
| - case NONCE_INVALID_TIME_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_INVALID_TIME_FAILURE;
|
| - break;
|
| - case STRIKE_REGISTER_TIMEOUT:
|
| - client_nonce_error = CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT;
|
| - break;
|
| - case STRIKE_REGISTER_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_STRIKE_REGISTER_FAILURE;
|
| - break;
|
| - case NONCE_UNKNOWN_FAILURE:
|
| - client_nonce_error = CLIENT_NONCE_UNKNOWN_FAILURE;
|
| - break;
|
| - case NONCE_OK:
|
| - default:
|
| - QUIC_BUG << "Unexpected client nonce error: " << nonce_error;
|
| - client_nonce_error = CLIENT_NONCE_UNKNOWN_FAILURE;
|
| - break;
|
| - }
|
| - result_->info.reject_reasons.push_back(client_nonce_error);
|
| - }
|
| - done_cb_->Run(result_, std::move(proof_source_details_));
|
| - }
|
| -
|
| - private:
|
| - ValidateClientHelloResultCallback::Result* result_;
|
| - std::unique_ptr<ProofSource::Details> proof_source_details_;
|
| - ValidateClientHelloResultCallback* done_cb_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(VerifyNonceIsValidAndUniqueCallback);
|
| -};
|
| -
|
| -// static
|
| -const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
|
| -
|
| -ClientHelloInfo::ClientHelloInfo(const IPAddress& in_client_ip,
|
| - QuicWallTime in_now)
|
| - : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {}
|
| -
|
| -ClientHelloInfo::~ClientHelloInfo() {}
|
| -
|
| -PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {}
|
| -
|
| -PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {}
|
| -
|
| -ValidateClientHelloResultCallback::Result::Result(
|
| - const CryptoHandshakeMessage& in_client_hello,
|
| - IPAddress in_client_ip,
|
| - QuicWallTime in_now)
|
| - : client_hello(in_client_hello),
|
| - info(in_client_ip, in_now),
|
| - error_code(QUIC_NO_ERROR) {}
|
| -
|
| -ValidateClientHelloResultCallback::Result::~Result() {}
|
| -
|
| -ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {}
|
| -
|
| -ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {}
|
| -
|
| -void ValidateClientHelloResultCallback::Run(
|
| - const Result* result,
|
| - std::unique_ptr<ProofSource::Details> details) {
|
| - RunImpl(result->client_hello, *result, std::move(details));
|
| - delete result;
|
| - delete this;
|
| -}
|
| -
|
| -QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
|
| - : expiry_time(QuicWallTime::Zero()),
|
| - channel_id_enabled(false),
|
| - token_binding_enabled(false),
|
| - p256(false) {}
|
| -
|
| -QuicCryptoServerConfig::ConfigOptions::ConfigOptions(
|
| - const ConfigOptions& other) = default;
|
| -
|
| -QuicCryptoServerConfig::QuicCryptoServerConfig(
|
| - StringPiece source_address_token_secret,
|
| - QuicRandom* server_nonce_entropy,
|
| - std::unique_ptr<ProofSource> proof_source)
|
| - : replay_protection_(true),
|
| - chlo_multiplier_(kMultiplier),
|
| - configs_lock_(),
|
| - primary_config_(nullptr),
|
| - next_config_promotion_time_(QuicWallTime::Zero()),
|
| - server_nonce_strike_register_lock_(),
|
| - proof_source_(std::move(proof_source)),
|
| - strike_register_no_startup_period_(false),
|
| - strike_register_max_entries_(1 << 10),
|
| - strike_register_window_secs_(600),
|
| - source_address_token_future_secs_(3600),
|
| - source_address_token_lifetime_secs_(86400),
|
| - server_nonce_strike_register_max_entries_(1 << 10),
|
| - server_nonce_strike_register_window_secs_(120),
|
| - enable_serving_sct_(false) {
|
| - DCHECK(proof_source_.get());
|
| - default_source_address_token_boxer_.SetKeys(
|
| - {DeriveSourceAddressTokenKey(source_address_token_secret)});
|
| -
|
| - // Generate a random key and orbit for server nonces.
|
| - server_nonce_entropy->RandBytes(server_nonce_orbit_,
|
| - sizeof(server_nonce_orbit_));
|
| - const size_t key_size = server_nonce_boxer_.GetKeySize();
|
| - std::unique_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]);
|
| - server_nonce_entropy->RandBytes(key_bytes.get(), key_size);
|
| -
|
| - server_nonce_boxer_.SetKeys(
|
| - {string(reinterpret_cast<char*>(key_bytes.get()), key_size)});
|
| -}
|
| -
|
| -QuicCryptoServerConfig::~QuicCryptoServerConfig() {
|
| - primary_config_ = nullptr;
|
| -}
|
| -
|
| -// static
|
| -QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig(
|
| - QuicRandom* rand,
|
| - const QuicClock* clock,
|
| - const ConfigOptions& options) {
|
| - CryptoHandshakeMessage msg;
|
| -
|
| - const string curve25519_private_key =
|
| - Curve25519KeyExchange::NewPrivateKey(rand);
|
| - std::unique_ptr<Curve25519KeyExchange> curve25519(
|
| - Curve25519KeyExchange::New(curve25519_private_key));
|
| - StringPiece curve25519_public_value = curve25519->public_value();
|
| -
|
| - string encoded_public_values;
|
| - // First three bytes encode the length of the public value.
|
| - DCHECK_LT(curve25519_public_value.size(), (1U << 24));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(curve25519_public_value.size()));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(curve25519_public_value.size() >> 8));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(curve25519_public_value.size() >> 16));
|
| - encoded_public_values.append(curve25519_public_value.data(),
|
| - curve25519_public_value.size());
|
| -
|
| - string p256_private_key;
|
| - if (options.p256) {
|
| - p256_private_key = P256KeyExchange::NewPrivateKey();
|
| - std::unique_ptr<P256KeyExchange> p256(
|
| - P256KeyExchange::New(p256_private_key));
|
| - StringPiece p256_public_value = p256->public_value();
|
| -
|
| - DCHECK_LT(p256_public_value.size(), (1U << 24));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(p256_public_value.size()));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(p256_public_value.size() >> 8));
|
| - encoded_public_values.push_back(
|
| - static_cast<char>(p256_public_value.size() >> 16));
|
| - encoded_public_values.append(p256_public_value.data(),
|
| - p256_public_value.size());
|
| - }
|
| -
|
| - msg.set_tag(kSCFG);
|
| - if (options.p256) {
|
| - msg.SetVector(kKEXS, QuicTagVector{kC255, kP256});
|
| - } else {
|
| - msg.SetVector(kKEXS, QuicTagVector{kC255});
|
| - }
|
| - msg.SetVector(kAEAD, QuicTagVector{kAESG, kCC20});
|
| - msg.SetStringPiece(kPUBS, encoded_public_values);
|
| -
|
| - if (options.expiry_time.IsZero()) {
|
| - const QuicWallTime now = clock->WallNow();
|
| - const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds(
|
| - 60 * 60 * 24 * 180 /* 180 days, ~six months */));
|
| - const uint64_t expiry_seconds = expiry.ToUNIXSeconds();
|
| - msg.SetValue(kEXPY, expiry_seconds);
|
| - } else {
|
| - msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds());
|
| - }
|
| -
|
| - char orbit_bytes[kOrbitSize];
|
| - if (options.orbit.size() == sizeof(orbit_bytes)) {
|
| - memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes));
|
| - } else {
|
| - DCHECK(options.orbit.empty());
|
| - rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
|
| - }
|
| - msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes)));
|
| -
|
| - if (options.channel_id_enabled) {
|
| - msg.SetVector(kPDMD, QuicTagVector{kCHID});
|
| - }
|
| -
|
| - if (options.token_binding_enabled) {
|
| - msg.SetVector(kTBKP, QuicTagVector{kP256});
|
| - }
|
| -
|
| - if (options.id.empty()) {
|
| - // We need to ensure that the SCID changes whenever the server config does
|
| - // thus we make it a hash of the rest of the server config.
|
| - std::unique_ptr<QuicData> serialized(
|
| - CryptoFramer::ConstructHandshakeMessage(msg));
|
| - std::unique_ptr<SecureHash> hash(SecureHash::Create(SecureHash::SHA256));
|
| - hash->Update(serialized->data(), serialized->length());
|
| -
|
| - char scid_bytes[16];
|
| - hash->Finish(scid_bytes, sizeof(scid_bytes));
|
| - msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes)));
|
| - } else {
|
| - msg.SetStringPiece(kSCID, options.id);
|
| - }
|
| - // Don't put new tags below this point. The SCID generation should hash over
|
| - // everything but itself and so extra tags should be added prior to the
|
| - // preceeding if block.
|
| -
|
| - std::unique_ptr<QuicData> serialized(
|
| - CryptoFramer::ConstructHandshakeMessage(msg));
|
| -
|
| - std::unique_ptr<QuicServerConfigProtobuf> config(
|
| - new QuicServerConfigProtobuf);
|
| - config->set_config(serialized->AsStringPiece());
|
| - QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key();
|
| - curve25519_key->set_tag(kC255);
|
| - curve25519_key->set_private_key(curve25519_private_key);
|
| -
|
| - if (options.p256) {
|
| - QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key();
|
| - p256_key->set_tag(kP256);
|
| - p256_key->set_private_key(p256_private_key);
|
| - }
|
| -
|
| - return config.release();
|
| -}
|
| -
|
| -CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
|
| - QuicServerConfigProtobuf* protobuf,
|
| - const QuicWallTime now) {
|
| - std::unique_ptr<CryptoHandshakeMessage> msg(
|
| - CryptoFramer::ParseMessage(protobuf->config()));
|
| -
|
| - if (!msg.get()) {
|
| - LOG(WARNING) << "Failed to parse server config message";
|
| - return nullptr;
|
| - }
|
| -
|
| - scoped_refptr<Config> config(ParseConfigProtobuf(protobuf));
|
| - if (!config.get()) {
|
| - LOG(WARNING) << "Failed to parse server config message";
|
| - return nullptr;
|
| - }
|
| -
|
| - {
|
| - base::AutoLock locked(configs_lock_);
|
| - if (configs_.find(config->id) != configs_.end()) {
|
| - LOG(WARNING) << "Failed to add config because another with the same "
|
| - "server config id already exists: "
|
| - << QuicUtils::HexEncode(config->id);
|
| - return nullptr;
|
| - }
|
| -
|
| - configs_[config->id] = config;
|
| - SelectNewPrimaryConfig(now);
|
| - DCHECK(primary_config_.get());
|
| - DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
|
| - }
|
| -
|
| - return msg.release();
|
| -}
|
| -
|
| -CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
|
| - QuicRandom* rand,
|
| - const QuicClock* clock,
|
| - const ConfigOptions& options) {
|
| - std::unique_ptr<QuicServerConfigProtobuf> config(
|
| - GenerateConfig(rand, clock, options));
|
| - return AddConfig(config.get(), clock->WallNow());
|
| -}
|
| -
|
| -bool QuicCryptoServerConfig::SetConfigs(
|
| - const vector<QuicServerConfigProtobuf*>& protobufs,
|
| - const QuicWallTime now) {
|
| - vector<scoped_refptr<Config>> parsed_configs;
|
| - bool ok = true;
|
| -
|
| - for (vector<QuicServerConfigProtobuf*>::const_iterator i = protobufs.begin();
|
| - i != protobufs.end(); ++i) {
|
| - scoped_refptr<Config> config(ParseConfigProtobuf(*i));
|
| - if (!config.get()) {
|
| - ok = false;
|
| - break;
|
| - }
|
| -
|
| - parsed_configs.push_back(config);
|
| - }
|
| -
|
| - if (parsed_configs.empty()) {
|
| - LOG(WARNING) << "New config list is empty.";
|
| - ok = false;
|
| - }
|
| -
|
| - if (!ok) {
|
| - LOG(WARNING) << "Rejecting QUIC configs because of above errors";
|
| - } else {
|
| - VLOG(1) << "Updating configs:";
|
| -
|
| - base::AutoLock locked(configs_lock_);
|
| - ConfigMap new_configs;
|
| -
|
| - for (vector<scoped_refptr<Config>>::const_iterator i =
|
| - parsed_configs.begin();
|
| - i != parsed_configs.end(); ++i) {
|
| - scoped_refptr<Config> config = *i;
|
| -
|
| - ConfigMap::iterator it = configs_.find(config->id);
|
| - if (it != configs_.end()) {
|
| - VLOG(1) << "Keeping scid: " << QuicUtils::HexEncode(config->id)
|
| - << " orbit: "
|
| - << QuicUtils::HexEncode(
|
| - reinterpret_cast<const char*>(config->orbit), kOrbitSize)
|
| - << " new primary_time " << config->primary_time.ToUNIXSeconds()
|
| - << " old primary_time "
|
| - << it->second->primary_time.ToUNIXSeconds() << " new priority "
|
| - << config->priority << " old priority " << it->second->priority;
|
| - // Update primary_time and priority.
|
| - it->second->primary_time = config->primary_time;
|
| - it->second->priority = config->priority;
|
| - new_configs.insert(*it);
|
| - } else {
|
| - VLOG(1) << "Adding scid: " << QuicUtils::HexEncode(config->id)
|
| - << " orbit: "
|
| - << QuicUtils::HexEncode(
|
| - reinterpret_cast<const char*>(config->orbit), kOrbitSize)
|
| - << " primary_time " << config->primary_time.ToUNIXSeconds()
|
| - << " priority " << config->priority;
|
| - new_configs.insert(std::make_pair(config->id, config));
|
| - }
|
| - }
|
| -
|
| - configs_.swap(new_configs);
|
| - SelectNewPrimaryConfig(now);
|
| - DCHECK(primary_config_.get());
|
| - DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
|
| - }
|
| -
|
| - return ok;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::SetDefaultSourceAddressTokenKeys(
|
| - const vector<string>& keys) {
|
| - default_source_address_token_boxer_.SetKeys(keys);
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const {
|
| - base::AutoLock locked(configs_lock_);
|
| - for (ConfigMap::const_iterator it = configs_.begin(); it != configs_.end();
|
| - ++it) {
|
| - scids->push_back(it->first);
|
| - }
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::ValidateClientHello(
|
| - const CryptoHandshakeMessage& client_hello,
|
| - const IPAddress& client_ip,
|
| - const IPAddress& server_ip,
|
| - QuicVersion version,
|
| - const QuicClock* clock,
|
| - QuicCryptoProof* crypto_proof,
|
| - ValidateClientHelloResultCallback* done_cb) const {
|
| - const QuicWallTime now(clock->WallNow());
|
| -
|
| - ValidateClientHelloResultCallback::Result* result =
|
| - new ValidateClientHelloResultCallback::Result(client_hello, client_ip,
|
| - now);
|
| -
|
| - StringPiece requested_scid;
|
| - client_hello.GetStringPiece(kSCID, &requested_scid);
|
| -
|
| - uint8_t primary_orbit[kOrbitSize];
|
| - scoped_refptr<Config> requested_config;
|
| - scoped_refptr<Config> primary_config;
|
| - {
|
| - base::AutoLock locked(configs_lock_);
|
| -
|
| - if (!primary_config_.get()) {
|
| - result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
|
| - result->error_details = "No configurations loaded";
|
| - } else {
|
| - if (!next_config_promotion_time_.IsZero() &&
|
| - next_config_promotion_time_.IsAfter(now)) {
|
| - SelectNewPrimaryConfig(now);
|
| - DCHECK(primary_config_.get());
|
| - DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
|
| - }
|
| -
|
| - memcpy(primary_orbit, primary_config_->orbit, sizeof(primary_orbit));
|
| - }
|
| -
|
| - requested_config = GetConfigWithScid(requested_scid);
|
| - primary_config = primary_config_;
|
| - crypto_proof->config = primary_config_;
|
| - }
|
| -
|
| - if (result->error_code == QUIC_NO_ERROR) {
|
| - if (FLAGS_quic_refresh_proof && version > QUIC_VERSION_30) {
|
| - // QUIC v31 and above require a new proof for each CHLO so clear the
|
| - // existing proof, if any.
|
| - crypto_proof->chain = nullptr;
|
| - crypto_proof->signature = "";
|
| - crypto_proof->cert_sct = "";
|
| - }
|
| - EvaluateClientHello(server_ip, version, primary_orbit, requested_config,
|
| - primary_config, crypto_proof, result, done_cb);
|
| - } else {
|
| - done_cb->Run(result, nullptr /* proof_source_details */);
|
| - }
|
| -}
|
| -
|
| -QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| - const ValidateClientHelloResultCallback::Result& validate_chlo_result,
|
| - bool reject_only,
|
| - QuicConnectionId connection_id,
|
| - const IPAddress& server_ip,
|
| - const IPEndPoint& client_address,
|
| - QuicVersion version,
|
| - const QuicVersionVector& supported_versions,
|
| - bool use_stateless_rejects,
|
| - QuicConnectionId server_designated_connection_id,
|
| - const QuicClock* clock,
|
| - QuicRandom* rand,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - QuicCryptoNegotiatedParameters* params,
|
| - QuicCryptoProof* crypto_proof,
|
| - CryptoHandshakeMessage* out,
|
| - DiversificationNonce* out_diversification_nonce,
|
| - string* error_details) const {
|
| - DCHECK(error_details);
|
| -
|
| - const CryptoHandshakeMessage& client_hello =
|
| - validate_chlo_result.client_hello;
|
| - const ClientHelloInfo& info = validate_chlo_result.info;
|
| -
|
| - QuicErrorCode valid = CryptoUtils::ValidateClientHello(
|
| - client_hello, version, supported_versions, error_details);
|
| - if (valid != QUIC_NO_ERROR)
|
| - return valid;
|
| -
|
| - StringPiece requested_scid;
|
| - client_hello.GetStringPiece(kSCID, &requested_scid);
|
| - const QuicWallTime now(clock->WallNow());
|
| -
|
| - scoped_refptr<Config> requested_config;
|
| - scoped_refptr<Config> primary_config;
|
| - {
|
| - base::AutoLock locked(configs_lock_);
|
| -
|
| - if (!primary_config_.get()) {
|
| - *error_details = "No configurations loaded";
|
| - return QUIC_CRYPTO_INTERNAL_ERROR;
|
| - }
|
| -
|
| - if (!next_config_promotion_time_.IsZero() &&
|
| - next_config_promotion_time_.IsAfter(now)) {
|
| - SelectNewPrimaryConfig(now);
|
| - DCHECK(primary_config_.get());
|
| - DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
|
| - }
|
| -
|
| - // Use the config that the client requested in order to do key-agreement.
|
| - // Otherwise give it a copy of |primary_config_| to use.
|
| - primary_config = crypto_proof->config;
|
| - requested_config = GetConfigWithScid(requested_scid);
|
| - }
|
| -
|
| - if (validate_chlo_result.error_code != QUIC_NO_ERROR) {
|
| - *error_details = validate_chlo_result.error_details;
|
| - return validate_chlo_result.error_code;
|
| - }
|
| -
|
| - out->Clear();
|
| -
|
| - bool x509_supported = false;
|
| - bool x509_ecdsa_supported = false;
|
| - ParseProofDemand(client_hello, &x509_supported, &x509_ecdsa_supported);
|
| - if (!x509_supported && FLAGS_quic_require_x509) {
|
| - *error_details = "Missing or invalid PDMD";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| - DCHECK(proof_source_.get());
|
| - string chlo_hash;
|
| - CryptoUtils::HashHandshakeMessage(client_hello, &chlo_hash);
|
| - // No need to get a new proof if one was already generated.
|
| - if (!crypto_proof->chain &&
|
| - !proof_source_->GetProof(
|
| - server_ip, info.sni.as_string(), primary_config->serialized, version,
|
| - chlo_hash, x509_ecdsa_supported, &crypto_proof->chain,
|
| - &crypto_proof->signature, &crypto_proof->cert_sct)) {
|
| - return QUIC_HANDSHAKE_FAILED;
|
| - }
|
| -
|
| - StringPiece cert_sct;
|
| - if (client_hello.GetStringPiece(kCertificateSCTTag, &cert_sct) &&
|
| - cert_sct.empty()) {
|
| - params->sct_supported_by_client = true;
|
| - }
|
| -
|
| - if (!info.reject_reasons.empty() || !requested_config.get()) {
|
| - BuildRejection(version, *primary_config, client_hello, info,
|
| - validate_chlo_result.cached_network_params,
|
| - use_stateless_rejects, server_designated_connection_id, rand,
|
| - compressed_certs_cache, params, *crypto_proof, out);
|
| - return QUIC_NO_ERROR;
|
| - }
|
| -
|
| - if (reject_only) {
|
| - return QUIC_NO_ERROR;
|
| - }
|
| -
|
| - const QuicTag* their_aeads;
|
| - const QuicTag* their_key_exchanges;
|
| - size_t num_their_aeads, num_their_key_exchanges;
|
| - if (client_hello.GetTaglist(kAEAD, &their_aeads, &num_their_aeads) !=
|
| - QUIC_NO_ERROR ||
|
| - client_hello.GetTaglist(kKEXS, &their_key_exchanges,
|
| - &num_their_key_exchanges) != QUIC_NO_ERROR ||
|
| - num_their_aeads != 1 || num_their_key_exchanges != 1) {
|
| - *error_details = "Missing or invalid AEAD or KEXS";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| -
|
| - size_t key_exchange_index;
|
| - if (!QuicUtils::FindMutualTag(requested_config->aead, their_aeads,
|
| - num_their_aeads, QuicUtils::LOCAL_PRIORITY,
|
| - ¶ms->aead, nullptr) ||
|
| - !QuicUtils::FindMutualTag(requested_config->kexs, their_key_exchanges,
|
| - num_their_key_exchanges,
|
| - QuicUtils::LOCAL_PRIORITY,
|
| - ¶ms->key_exchange, &key_exchange_index)) {
|
| - *error_details = "Unsupported AEAD or KEXS";
|
| - return QUIC_CRYPTO_NO_SUPPORT;
|
| - }
|
| -
|
| - if (!requested_config->tb_key_params.empty()) {
|
| - const QuicTag* their_tbkps;
|
| - size_t num_their_tbkps;
|
| - switch (client_hello.GetTaglist(kTBKP, &their_tbkps, &num_their_tbkps)) {
|
| - case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
|
| - break;
|
| - case QUIC_NO_ERROR:
|
| - if (QuicUtils::FindMutualTag(
|
| - requested_config->tb_key_params, their_tbkps, num_their_tbkps,
|
| - QuicUtils::LOCAL_PRIORITY, ¶ms->token_binding_key_param,
|
| - nullptr)) {
|
| - break;
|
| - }
|
| - default:
|
| - *error_details = "Invalid Token Binding key parameter";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| - }
|
| -
|
| - StringPiece public_value;
|
| - if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
|
| - *error_details = "Missing public value";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| -
|
| - const KeyExchange* key_exchange =
|
| - requested_config->key_exchanges[key_exchange_index];
|
| - if (!key_exchange->CalculateSharedKey(public_value,
|
| - ¶ms->initial_premaster_secret)) {
|
| - *error_details = "Invalid public value";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| -
|
| - if (!info.sni.empty()) {
|
| - std::unique_ptr<char[]> sni_tmp(new char[info.sni.length() + 1]);
|
| - memcpy(sni_tmp.get(), info.sni.data(), info.sni.length());
|
| - sni_tmp[info.sni.length()] = 0;
|
| - params->sni = CryptoUtils::NormalizeHostname(sni_tmp.get());
|
| - }
|
| -
|
| - string hkdf_suffix;
|
| - const QuicData& client_hello_serialized = client_hello.GetSerialized();
|
| - hkdf_suffix.reserve(sizeof(connection_id) + client_hello_serialized.length() +
|
| - requested_config->serialized.size());
|
| - hkdf_suffix.append(reinterpret_cast<char*>(&connection_id),
|
| - sizeof(connection_id));
|
| - hkdf_suffix.append(client_hello_serialized.data(),
|
| - client_hello_serialized.length());
|
| - hkdf_suffix.append(requested_config->serialized);
|
| - DCHECK(proof_source_.get());
|
| - if (crypto_proof->chain->certs.empty()) {
|
| - *error_details = "Failed to get certs";
|
| - return QUIC_CRYPTO_INTERNAL_ERROR;
|
| - }
|
| - hkdf_suffix.append(crypto_proof->chain->certs.at(0));
|
| -
|
| - StringPiece cetv_ciphertext;
|
| - if (requested_config->channel_id_enabled &&
|
| - client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) {
|
| - CryptoHandshakeMessage client_hello_copy(client_hello);
|
| - client_hello_copy.Erase(kCETV);
|
| - client_hello_copy.Erase(kPAD);
|
| -
|
| - const QuicData& client_hello_copy_serialized =
|
| - client_hello_copy.GetSerialized();
|
| - string hkdf_input;
|
| - hkdf_input.append(QuicCryptoConfig::kCETVLabel,
|
| - strlen(QuicCryptoConfig::kCETVLabel) + 1);
|
| - hkdf_input.append(reinterpret_cast<char*>(&connection_id),
|
| - sizeof(connection_id));
|
| - hkdf_input.append(client_hello_copy_serialized.data(),
|
| - client_hello_copy_serialized.length());
|
| - hkdf_input.append(requested_config->serialized);
|
| -
|
| - CrypterPair crypters;
|
| - if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
|
| - info.client_nonce, info.server_nonce,
|
| - hkdf_input, Perspective::IS_SERVER,
|
| - CryptoUtils::Diversification::Never(),
|
| - &crypters, nullptr /* subkey secret */)) {
|
| - *error_details = "Symmetric key setup failed";
|
| - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
| - }
|
| -
|
| - char plaintext[kMaxPacketSize];
|
| - size_t plaintext_length = 0;
|
| - const bool success = crypters.decrypter->DecryptPacket(
|
| - kDefaultPathId, 0 /* packet number */,
|
| - StringPiece() /* associated data */, cetv_ciphertext, plaintext,
|
| - &plaintext_length, kMaxPacketSize);
|
| - if (!success) {
|
| - *error_details = "CETV decryption failure";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| - std::unique_ptr<CryptoHandshakeMessage> cetv(
|
| - CryptoFramer::ParseMessage(StringPiece(plaintext, plaintext_length)));
|
| - if (!cetv.get()) {
|
| - *error_details = "CETV parse error";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| -
|
| - StringPiece key, signature;
|
| - if (cetv->GetStringPiece(kCIDK, &key) &&
|
| - cetv->GetStringPiece(kCIDS, &signature)) {
|
| - if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) {
|
| - *error_details = "ChannelID signature failure";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| -
|
| - params->channel_id = key.as_string();
|
| - }
|
| - }
|
| -
|
| - string hkdf_input;
|
| - size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
|
| - hkdf_input.reserve(label_len + hkdf_suffix.size());
|
| - hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
|
| - hkdf_input.append(hkdf_suffix);
|
| -
|
| - string* subkey_secret = ¶ms->initial_subkey_secret;
|
| - CryptoUtils::Diversification diversification =
|
| - CryptoUtils::Diversification::Never();
|
| - if (version > QUIC_VERSION_32) {
|
| - rand->RandBytes(reinterpret_cast<char*>(out_diversification_nonce),
|
| - sizeof(*out_diversification_nonce));
|
| - diversification =
|
| - CryptoUtils::Diversification::Now(out_diversification_nonce);
|
| - }
|
| -
|
| - if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
|
| - info.client_nonce, info.server_nonce, hkdf_input,
|
| - Perspective::IS_SERVER, diversification,
|
| - ¶ms->initial_crypters, subkey_secret)) {
|
| - *error_details = "Symmetric key setup failed";
|
| - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
| - }
|
| -
|
| - string forward_secure_public_value;
|
| - if (ephemeral_key_source_.get()) {
|
| - params->forward_secure_premaster_secret =
|
| - ephemeral_key_source_->CalculateForwardSecureKey(
|
| - key_exchange, rand, clock->ApproximateNow(), public_value,
|
| - &forward_secure_public_value);
|
| - } else {
|
| - std::unique_ptr<KeyExchange> forward_secure_key_exchange(
|
| - key_exchange->NewKeyPair(rand));
|
| - forward_secure_public_value =
|
| - forward_secure_key_exchange->public_value().as_string();
|
| - if (!forward_secure_key_exchange->CalculateSharedKey(
|
| - public_value, ¶ms->forward_secure_premaster_secret)) {
|
| - *error_details = "Invalid public value";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| - }
|
| - }
|
| -
|
| - string forward_secure_hkdf_input;
|
| - label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
|
| - forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size());
|
| - forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel,
|
| - label_len);
|
| - forward_secure_hkdf_input.append(hkdf_suffix);
|
| -
|
| - string shlo_nonce;
|
| - shlo_nonce = NewServerNonce(rand, info.now);
|
| - out->SetStringPiece(kServerNonceTag, shlo_nonce);
|
| -
|
| - if (!CryptoUtils::DeriveKeys(
|
| - params->forward_secure_premaster_secret, params->aead,
|
| - info.client_nonce,
|
| - shlo_nonce.empty() ? info.server_nonce : shlo_nonce,
|
| - forward_secure_hkdf_input, Perspective::IS_SERVER,
|
| - CryptoUtils::Diversification::Never(),
|
| - ¶ms->forward_secure_crypters, ¶ms->subkey_secret)) {
|
| - *error_details = "Symmetric key setup failed";
|
| - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
| - }
|
| -
|
| - out->set_tag(kSHLO);
|
| - QuicTagVector supported_version_tags;
|
| - for (size_t i = 0; i < supported_versions.size(); ++i) {
|
| - supported_version_tags.push_back(
|
| - QuicVersionToQuicTag(supported_versions[i]));
|
| - }
|
| - out->SetVector(kVER, supported_version_tags);
|
| - out->SetStringPiece(
|
| - kSourceAddressTokenTag,
|
| - NewSourceAddressToken(*requested_config.get(), info.source_address_tokens,
|
| - client_address.address(), rand, info.now, nullptr));
|
| - QuicSocketAddressCoder address_coder(client_address);
|
| - out->SetStringPiece(kCADR, address_coder.Encode());
|
| - out->SetStringPiece(kPUBS, forward_secure_public_value);
|
| -
|
| - return QUIC_NO_ERROR;
|
| -}
|
| -
|
| -scoped_refptr<QuicCryptoServerConfig::Config>
|
| -QuicCryptoServerConfig::GetConfigWithScid(StringPiece requested_scid) const {
|
| - // In Chromium, we will dead lock if the lock is held by the current thread.
|
| - // Chromium doesn't have AssertReaderHeld API call.
|
| - // configs_lock_.AssertReaderHeld();
|
| -
|
| - if (!requested_scid.empty()) {
|
| - ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
|
| - if (it != configs_.end()) {
|
| - // We'll use the config that the client requested in order to do
|
| - // key-agreement.
|
| - return scoped_refptr<Config>(it->second);
|
| - }
|
| - }
|
| -
|
| - return scoped_refptr<Config>();
|
| -}
|
| -
|
| -// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
|
| -// Config's based on their primary_time.
|
| -// static
|
| -bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
|
| - const scoped_refptr<Config>& a,
|
| - const scoped_refptr<Config>& b) {
|
| - if (a->primary_time.IsBefore(b->primary_time) ||
|
| - b->primary_time.IsBefore(a->primary_time)) {
|
| - // Primary times differ.
|
| - return a->primary_time.IsBefore(b->primary_time);
|
| - } else if (a->priority != b->priority) {
|
| - // Primary times are equal, sort backwards by priority.
|
| - return a->priority < b->priority;
|
| - } else {
|
| - // Primary times and priorities are equal, sort by config id.
|
| - return a->id < b->id;
|
| - }
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::SelectNewPrimaryConfig(
|
| - const QuicWallTime now) const {
|
| - vector<scoped_refptr<Config>> configs;
|
| - configs.reserve(configs_.size());
|
| -
|
| - for (ConfigMap::const_iterator it = configs_.begin(); it != configs_.end();
|
| - ++it) {
|
| - // TODO(avd) Exclude expired configs?
|
| - configs.push_back(it->second);
|
| - }
|
| -
|
| - if (configs.empty()) {
|
| - if (primary_config_.get()) {
|
| - QUIC_BUG << "No valid QUIC server config. Keeping the current config.";
|
| - } else {
|
| - QUIC_BUG << "No valid QUIC server config.";
|
| - }
|
| - return;
|
| - }
|
| -
|
| - std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
|
| -
|
| - Config* best_candidate = configs[0].get();
|
| -
|
| - for (size_t i = 0; i < configs.size(); ++i) {
|
| - const scoped_refptr<Config> config(configs[i]);
|
| - if (!config->primary_time.IsAfter(now)) {
|
| - if (config->primary_time.IsAfter(best_candidate->primary_time)) {
|
| - best_candidate = config.get();
|
| - }
|
| - continue;
|
| - }
|
| -
|
| - // This is the first config with a primary_time in the future. Thus the
|
| - // previous Config should be the primary and this one should determine the
|
| - // next_config_promotion_time_.
|
| - scoped_refptr<Config> new_primary(best_candidate);
|
| - if (i == 0) {
|
| - // We need the primary_time of the next config.
|
| - if (configs.size() > 1) {
|
| - next_config_promotion_time_ = configs[1]->primary_time;
|
| - } else {
|
| - next_config_promotion_time_ = QuicWallTime::Zero();
|
| - }
|
| - } else {
|
| - next_config_promotion_time_ = config->primary_time;
|
| - }
|
| -
|
| - if (primary_config_.get()) {
|
| - primary_config_->is_primary = false;
|
| - }
|
| - primary_config_ = new_primary;
|
| - new_primary->is_primary = true;
|
| - DVLOG(1) << "New primary config. orbit: "
|
| - << QuicUtils::HexEncode(
|
| - reinterpret_cast<const char*>(primary_config_->orbit),
|
| - kOrbitSize);
|
| - if (primary_config_changed_cb_.get() != nullptr) {
|
| - primary_config_changed_cb_->Run(primary_config_->id);
|
| - }
|
| -
|
| - return;
|
| - }
|
| -
|
| - // All config's primary times are in the past. We should make the most recent
|
| - // and highest priority candidate primary.
|
| - scoped_refptr<Config> new_primary(best_candidate);
|
| - if (primary_config_.get()) {
|
| - primary_config_->is_primary = false;
|
| - }
|
| - primary_config_ = new_primary;
|
| - new_primary->is_primary = true;
|
| - DVLOG(1) << "New primary config. orbit: "
|
| - << QuicUtils::HexEncode(
|
| - reinterpret_cast<const char*>(primary_config_->orbit),
|
| - kOrbitSize)
|
| - << " scid: " << QuicUtils::HexEncode(primary_config_->id);
|
| - next_config_promotion_time_ = QuicWallTime::Zero();
|
| - if (primary_config_changed_cb_.get() != nullptr) {
|
| - primary_config_changed_cb_->Run(primary_config_->id);
|
| - }
|
| -}
|
| -
|
| -class EvaluateClientHelloCallback : public ProofSource::Callback {
|
| - public:
|
| - EvaluateClientHelloCallback(
|
| - const QuicCryptoServerConfig& config,
|
| - bool found_error,
|
| - const IPAddress& server_ip,
|
| - QuicVersion version,
|
| - const uint8_t* primary_orbit,
|
| - scoped_refptr<QuicCryptoServerConfig::Config> requested_config,
|
| - scoped_refptr<QuicCryptoServerConfig::Config> primary_config,
|
| - QuicCryptoProof* crypto_proof,
|
| - ValidateClientHelloResultCallback::Result* client_hello_state,
|
| - ValidateClientHelloResultCallback* done_cb)
|
| - : config_(config),
|
| - found_error_(found_error),
|
| - server_ip_(server_ip),
|
| - version_(version),
|
| - primary_orbit_(primary_orbit),
|
| - requested_config_(std::move(requested_config)),
|
| - primary_config_(std::move(primary_config)),
|
| - crypto_proof_(crypto_proof),
|
| - client_hello_state_(client_hello_state),
|
| - done_cb_(done_cb) {}
|
| -
|
| - void Run(bool ok,
|
| - const scoped_refptr<ProofSource::Chain>& chain,
|
| - const string& signature,
|
| - const string& leaf_cert_sct,
|
| - std::unique_ptr<ProofSource::Details> details) override {
|
| - if (ok) {
|
| - crypto_proof_->chain = chain;
|
| - crypto_proof_->signature = signature;
|
| - crypto_proof_->cert_sct = leaf_cert_sct;
|
| - }
|
| - config_.EvaluateClientHelloAfterGetProof(
|
| - found_error_, server_ip_, version_, primary_orbit_, requested_config_,
|
| - primary_config_, crypto_proof_, std::move(details), !ok,
|
| - client_hello_state_, done_cb_);
|
| - }
|
| -
|
| - private:
|
| - const QuicCryptoServerConfig& config_;
|
| - const bool found_error_;
|
| - const IPAddress& server_ip_;
|
| - const QuicVersion version_;
|
| - const uint8_t* primary_orbit_;
|
| - const scoped_refptr<QuicCryptoServerConfig::Config> requested_config_;
|
| - const scoped_refptr<QuicCryptoServerConfig::Config> primary_config_;
|
| - QuicCryptoProof* crypto_proof_;
|
| - ValidateClientHelloResultCallback::Result* client_hello_state_;
|
| - ValidateClientHelloResultCallback* done_cb_;
|
| -};
|
| -
|
| -void QuicCryptoServerConfig::EvaluateClientHello(
|
| - const IPAddress& server_ip,
|
| - QuicVersion version,
|
| - const uint8_t* primary_orbit,
|
| - scoped_refptr<Config> requested_config,
|
| - scoped_refptr<Config> primary_config,
|
| - QuicCryptoProof* crypto_proof,
|
| - ValidateClientHelloResultCallback::Result* client_hello_state,
|
| - ValidateClientHelloResultCallback* done_cb) const {
|
| - ValidateClientHelloHelper helper(client_hello_state, done_cb);
|
| -
|
| - const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello;
|
| - ClientHelloInfo* info = &(client_hello_state->info);
|
| -
|
| - if (client_hello.size() < kClientHelloMinimumSize) {
|
| - helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH,
|
| - "Client hello too small", nullptr);
|
| - return;
|
| - }
|
| -
|
| - if (client_hello.GetStringPiece(kSNI, &info->sni) &&
|
| - !CryptoUtils::IsValidSNI(info->sni)) {
|
| - helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| - "Invalid SNI name", nullptr);
|
| - return;
|
| - }
|
| -
|
| - client_hello.GetStringPiece(kUAID, &info->user_agent_id);
|
| -
|
| - HandshakeFailureReason source_address_token_error = MAX_FAILURE_REASON;
|
| - StringPiece srct;
|
| - if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
|
| - Config& config = requested_config ? *requested_config : *primary_config;
|
| - source_address_token_error =
|
| - ParseSourceAddressToken(config, 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 {
|
| - source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
|
| - }
|
| -
|
| - if (!requested_config.get()) {
|
| - StringPiece requested_scid;
|
| - if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
|
| - info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
|
| - } else {
|
| - info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
|
| - }
|
| - // No server config with the requested ID.
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
|
| - return;
|
| - }
|
| -
|
| - if (!client_hello.GetStringPiece(kNONC, &info->client_nonce)) {
|
| - info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
|
| - // Report no client nonce as INCHOATE_HELLO_FAILURE.
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
|
| - return;
|
| - }
|
| -
|
| - bool found_error = false;
|
| - if (source_address_token_error != HANDSHAKE_OK) {
|
| - info->reject_reasons.push_back(source_address_token_error);
|
| - // No valid source address token.
|
| - found_error = true;
|
| - }
|
| -
|
| - bool get_proof_failed = false;
|
| - bool x509_supported = false;
|
| - bool x509_ecdsa_supported = false;
|
| - ParseProofDemand(client_hello, &x509_supported, &x509_ecdsa_supported);
|
| - string serialized_config = primary_config->serialized;
|
| - string chlo_hash;
|
| - CryptoUtils::HashHandshakeMessage(client_hello, &chlo_hash);
|
| - bool need_proof = true;
|
| - if (FLAGS_quic_refresh_proof) {
|
| - need_proof = !crypto_proof->chain;
|
| - }
|
| - if (FLAGS_enable_async_get_proof) {
|
| - if (need_proof) {
|
| - // Make an async call to GetProof and setup the callback to trampoline
|
| - // back into EvaluateClientHelloAfterGetProof
|
| - std::unique_ptr<EvaluateClientHelloCallback> cb(
|
| - new EvaluateClientHelloCallback(
|
| - *this, found_error, server_ip, version, primary_orbit,
|
| - requested_config, primary_config, crypto_proof,
|
| - client_hello_state, done_cb));
|
| - proof_source_->GetProof(server_ip, info->sni.as_string(),
|
| - serialized_config, version, chlo_hash,
|
| - x509_ecdsa_supported, std::move(cb));
|
| - helper.DetachCallback();
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // No need to get a new proof if one was already generated.
|
| - if (need_proof &&
|
| - !proof_source_->GetProof(
|
| - server_ip, info->sni.as_string(), serialized_config, version,
|
| - chlo_hash, x509_ecdsa_supported, &crypto_proof->chain,
|
| - &crypto_proof->signature, &crypto_proof->cert_sct)) {
|
| - get_proof_failed = true;
|
| - }
|
| -
|
| - // Details are null because the synchronous version of GetProof does not
|
| - // return any stats. Eventually the synchronous codepath will be eliminated.
|
| - EvaluateClientHelloAfterGetProof(
|
| - found_error, server_ip, version, primary_orbit, requested_config,
|
| - primary_config, crypto_proof, nullptr /* proof_source_details */,
|
| - get_proof_failed, client_hello_state, done_cb);
|
| - helper.DetachCallback();
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::EvaluateClientHelloAfterGetProof(
|
| - bool found_error,
|
| - const IPAddress& server_ip,
|
| - QuicVersion version,
|
| - const uint8_t* primary_orbit,
|
| - scoped_refptr<Config> requested_config,
|
| - scoped_refptr<Config> primary_config,
|
| - QuicCryptoProof* crypto_proof,
|
| - std::unique_ptr<ProofSource::Details> proof_source_details,
|
| - bool get_proof_failed,
|
| - ValidateClientHelloResultCallback::Result* client_hello_state,
|
| - ValidateClientHelloResultCallback* done_cb) const {
|
| - ValidateClientHelloHelper helper(client_hello_state, done_cb);
|
| - const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello;
|
| - ClientHelloInfo* info = &(client_hello_state->info);
|
| -
|
| - if (get_proof_failed) {
|
| - found_error = true;
|
| - info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
|
| - }
|
| -
|
| - if (!ValidateExpectedLeafCertificate(client_hello, *crypto_proof)) {
|
| - found_error = true;
|
| - info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE);
|
| - }
|
| -
|
| - if (info->client_nonce.size() != kNonceSize) {
|
| - info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
|
| - // Invalid client nonce.
|
| - LOG(ERROR) << "Invalid client nonce: " << client_hello.DebugString();
|
| - DVLOG(1) << "Invalid client nonce.";
|
| - found_error = true;
|
| - }
|
| -
|
| - // Server nonce is optional, and used for key derivation if present.
|
| - client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
|
| -
|
| - if (version > QUIC_VERSION_32) {
|
| - DVLOG(1) << "No 0-RTT replay protection in QUIC_VERSION_33 and higher.";
|
| - // If the server nonce is empty and we're requiring handshake confirmation
|
| - // for DoS reasons then we must reject the CHLO.
|
| - if (FLAGS_quic_require_handshake_confirmation &&
|
| - info->server_nonce.empty()) {
|
| - info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
|
| - }
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| -
|
| - if (!replay_protection_) {
|
| - DVLOG(1) << "No replay protection.";
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| -
|
| - if (!info->server_nonce.empty()) {
|
| - // If the server nonce is present, use it to establish uniqueness.
|
| - HandshakeFailureReason server_nonce_error =
|
| - ValidateServerNonce(info->server_nonce, info->now);
|
| - bool is_unique = server_nonce_error == HANDSHAKE_OK;
|
| - if (!is_unique) {
|
| - info->reject_reasons.push_back(server_nonce_error);
|
| - }
|
| - DVLOG(1) << "Using server nonce, unique: " << is_unique;
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| - // If we hit this block, the server nonce was empty. If we're requiring
|
| - // handshake confirmation for DoS reasons and there's no server nonce present,
|
| - // reject the CHLO.
|
| - if (FLAGS_quic_require_handshake_confirmation ||
|
| - FLAGS_quic_require_handshake_confirmation_pre33) {
|
| - info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| -
|
| - // We want to contact strike register only if there are no errors because it
|
| - // is a RPC call and is expensive.
|
| - if (found_error) {
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| -
|
| - // Use the client nonce to establish uniqueness.
|
| - StrikeRegisterClient* strike_register_client;
|
| - {
|
| - base::AutoLock locked(strike_register_client_lock_);
|
| - strike_register_client = strike_register_client_.get();
|
| - }
|
| -
|
| - if (!strike_register_client) {
|
| - // Either a valid server nonces or a strike register is required.
|
| - // Since neither are present, reject the handshake which will send a
|
| - // server nonce to the client.
|
| - info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
|
| - helper.ValidationComplete(QUIC_NO_ERROR, "",
|
| - std::move(proof_source_details));
|
| - return;
|
| - }
|
| -
|
| - strike_register_client->VerifyNonceIsValidAndUnique(
|
| - info->client_nonce, info->now,
|
| - new VerifyNonceIsValidAndUniqueCallback(
|
| - client_hello_state, std::move(proof_source_details), done_cb));
|
| - helper.DetachCallback();
|
| -}
|
| -
|
| -bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
|
| - QuicVersion version,
|
| - StringPiece chlo_hash,
|
| - const SourceAddressTokens& previous_source_address_tokens,
|
| - const IPAddress& server_ip,
|
| - const IPAddress& client_ip,
|
| - const QuicClock* clock,
|
| - QuicRandom* rand,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - const QuicCryptoNegotiatedParameters& params,
|
| - const CachedNetworkParameters* cached_network_params,
|
| - CryptoHandshakeMessage* out) const {
|
| - string serialized;
|
| - string source_address_token;
|
| - const CommonCertSets* common_cert_sets;
|
| - {
|
| - base::AutoLock locked(configs_lock_);
|
| - serialized = primary_config_->serialized;
|
| - common_cert_sets = primary_config_->common_cert_sets;
|
| - source_address_token = NewSourceAddressToken(
|
| - *primary_config_, previous_source_address_tokens, client_ip, rand,
|
| - clock->WallNow(), cached_network_params);
|
| - }
|
| -
|
| - out->set_tag(kSCUP);
|
| - out->SetStringPiece(kSCFG, serialized);
|
| - out->SetStringPiece(kSourceAddressTokenTag, source_address_token);
|
| -
|
| - scoped_refptr<ProofSource::Chain> chain;
|
| - string signature;
|
| - string cert_sct;
|
| - if (!proof_source_->GetProof(server_ip, params.sni, serialized, version,
|
| - chlo_hash, params.x509_ecdsa_supported, &chain,
|
| - &signature, &cert_sct)) {
|
| - DVLOG(1) << "Server: failed to get proof.";
|
| - return false;
|
| - }
|
| -
|
| - const string compressed = CompressChain(
|
| - compressed_certs_cache, chain, params.client_common_set_hashes,
|
| - params.client_cached_cert_hashes, common_cert_sets);
|
| -
|
| - out->SetStringPiece(kCertificateTag, compressed);
|
| - out->SetStringPiece(kPROF, signature);
|
| - if (params.sct_supported_by_client && enable_serving_sct_) {
|
| - if (cert_sct.empty()) {
|
| - DLOG(WARNING) << "SCT is expected but it is empty.";
|
| - } else {
|
| - out->SetStringPiece(kCertificateSCTTag, cert_sct);
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
|
| - QuicVersion version,
|
| - StringPiece chlo_hash,
|
| - const SourceAddressTokens& previous_source_address_tokens,
|
| - const IPAddress& server_ip,
|
| - const IPAddress& client_ip,
|
| - const QuicClock* clock,
|
| - QuicRandom* rand,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - const QuicCryptoNegotiatedParameters& params,
|
| - const CachedNetworkParameters* cached_network_params,
|
| - std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
|
| - string serialized;
|
| - string source_address_token;
|
| - const CommonCertSets* common_cert_sets;
|
| - {
|
| - base::AutoLock locked(configs_lock_);
|
| - serialized = primary_config_->serialized;
|
| - common_cert_sets = primary_config_->common_cert_sets;
|
| - source_address_token = NewSourceAddressToken(
|
| - *primary_config_, previous_source_address_tokens, client_ip, rand,
|
| - clock->WallNow(), cached_network_params);
|
| - }
|
| -
|
| - CryptoHandshakeMessage message;
|
| - message.set_tag(kSCUP);
|
| - message.SetStringPiece(kSCFG, serialized);
|
| - message.SetStringPiece(kSourceAddressTokenTag, source_address_token);
|
| -
|
| - std::unique_ptr<BuildServerConfigUpdateMessageProofSourceCallback>
|
| - proof_source_cb(new BuildServerConfigUpdateMessageProofSourceCallback(
|
| - this, version, compressed_certs_cache, common_cert_sets, params,
|
| - std::move(message), std::move(cb)));
|
| -
|
| - proof_source_->GetProof(server_ip, params.sni, serialized, version, chlo_hash,
|
| - params.x509_ecdsa_supported,
|
| - std::move(proof_source_cb));
|
| -}
|
| -
|
| -QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
|
| - ~BuildServerConfigUpdateMessageProofSourceCallback() {}
|
| -
|
| -QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
|
| - BuildServerConfigUpdateMessageProofSourceCallback(
|
| - const QuicCryptoServerConfig* config,
|
| - QuicVersion version,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - const CommonCertSets* common_cert_sets,
|
| - const QuicCryptoNegotiatedParameters& params,
|
| - CryptoHandshakeMessage message,
|
| - std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb)
|
| - : config_(config),
|
| - version_(version),
|
| - compressed_certs_cache_(compressed_certs_cache),
|
| - common_cert_sets_(common_cert_sets),
|
| - client_common_set_hashes_(params.client_common_set_hashes),
|
| - client_cached_cert_hashes_(params.client_cached_cert_hashes),
|
| - sct_supported_by_client_(params.sct_supported_by_client),
|
| - message_(std::move(message)),
|
| - cb_(std::move(cb)) {}
|
| -
|
| -void QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
|
| - Run(bool ok,
|
| - const scoped_refptr<ProofSource::Chain>& chain,
|
| - const string& signature,
|
| - const string& leaf_cert_sct,
|
| - std::unique_ptr<ProofSource::Details> details) {
|
| - config_->FinishBuildServerConfigUpdateMessage(
|
| - version_, compressed_certs_cache_, common_cert_sets_,
|
| - client_common_set_hashes_, client_cached_cert_hashes_,
|
| - sct_supported_by_client_, ok, chain, signature, leaf_cert_sct,
|
| - std::move(details), std::move(message_), std::move(cb_));
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::FinishBuildServerConfigUpdateMessage(
|
| - QuicVersion version,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - const CommonCertSets* common_cert_sets,
|
| - const string& client_common_set_hashes,
|
| - const string& client_cached_cert_hashes,
|
| - bool sct_supported_by_client,
|
| - bool ok,
|
| - const scoped_refptr<ProofSource::Chain>& chain,
|
| - const string& signature,
|
| - const string& leaf_cert_sct,
|
| - std::unique_ptr<ProofSource::Details> details,
|
| - CryptoHandshakeMessage message,
|
| - std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
|
| - if (!ok) {
|
| - cb->Run(false, message);
|
| - return;
|
| - }
|
| -
|
| - const string compressed =
|
| - CompressChain(compressed_certs_cache, chain, client_common_set_hashes,
|
| - client_cached_cert_hashes, common_cert_sets);
|
| -
|
| - message.SetStringPiece(kCertificateTag, compressed);
|
| - message.SetStringPiece(kPROF, signature);
|
| - if (sct_supported_by_client && enable_serving_sct_) {
|
| - if (leaf_cert_sct.empty()) {
|
| - DLOG(WARNING) << "SCT is expected but it is empty.";
|
| - } else {
|
| - message.SetStringPiece(kCertificateSCTTag, leaf_cert_sct);
|
| - }
|
| - }
|
| -
|
| - cb->Run(true, message);
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::BuildRejection(
|
| - QuicVersion version,
|
| - const Config& config,
|
| - const CryptoHandshakeMessage& client_hello,
|
| - const ClientHelloInfo& info,
|
| - const CachedNetworkParameters& cached_network_params,
|
| - bool use_stateless_rejects,
|
| - QuicConnectionId server_designated_connection_id,
|
| - QuicRandom* rand,
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - QuicCryptoNegotiatedParameters* params,
|
| - const QuicCryptoProof& crypto_proof,
|
| - CryptoHandshakeMessage* out) const {
|
| - if (FLAGS_enable_quic_stateless_reject_support && use_stateless_rejects) {
|
| - DVLOG(1) << "QUIC Crypto server config returning stateless reject "
|
| - << "with server-designated connection ID "
|
| - << server_designated_connection_id;
|
| - out->set_tag(kSREJ);
|
| - out->SetValue(kRCID, server_designated_connection_id);
|
| - } else {
|
| - out->set_tag(kREJ);
|
| - }
|
| - out->SetStringPiece(kSCFG, config.serialized);
|
| - 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));
|
| - }
|
| -
|
| - // Send client the reject reason for debugging purposes.
|
| - DCHECK_LT(0u, info.reject_reasons.size());
|
| - out->SetVector(kRREJ, info.reject_reasons);
|
| -
|
| - // The client may have requested a certificate chain.
|
| - bool x509_supported = false;
|
| - ParseProofDemand(client_hello, &x509_supported,
|
| - ¶ms->x509_ecdsa_supported);
|
| - if (!x509_supported && FLAGS_quic_require_x509) {
|
| - QUIC_BUG << "x509 certificates not supported in proof demand";
|
| - return;
|
| - }
|
| -
|
| - StringPiece client_common_set_hashes;
|
| - if (client_hello.GetStringPiece(kCCS, &client_common_set_hashes)) {
|
| - params->client_common_set_hashes = client_common_set_hashes.as_string();
|
| - }
|
| -
|
| - StringPiece client_cached_cert_hashes;
|
| - if (client_hello.GetStringPiece(kCCRT, &client_cached_cert_hashes)) {
|
| - params->client_cached_cert_hashes = client_cached_cert_hashes.as_string();
|
| - }
|
| -
|
| - const string compressed =
|
| - CompressChain(compressed_certs_cache, crypto_proof.chain,
|
| - params->client_common_set_hashes,
|
| - params->client_cached_cert_hashes, config.common_cert_sets);
|
| -
|
| - // kREJOverheadBytes is a very rough estimate of how much of a REJ
|
| - // message is taken up by things other than the certificates.
|
| - // STK: 56 bytes
|
| - // SNO: 56 bytes
|
| - // SCFG
|
| - // SCID: 16 bytes
|
| - // PUBS: 38 bytes
|
| - const size_t kREJOverheadBytes = 166;
|
| - // max_unverified_size is the number of bytes that the certificate chain,
|
| - // signature, and (optionally) signed certificate timestamp can consume before
|
| - // we will demand a valid source-address token.
|
| - const size_t max_unverified_size =
|
| - client_hello.size() * chlo_multiplier_ - kREJOverheadBytes;
|
| - static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
|
| - "overhead calculation may underflow");
|
| - bool should_return_sct =
|
| - params->sct_supported_by_client && enable_serving_sct_;
|
| - const size_t sct_size = should_return_sct ? crypto_proof.cert_sct.size() : 0;
|
| - if (info.valid_source_address_token ||
|
| - crypto_proof.signature.size() + compressed.size() + sct_size <
|
| - max_unverified_size) {
|
| - out->SetStringPiece(kCertificateTag, compressed);
|
| - out->SetStringPiece(kPROF, crypto_proof.signature);
|
| - if (should_return_sct) {
|
| - if (crypto_proof.cert_sct.empty()) {
|
| - DLOG(WARNING) << "SCT is expected but it is empty.";
|
| - } else {
|
| - out->SetStringPiece(kCertificateSCTTag, crypto_proof.cert_sct);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -string QuicCryptoServerConfig::CompressChain(
|
| - QuicCompressedCertsCache* compressed_certs_cache,
|
| - const scoped_refptr<ProofSource::Chain>& chain,
|
| - const string& client_common_set_hashes,
|
| - const string& client_cached_cert_hashes,
|
| - const CommonCertSets* common_sets) {
|
| - // Check whether the compressed certs is available in the cache.
|
| - DCHECK(compressed_certs_cache);
|
| - const string* cached_value = compressed_certs_cache->GetCompressedCert(
|
| - chain, client_common_set_hashes, client_cached_cert_hashes);
|
| - if (cached_value) {
|
| - return *cached_value;
|
| - }
|
| -
|
| - const string compressed =
|
| - CertCompressor::CompressChain(chain->certs, client_common_set_hashes,
|
| - client_common_set_hashes, common_sets);
|
| -
|
| - // Insert the newly compressed cert to cache.
|
| - compressed_certs_cache->Insert(chain, client_common_set_hashes,
|
| - client_cached_cert_hashes, compressed);
|
| - return compressed;
|
| -}
|
| -
|
| -scoped_refptr<QuicCryptoServerConfig::Config>
|
| -QuicCryptoServerConfig::ParseConfigProtobuf(
|
| - QuicServerConfigProtobuf* protobuf) {
|
| - std::unique_ptr<CryptoHandshakeMessage> msg(
|
| - CryptoFramer::ParseMessage(protobuf->config()));
|
| -
|
| - if (msg->tag() != kSCFG) {
|
| - LOG(WARNING) << "Server config message has tag " << msg->tag()
|
| - << " expected " << kSCFG;
|
| - return nullptr;
|
| - }
|
| -
|
| - scoped_refptr<Config> config(new Config);
|
| - config->serialized = protobuf->config();
|
| -
|
| - if (!protobuf->has_source_address_token_secret_override()) {
|
| - // Use the default boxer.
|
| - config->source_address_token_boxer = &default_source_address_token_boxer_;
|
| - } else {
|
| - // Create override boxer instance.
|
| - CryptoSecretBoxer* boxer = new CryptoSecretBoxer;
|
| - boxer->SetKeys({DeriveSourceAddressTokenKey(
|
| - protobuf->source_address_token_secret_override())});
|
| - config->source_address_token_boxer_storage.reset(boxer);
|
| - config->source_address_token_boxer = boxer;
|
| - }
|
| -
|
| - if (protobuf->has_primary_time()) {
|
| - config->primary_time =
|
| - QuicWallTime::FromUNIXSeconds(protobuf->primary_time());
|
| - }
|
| -
|
| - config->priority = protobuf->priority();
|
| -
|
| - StringPiece scid;
|
| - if (!msg->GetStringPiece(kSCID, &scid)) {
|
| - LOG(WARNING) << "Server config message is missing SCID";
|
| - return nullptr;
|
| - }
|
| - config->id = scid.as_string();
|
| -
|
| - const QuicTag* aead_tags;
|
| - size_t aead_len;
|
| - if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) {
|
| - LOG(WARNING) << "Server config message is missing AEAD";
|
| - return nullptr;
|
| - }
|
| - config->aead = vector<QuicTag>(aead_tags, aead_tags + aead_len);
|
| -
|
| - const QuicTag* kexs_tags;
|
| - size_t kexs_len;
|
| - if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) {
|
| - LOG(WARNING) << "Server config message is missing KEXS";
|
| - return nullptr;
|
| - }
|
| -
|
| - const QuicTag* tbkp_tags;
|
| - size_t tbkp_len;
|
| - QuicErrorCode err;
|
| - if ((err = msg->GetTaglist(kTBKP, &tbkp_tags, &tbkp_len)) !=
|
| - QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND &&
|
| - err != QUIC_NO_ERROR) {
|
| - LOG(WARNING) << "Server config message is missing or has invalid TBKP";
|
| - return nullptr;
|
| - }
|
| - config->tb_key_params = vector<QuicTag>(tbkp_tags, tbkp_tags + tbkp_len);
|
| -
|
| - StringPiece orbit;
|
| - if (!msg->GetStringPiece(kORBT, &orbit)) {
|
| - LOG(WARNING) << "Server config message is missing ORBT";
|
| - return nullptr;
|
| - }
|
| -
|
| - if (orbit.size() != kOrbitSize) {
|
| - LOG(WARNING) << "Orbit value in server config is the wrong length."
|
| - " Got "
|
| - << orbit.size() << " want " << kOrbitSize;
|
| - return nullptr;
|
| - }
|
| - static_assert(sizeof(config->orbit) == kOrbitSize,
|
| - "orbit has incorrect size");
|
| - memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
|
| -
|
| - {
|
| - StrikeRegisterClient* strike_register_client;
|
| - {
|
| - base::AutoLock locked(strike_register_client_lock_);
|
| - strike_register_client = strike_register_client_.get();
|
| - }
|
| -
|
| - if (strike_register_client != nullptr &&
|
| - !strike_register_client->IsKnownOrbit(orbit)) {
|
| - LOG(WARNING)
|
| - << "Rejecting server config with orbit that the strike register "
|
| - "client doesn't know about.";
|
| - return nullptr;
|
| - }
|
| - }
|
| -
|
| - if (kexs_len != protobuf->key_size()) {
|
| - LOG(WARNING) << "Server config has " << kexs_len
|
| - << " key exchange methods configured, but "
|
| - << protobuf->key_size() << " private keys";
|
| - return nullptr;
|
| - }
|
| -
|
| - const QuicTag* proof_demand_tags;
|
| - size_t num_proof_demand_tags;
|
| - if (msg->GetTaglist(kPDMD, &proof_demand_tags, &num_proof_demand_tags) ==
|
| - QUIC_NO_ERROR) {
|
| - for (size_t i = 0; i < num_proof_demand_tags; i++) {
|
| - if (proof_demand_tags[i] == kCHID) {
|
| - config->channel_id_enabled = true;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - for (size_t i = 0; i < kexs_len; i++) {
|
| - const QuicTag tag = kexs_tags[i];
|
| - string private_key;
|
| -
|
| - config->kexs.push_back(tag);
|
| -
|
| - for (size_t j = 0; j < protobuf->key_size(); j++) {
|
| - const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i);
|
| - if (key.tag() == tag) {
|
| - private_key = key.private_key();
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (private_key.empty()) {
|
| - LOG(WARNING) << "Server config contains key exchange method without "
|
| - "corresponding private key: "
|
| - << tag;
|
| - return nullptr;
|
| - }
|
| -
|
| - std::unique_ptr<KeyExchange> ka;
|
| - switch (tag) {
|
| - case kC255:
|
| - ka.reset(Curve25519KeyExchange::New(private_key));
|
| - if (!ka.get()) {
|
| - LOG(WARNING) << "Server config contained an invalid curve25519"
|
| - " private key.";
|
| - return nullptr;
|
| - }
|
| - break;
|
| - case kP256:
|
| - ka.reset(P256KeyExchange::New(private_key));
|
| - if (!ka.get()) {
|
| - LOG(WARNING) << "Server config contained an invalid P-256"
|
| - " private key.";
|
| - return nullptr;
|
| - }
|
| - break;
|
| - default:
|
| - LOG(WARNING) << "Server config message contains unknown key exchange "
|
| - "method: "
|
| - << tag;
|
| - return nullptr;
|
| - }
|
| -
|
| - for (const KeyExchange* key_exchange : config->key_exchanges) {
|
| - if (key_exchange->tag() == tag) {
|
| - LOG(WARNING) << "Duplicate key exchange in config: " << tag;
|
| - return nullptr;
|
| - }
|
| - }
|
| -
|
| - config->key_exchanges.push_back(ka.release());
|
| - }
|
| -
|
| - return config;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::SetEphemeralKeySource(
|
| - EphemeralKeySource* ephemeral_key_source) {
|
| - ephemeral_key_source_.reset(ephemeral_key_source);
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::SetStrikeRegisterClient(
|
| - StrikeRegisterClient* strike_register_client) {
|
| - base::AutoLock locker(strike_register_client_lock_);
|
| - DCHECK(!strike_register_client_.get());
|
| - strike_register_client_.reset(strike_register_client);
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_replay_protection(bool on) {
|
| - replay_protection_ = on;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_chlo_multiplier(size_t multiplier) {
|
| - chlo_multiplier_ = multiplier;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_strike_register_no_startup_period() {
|
| - base::AutoLock locker(strike_register_client_lock_);
|
| - DCHECK(!strike_register_client_.get());
|
| - strike_register_no_startup_period_ = true;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_strike_register_max_entries(
|
| - uint32_t max_entries) {
|
| - base::AutoLock locker(strike_register_client_lock_);
|
| - DCHECK(!strike_register_client_.get());
|
| - strike_register_max_entries_ = max_entries;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_strike_register_window_secs(
|
| - uint32_t window_secs) {
|
| - base::AutoLock locker(strike_register_client_lock_);
|
| - DCHECK(!strike_register_client_.get());
|
| - strike_register_window_secs_ = window_secs;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_source_address_token_future_secs(
|
| - uint32_t future_secs) {
|
| - source_address_token_future_secs_ = future_secs;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
|
| - uint32_t lifetime_secs) {
|
| - source_address_token_lifetime_secs_ = lifetime_secs;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_server_nonce_strike_register_max_entries(
|
| - uint32_t max_entries) {
|
| - DCHECK(!server_nonce_strike_register_.get());
|
| - server_nonce_strike_register_max_entries_ = max_entries;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs(
|
| - uint32_t window_secs) {
|
| - DCHECK(!server_nonce_strike_register_.get());
|
| - server_nonce_strike_register_window_secs_ = window_secs;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::set_enable_serving_sct(bool enable_serving_sct) {
|
| - enable_serving_sct_ = enable_serving_sct;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
|
| - PrimaryConfigChangedCallback* cb) {
|
| - base::AutoLock locked(configs_lock_);
|
| - primary_config_changed_cb_.reset(cb);
|
| -}
|
| -
|
| -string QuicCryptoServerConfig::NewSourceAddressToken(
|
| - const Config& config,
|
| - const SourceAddressTokens& previous_tokens,
|
| - const IPAddress& ip,
|
| - QuicRandom* rand,
|
| - QuicWallTime now,
|
| - const CachedNetworkParameters* cached_network_params) const {
|
| - SourceAddressTokens source_address_tokens;
|
| - SourceAddressToken* source_address_token = source_address_tokens.add_tokens();
|
| - source_address_token->set_ip(IPAddressToPackedString(DualstackIPAddress(ip)));
|
| - source_address_token->set_timestamp(now.ToUNIXSeconds());
|
| - if (cached_network_params != nullptr) {
|
| - *(source_address_token->mutable_cached_network_parameters()) =
|
| - *cached_network_params;
|
| - }
|
| -
|
| - // Append previous tokens.
|
| - for (const SourceAddressToken& token : previous_tokens.tokens()) {
|
| - 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_tokens.SerializeAsString());
|
| -}
|
| -
|
| -int QuicCryptoServerConfig::NumberOfConfigs() const {
|
| - base::AutoLock locked(configs_lock_);
|
| - return configs_.size();
|
| -}
|
| -
|
| -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 (!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 source_address_token;
|
| - if (!source_address_token.ParseFromArray(plaintext.data(),
|
| - plaintext.size())) {
|
| - return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
|
| - }
|
| - *tokens->add_tokens() = source_address_token;
|
| - }
|
| -
|
| - return HANDSHAKE_OK;
|
| -}
|
| -
|
| -HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens(
|
| - const SourceAddressTokens& source_address_tokens,
|
| - const IPAddress& ip,
|
| - QuicWallTime now,
|
| - CachedNetworkParameters* cached_network_params) const {
|
| - HandshakeFailureReason reason =
|
| - SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
|
| - for (const SourceAddressToken& token : source_address_tokens.tokens()) {
|
| - 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 IPAddress& ip,
|
| - QuicWallTime now) const {
|
| - if (source_address_token.ip() !=
|
| - IPAddressToPackedString(DualstackIPAddress(ip))) {
|
| - // 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 =
|
| - 4 /* timestamp */ + 20 /* random bytes */;
|
| -
|
| -string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
|
| - QuicWallTime now) const {
|
| - const uint32_t timestamp = static_cast<uint32_t>(now.ToUNIXSeconds());
|
| -
|
| - uint8_t server_nonce[kServerNoncePlaintextSize];
|
| - static_assert(sizeof(server_nonce) > sizeof(timestamp), "nonce too small");
|
| - server_nonce[0] = static_cast<uint8_t>(timestamp >> 24);
|
| - server_nonce[1] = static_cast<uint8_t>(timestamp >> 16);
|
| - server_nonce[2] = static_cast<uint8_t>(timestamp >> 8);
|
| - server_nonce[3] = static_cast<uint8_t>(timestamp);
|
| - rand->RandBytes(&server_nonce[sizeof(timestamp)],
|
| - sizeof(server_nonce) - sizeof(timestamp));
|
| -
|
| - return server_nonce_boxer_.Box(
|
| - rand,
|
| - StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce)));
|
| -}
|
| -
|
| -HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce(
|
| - StringPiece token,
|
| - QuicWallTime now) const {
|
| - string storage;
|
| - StringPiece plaintext;
|
| - if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) {
|
| - return SERVER_NONCE_DECRYPTION_FAILURE;
|
| - }
|
| -
|
| - // plaintext contains:
|
| - // uint32_t timestamp
|
| - // uint8_t[20] random bytes
|
| -
|
| - if (plaintext.size() != kServerNoncePlaintextSize) {
|
| - // This should never happen because the value decrypted correctly.
|
| - QUIC_BUG << "Seemingly valid server nonce had incorrect length.";
|
| - return SERVER_NONCE_INVALID_FAILURE;
|
| - }
|
| -
|
| - uint8_t server_nonce[32];
|
| - memcpy(server_nonce, plaintext.data(), 4);
|
| - memcpy(server_nonce + 4, server_nonce_orbit_, sizeof(server_nonce_orbit_));
|
| - memcpy(server_nonce + 4 + sizeof(server_nonce_orbit_), plaintext.data() + 4,
|
| - 20);
|
| - static_assert(4 + sizeof(server_nonce_orbit_) + 20 == sizeof(server_nonce),
|
| - "bad nonce buffer length");
|
| -
|
| - InsertStatus nonce_error;
|
| - {
|
| - base::AutoLock auto_lock(server_nonce_strike_register_lock_);
|
| - if (server_nonce_strike_register_.get() == nullptr) {
|
| - server_nonce_strike_register_.reset(new StrikeRegister(
|
| - server_nonce_strike_register_max_entries_,
|
| - static_cast<uint32_t>(now.ToUNIXSeconds()),
|
| - server_nonce_strike_register_window_secs_, server_nonce_orbit_,
|
| - StrikeRegister::NO_STARTUP_PERIOD_NEEDED));
|
| - }
|
| - nonce_error = server_nonce_strike_register_->Insert(
|
| - server_nonce, static_cast<uint32_t>(now.ToUNIXSeconds()));
|
| - }
|
| -
|
| - switch (nonce_error) {
|
| - case NONCE_OK:
|
| - return HANDSHAKE_OK;
|
| - case NONCE_INVALID_FAILURE:
|
| - case NONCE_INVALID_ORBIT_FAILURE:
|
| - return SERVER_NONCE_INVALID_FAILURE;
|
| - case NONCE_NOT_UNIQUE_FAILURE:
|
| - return SERVER_NONCE_NOT_UNIQUE_FAILURE;
|
| - case NONCE_INVALID_TIME_FAILURE:
|
| - return SERVER_NONCE_INVALID_TIME_FAILURE;
|
| - case NONCE_UNKNOWN_FAILURE:
|
| - case STRIKE_REGISTER_TIMEOUT:
|
| - case STRIKE_REGISTER_FAILURE:
|
| - default:
|
| - QUIC_BUG << "Unexpected server nonce error: " << nonce_error;
|
| - return SERVER_NONCE_NOT_UNIQUE_FAILURE;
|
| - }
|
| -}
|
| -
|
| -bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate(
|
| - const CryptoHandshakeMessage& client_hello,
|
| - const QuicCryptoProof& crypto_proof) const {
|
| - if (crypto_proof.chain->certs.empty()) {
|
| - return false;
|
| - }
|
| -
|
| - uint64_t hash_from_client;
|
| - if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) {
|
| - return false;
|
| - }
|
| - return CryptoUtils::ComputeLeafCertHash(crypto_proof.chain->certs.at(0)) ==
|
| - hash_from_client;
|
| -}
|
| -
|
| -void QuicCryptoServerConfig::ParseProofDemand(
|
| - const CryptoHandshakeMessage& client_hello,
|
| - bool* x509_supported,
|
| - bool* x509_ecdsa_supported) const {
|
| - const QuicTag* their_proof_demands;
|
| - size_t num_their_proof_demands;
|
| -
|
| - if (client_hello.GetTaglist(kPDMD, &their_proof_demands,
|
| - &num_their_proof_demands) != QUIC_NO_ERROR) {
|
| - return;
|
| - }
|
| -
|
| - *x509_supported = false;
|
| - for (size_t i = 0; i < num_their_proof_demands; i++) {
|
| - switch (their_proof_demands[i]) {
|
| - case kX509:
|
| - *x509_supported = true;
|
| - *x509_ecdsa_supported = true;
|
| - break;
|
| - case kX59R:
|
| - *x509_supported = true;
|
| - break;
|
| - }
|
| - }
|
| -}
|
| -
|
| -QuicCryptoServerConfig::Config::Config()
|
| - : channel_id_enabled(false),
|
| - is_primary(false),
|
| - primary_time(QuicWallTime::Zero()),
|
| - priority(0),
|
| - source_address_token_boxer(nullptr) {}
|
| -
|
| -QuicCryptoServerConfig::Config::~Config() {
|
| - STLDeleteElements(&key_exchanges);
|
| -}
|
| -
|
| -QuicCryptoProof::QuicCryptoProof() {}
|
| -QuicCryptoProof::~QuicCryptoProof() {}
|
| -} // namespace net
|
|
|