| Index: net/quic/core/crypto/quic_crypto_server_config.cc
|
| diff --git a/net/quic/core/crypto/quic_crypto_server_config.cc b/net/quic/core/crypto/quic_crypto_server_config.cc
|
| index 0853835de8611f21c538911502a4e22f19445551..15ec453df3e18d222c93feea9a0893e4f70aaedd 100644
|
| --- a/net/quic/core/crypto/quic_crypto_server_config.cc
|
| +++ b/net/quic/core/crypto/quic_crypto_server_config.cc
|
| @@ -203,6 +203,10 @@ ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {}
|
|
|
| ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {}
|
|
|
| +ProcessClientHelloResultCallback::ProcessClientHelloResultCallback() {}
|
| +
|
| +ProcessClientHelloResultCallback::~ProcessClientHelloResultCallback() {}
|
| +
|
| QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
|
| : expiry_time(QuicWallTime::Zero()),
|
| channel_id_enabled(false),
|
| @@ -552,7 +556,39 @@ void QuicCryptoServerConfig::ValidateClientHello(
|
| }
|
| }
|
|
|
| -QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| +class ProcessClientHelloHelper {
|
| + public:
|
| + explicit ProcessClientHelloHelper(
|
| + std::unique_ptr<ProcessClientHelloResultCallback>* done_cb)
|
| + : done_cb_(done_cb) {}
|
| +
|
| + ~ProcessClientHelloHelper() {
|
| + QUIC_BUG_IF(done_cb_ != nullptr)
|
| + << "Deleting ProcessClientHelloHelper with a pending callback.";
|
| + }
|
| +
|
| + void Fail(QuicErrorCode error, const string& error_details) {
|
| + (*done_cb_)->Run(error, error_details, nullptr, nullptr);
|
| + DetachCallback();
|
| + }
|
| +
|
| + void Succeed(std::unique_ptr<CryptoHandshakeMessage> message,
|
| + std::unique_ptr<DiversificationNonce> diversification_nonce) {
|
| + (*done_cb_)->Run(QUIC_NO_ERROR, string(), std::move(message),
|
| + std::move(diversification_nonce));
|
| + DetachCallback();
|
| + }
|
| +
|
| + void DetachCallback() {
|
| + QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached.";
|
| + done_cb_ = nullptr;
|
| + }
|
| +
|
| + private:
|
| + std::unique_ptr<ProcessClientHelloResultCallback>* done_cb_;
|
| +};
|
| +
|
| +void QuicCryptoServerConfig::ProcessClientHello(
|
| scoped_refptr<ValidateClientHelloResultCallback::Result>
|
| validate_chlo_result,
|
| bool reject_only,
|
| @@ -570,19 +606,22 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| QuicCryptoProof* crypto_proof,
|
| QuicByteCount total_framing_overhead,
|
| QuicByteCount chlo_packet_size,
|
| - CryptoHandshakeMessage* out,
|
| - DiversificationNonce* out_diversification_nonce,
|
| - string* error_details) const {
|
| - DCHECK(error_details);
|
| + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const {
|
| + DCHECK(done_cb);
|
| +
|
| + ProcessClientHelloHelper helper(&done_cb);
|
|
|
| const CryptoHandshakeMessage& client_hello =
|
| validate_chlo_result->client_hello;
|
| const ClientHelloInfo& info = validate_chlo_result->info;
|
|
|
| + string error_details;
|
| QuicErrorCode valid = CryptoUtils::ValidateClientHello(
|
| - client_hello, version, supported_versions, error_details);
|
| - if (valid != QUIC_NO_ERROR)
|
| - return valid;
|
| + client_hello, version, supported_versions, &error_details);
|
| + if (valid != QUIC_NO_ERROR) {
|
| + helper.Fail(valid, error_details);
|
| + return;
|
| + }
|
|
|
| StringPiece requested_scid;
|
| client_hello.GetStringPiece(kSCID, &requested_scid);
|
| @@ -590,71 +629,117 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
|
|
| scoped_refptr<Config> requested_config;
|
| scoped_refptr<Config> primary_config;
|
| + bool no_primary_config = false;
|
| {
|
| base::AutoLock locked(configs_lock_);
|
|
|
| - if (!primary_config_.get()) {
|
| - *error_details = "No configurations loaded";
|
| - return QUIC_CRYPTO_INTERNAL_ERROR;
|
| - }
|
| + if (!primary_config_) {
|
| + no_primary_config = true;
|
| + } 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_);
|
| + }
|
|
|
| - 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);
|
| }
|
| -
|
| - // 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 (no_primary_config) {
|
| + helper.Fail(QUIC_CRYPTO_INTERNAL_ERROR, "No configurations loaded");
|
| + return;
|
| }
|
|
|
| if (validate_chlo_result->error_code != QUIC_NO_ERROR) {
|
| - *error_details = validate_chlo_result->error_details;
|
| - return validate_chlo_result->error_code;
|
| + helper.Fail(validate_chlo_result->error_code,
|
| + validate_chlo_result->error_details);
|
| + return;
|
| }
|
|
|
| - out->Clear();
|
| -
|
| if (!ClientDemandsX509Proof(client_hello)) {
|
| - *error_details = "Missing or invalid PDMD";
|
| - return QUIC_UNSUPPORTED_PROOF_DEMAND;
|
| + helper.Fail(QUIC_UNSUPPORTED_PROOF_DEMAND, "Missing or invalid PDMD");
|
| + return;
|
| }
|
| 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,
|
| - &crypto_proof->chain, &crypto_proof->signature,
|
| - &crypto_proof->cert_sct)) {
|
| - return QUIC_HANDSHAKE_FAILED;
|
| + if (!crypto_proof->chain) {
|
| + if (!proof_source_->GetProof(server_ip, info.sni.as_string(),
|
| + primary_config->serialized, version, chlo_hash,
|
| + &crypto_proof->chain, &crypto_proof->signature,
|
| + &crypto_proof->cert_sct)) {
|
| + helper.Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
|
| + return;
|
| + }
|
| }
|
|
|
| + // Note: this split exists to facilitate a future change in which the async
|
| + // version of GetProof will be called.
|
| + helper.DetachCallback();
|
| + ProcessClientHelloAfterGetProof(
|
| + *validate_chlo_result, reject_only, connection_id, client_address,
|
| + version, supported_versions, use_stateless_rejects,
|
| + server_designated_connection_id, clock, rand, compressed_certs_cache,
|
| + params, crypto_proof, total_framing_overhead, chlo_packet_size,
|
| + requested_config, primary_config, std::move(done_cb));
|
| +}
|
| +
|
| +void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof(
|
| + const ValidateClientHelloResultCallback::Result& validate_chlo_result,
|
| + bool reject_only,
|
| + QuicConnectionId connection_id,
|
| + 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,
|
| + QuicByteCount total_framing_overhead,
|
| + QuicByteCount chlo_packet_size,
|
| + const scoped_refptr<Config>& requested_config,
|
| + const scoped_refptr<Config>& primary_config,
|
| + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const {
|
| + ProcessClientHelloHelper helper(&done_cb);
|
| +
|
| + const CryptoHandshakeMessage& client_hello =
|
| + validate_chlo_result.client_hello;
|
| + const ClientHelloInfo& info = validate_chlo_result.info;
|
| + std::unique_ptr<DiversificationNonce> out_diversification_nonce(
|
| + new DiversificationNonce);
|
| +
|
| StringPiece cert_sct;
|
| if (client_hello.GetStringPiece(kCertificateSCTTag, &cert_sct) &&
|
| cert_sct.empty()) {
|
| params->sct_supported_by_client = true;
|
| }
|
|
|
| + std::unique_ptr<CryptoHandshakeMessage> out(new CryptoHandshakeMessage);
|
| if (!info.reject_reasons.empty() || !requested_config.get()) {
|
| BuildRejection(version, clock->WallNow(), *primary_config, client_hello,
|
| - info, validate_chlo_result->cached_network_params,
|
| + info, validate_chlo_result.cached_network_params,
|
| use_stateless_rejects, server_designated_connection_id, rand,
|
| compressed_certs_cache, params, *crypto_proof,
|
| - total_framing_overhead, chlo_packet_size, out);
|
| + total_framing_overhead, chlo_packet_size, out.get());
|
| if (FLAGS_quic_export_rej_for_all_rejects &&
|
| rejection_observer_ != nullptr) {
|
| - rejection_observer_->OnRejectionBuilt(info.reject_reasons, out);
|
| + rejection_observer_->OnRejectionBuilt(info.reject_reasons, out.get());
|
| }
|
| - return QUIC_NO_ERROR;
|
| + helper.Succeed(std::move(out), std::move(out_diversification_nonce));
|
| + return;
|
| }
|
|
|
| if (reject_only) {
|
| - return QUIC_NO_ERROR;
|
| + helper.Succeed(std::move(out), std::move(out_diversification_nonce));
|
| + return;
|
| }
|
|
|
| const QuicTag* their_aeads;
|
| @@ -665,8 +750,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| 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;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| + "Missing or invalid AEAD or KEXS");
|
| + return;
|
| }
|
|
|
| size_t key_exchange_index;
|
| @@ -677,8 +763,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| num_their_key_exchanges,
|
| QuicUtils::LOCAL_PRIORITY,
|
| ¶ms->key_exchange, &key_exchange_index)) {
|
| - *error_details = "Unsupported AEAD or KEXS";
|
| - return QUIC_CRYPTO_NO_SUPPORT;
|
| + helper.Fail(QUIC_CRYPTO_NO_SUPPORT, "Unsupported AEAD or KEXS");
|
| + return;
|
| }
|
|
|
| if (!requested_config->tb_key_params.empty()) {
|
| @@ -695,23 +781,24 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| break;
|
| }
|
| default:
|
| - *error_details = "Invalid Token Binding key parameter";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| + "Invalid Token Binding key parameter");
|
| + return;
|
| }
|
| }
|
|
|
| StringPiece public_value;
|
| if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
|
| - *error_details = "Missing public value";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Missing public value");
|
| + return;
|
| }
|
|
|
| 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;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Invalid public value");
|
| + return;
|
| }
|
|
|
| if (!info.sni.empty()) {
|
| @@ -732,8 +819,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| 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;
|
| + helper.Fail(QUIC_CRYPTO_INTERNAL_ERROR, "Failed to get certs");
|
| + return;
|
| }
|
| hkdf_suffix.append(crypto_proof->chain->certs.at(0));
|
|
|
| @@ -761,8 +848,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| 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;
|
| + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
|
| + "Symmetric key setup failed");
|
| + return;
|
| }
|
|
|
| char plaintext[kMaxPacketSize];
|
| @@ -772,22 +860,24 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| StringPiece() /* associated data */, cetv_ciphertext, plaintext,
|
| &plaintext_length, kMaxPacketSize);
|
| if (!success) {
|
| - *error_details = "CETV decryption failure";
|
| - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| + "CETV decryption failure");
|
| + return;
|
| }
|
| 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;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "CETV parse error");
|
| + return;
|
| }
|
|
|
| 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;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| + "ChannelID signature failure");
|
| + return;
|
| }
|
|
|
| params->channel_id = key.as_string();
|
| @@ -806,7 +896,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| rand->RandBytes(out_diversification_nonce->data(),
|
| out_diversification_nonce->size());
|
| diversification =
|
| - CryptoUtils::Diversification::Now(out_diversification_nonce);
|
| + CryptoUtils::Diversification::Now(out_diversification_nonce.get());
|
| }
|
|
|
| if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
|
| @@ -814,8 +904,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| Perspective::IS_SERVER, diversification,
|
| ¶ms->initial_crypters,
|
| ¶ms->initial_subkey_secret)) {
|
| - *error_details = "Symmetric key setup failed";
|
| - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
| + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
|
| + "Symmetric key setup failed");
|
| + return;
|
| }
|
|
|
| string forward_secure_public_value;
|
| @@ -831,8 +922,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| 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;
|
| + helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
|
| + "Invalid public value");
|
| + return;
|
| }
|
| }
|
|
|
| @@ -854,8 +946,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| 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;
|
| + helper.Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
|
| + "Symmetric key setup failed");
|
| + return;
|
| }
|
|
|
| out->set_tag(kSHLO);
|
| @@ -873,7 +966,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
| out->SetStringPiece(kCADR, address_coder.Encode());
|
| out->SetStringPiece(kPUBS, forward_secure_public_value);
|
|
|
| - return QUIC_NO_ERROR;
|
| + helper.Succeed(std::move(out), std::move(out_diversification_nonce));
|
| }
|
|
|
| scoped_refptr<QuicCryptoServerConfig::Config>
|
| @@ -1035,7 +1128,7 @@ class QuicCryptoServerConfig::EvaluateClientHelloCallback
|
| config_.EvaluateClientHelloAfterGetProof(
|
| found_error_, server_ip_, version_, primary_orbit_, requested_config_,
|
| primary_config_, crypto_proof_, std::move(details), !ok,
|
| - std::move(client_hello_state_), std::move(done_cb_));
|
| + client_hello_state_, std::move(done_cb_));
|
| }
|
|
|
| private:
|
| @@ -1138,7 +1231,7 @@ void QuicCryptoServerConfig::EvaluateClientHello(
|
| new EvaluateClientHelloCallback(
|
| *this, found_error, server_ip, version, primary_orbit,
|
| requested_config, primary_config, crypto_proof,
|
| - std::move(client_hello_state), std::move(done_cb)));
|
| + client_hello_state, std::move(done_cb)));
|
| proof_source_->GetProof(server_ip, info->sni.as_string(),
|
| serialized_config, version, chlo_hash,
|
| std::move(cb));
|
| @@ -1161,7 +1254,7 @@ void QuicCryptoServerConfig::EvaluateClientHello(
|
| EvaluateClientHelloAfterGetProof(
|
| found_error, server_ip, version, primary_orbit, requested_config,
|
| primary_config, crypto_proof, nullptr /* proof_source_details */,
|
| - get_proof_failed, std::move(client_hello_state), std::move(done_cb));
|
| + get_proof_failed, client_hello_state, std::move(done_cb));
|
| helper.DetachCallback();
|
| }
|
|
|
| @@ -1273,7 +1366,7 @@ void QuicCryptoServerConfig::EvaluateClientHelloAfterGetProof(
|
|
|
| strike_register_client->VerifyNonceIsValidAndUnique(
|
| info->client_nonce, info->now,
|
| - new VerifyNonceIsValidAndUniqueCallback(std::move(client_hello_state),
|
| + new VerifyNonceIsValidAndUniqueCallback(client_hello_state,
|
| std::move(proof_source_details),
|
| std::move(done_cb)));
|
| helper.DetachCallback();
|
|
|