Allow handshaker to override QUIC transport parameters.

Due to randomized serialization of TransportParameters it is impossible for external ProofSource to guess the serialized output and compute the correct signature.
This CL allows ProofSource to pass back its version of transport parameters bitstring and uses it instead, if it's semantically equal.

No immediate prod behavior change: the default value of quic_transport_parameters is std::nullopt and parameters are left as is.

PiperOrigin-RevId: 489557924
diff --git a/quiche/quic/core/crypto/transport_parameters.cc b/quiche/quic/core/crypto/transport_parameters.cc
index 7aa2e05..ebaa070 100644
--- a/quiche/quic/core/crypto/transport_parameters.cc
+++ b/quiche/quic/core/crypto/transport_parameters.cc
@@ -71,6 +71,9 @@
 
 namespace {
 
+constexpr QuicVersionLabel kReservedVersionMask = 0x0f0f0f0f;
+constexpr QuicVersionLabel kReservedVersionBits = 0x0a0a0a0a;
+
 // The following constants define minimum and maximum allowed values for some of
 // the parameters. These come from the "Transport Parameter Definitions"
 // section of draft-ietf-quic-transport.
@@ -1621,4 +1624,32 @@
   return true;
 }
 
+void DegreaseTransportParameters(TransportParameters& parameters) {
+  // Strip GREASE from custom parameters.
+  for (auto it = parameters.custom_parameters.begin();
+       it != parameters.custom_parameters.end();
+       /**/) {
+    // See the "Reserved Transport Parameters" section of RFC 9000.
+    if (it->first % 31 == 27) {
+      parameters.custom_parameters.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+
+  // Strip GREASE from versions.
+  if (parameters.version_information.has_value()) {
+    QuicVersionLabelVector clean_versions;
+    for (QuicVersionLabel version :
+         parameters.version_information->other_versions) {
+      // See the "Versions" section of RFC 9000.
+      if ((version & kReservedVersionMask) != kReservedVersionBits) {
+        clean_versions.push_back(version);
+      }
+    }
+
+    parameters.version_information->other_versions = std::move(clean_versions);
+  }
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/crypto/transport_parameters.h b/quiche/quic/core/crypto/transport_parameters.h
index e62d1ef..78c2202 100644
--- a/quiche/quic/core/crypto/transport_parameters.h
+++ b/quiche/quic/core/crypto/transport_parameters.h
@@ -300,6 +300,12 @@
     const TransportParameters& in, const std::vector<uint8_t>& application_data,
     std::vector<uint8_t>* out);
 
+// Removes reserved values from custom_parameters and versions.
+// The resulting value can be reliably compared with an original or other
+// deserialized value.
+QUIC_EXPORT_PRIVATE void DegreaseTransportParameters(
+    TransportParameters& parameters);
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
diff --git a/quiche/quic/core/crypto/transport_parameters_test.cc b/quiche/quic/core/crypto/transport_parameters_test.cc
index 569a5a0..6b782c4 100644
--- a/quiche/quic/core/crypto/transport_parameters_test.cc
+++ b/quiche/quic/core/crypto/transport_parameters_test.cc
@@ -1040,6 +1040,56 @@
   }
 }
 
+TEST_P(TransportParametersTest, Degrease) {
+  TransportParameters orig_params;
+  orig_params.perspective = Perspective::IS_CLIENT;
+  orig_params.legacy_version_information =
+      CreateFakeLegacyVersionInformationClient();
+  orig_params.version_information = CreateFakeVersionInformation();
+  orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds);
+  orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest);
+  orig_params.initial_max_data.set_value(kFakeInitialMaxData);
+  orig_params.initial_max_stream_data_bidi_local.set_value(
+      kFakeInitialMaxStreamDataBidiLocal);
+  orig_params.initial_max_stream_data_bidi_remote.set_value(
+      kFakeInitialMaxStreamDataBidiRemote);
+  orig_params.initial_max_stream_data_uni.set_value(
+      kFakeInitialMaxStreamDataUni);
+  orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi);
+  orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni);
+  orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest);
+  orig_params.max_ack_delay.set_value(kMaxAckDelayForTest);
+  orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest);
+  orig_params.disable_active_migration = kFakeDisableMigration;
+  orig_params.active_connection_id_limit.set_value(
+      kActiveConnectionIdLimitForTest);
+  orig_params.initial_source_connection_id =
+      CreateFakeInitialSourceConnectionId();
+  orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime);
+  orig_params.google_handshake_message =
+      absl::HexStringToBytes(kFakeGoogleHandshakeMessage);
+  orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
+  orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+  orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+  std::vector<uint8_t> serialized;
+  ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
+
+  TransportParameters new_params;
+  std::string error_details;
+  ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
+                                       serialized.data(), serialized.size(),
+                                       &new_params, &error_details))
+      << error_details;
+  EXPECT_TRUE(error_details.empty());
+
+  // Deserialized parameters have grease added.
+  EXPECT_NE(new_params, orig_params);
+
+  DegreaseTransportParameters(new_params);
+  EXPECT_EQ(new_params, orig_params);
+}
+
 class TransportParametersTicketSerializationTest : public QuicTest {
  protected:
   void SetUp() override {
diff --git a/quiche/quic/core/quic_types.h b/quiche/quic/core/quic_types.h
index 1496625..d2f55b9 100644
--- a/quiche/quic/core/quic_types.h
+++ b/quiche/quic/core/quic_types.h
@@ -838,6 +838,8 @@
   // Client certificate mode for mTLS support. Only used at server side.
   // absl::nullopt means do not change client certificate mode.
   absl::optional<ClientCertMode> client_cert_mode;
+  // QUIC transport parameters as serialized by ProofSourceHandle.
+  absl::optional<std::vector<uint8_t>> quic_transport_parameters;
 };
 
 // ParsedClientHello contains client hello information extracted from a fully
diff --git a/quiche/quic/core/tls_server_handshaker.cc b/quiche/quic/core/tls_server_handshaker.cc
index dbf0808..b035c60 100644
--- a/quiche/quic/core/tls_server_handshaker.cc
+++ b/quiche/quic/core/tls_server_handshaker.cc
@@ -505,30 +505,30 @@
   SetTransportParametersResult result;
   QUICHE_DCHECK(!result.success);
 
-  TransportParameters server_params;
-  server_params.perspective = Perspective::IS_SERVER;
-  server_params.legacy_version_information =
+  server_params_.perspective = Perspective::IS_SERVER;
+  server_params_.legacy_version_information =
       TransportParameters::LegacyVersionInformation();
-  server_params.legacy_version_information.value().supported_versions =
+  server_params_.legacy_version_information.value().supported_versions =
       CreateQuicVersionLabelVector(session()->supported_versions());
-  server_params.legacy_version_information.value().version =
+  server_params_.legacy_version_information.value().version =
       CreateQuicVersionLabel(session()->connection()->version());
-  server_params.version_information = TransportParameters::VersionInformation();
-  server_params.version_information.value().chosen_version =
+  server_params_.version_information =
+      TransportParameters::VersionInformation();
+  server_params_.version_information.value().chosen_version =
       CreateQuicVersionLabel(session()->version());
-  server_params.version_information.value().other_versions =
+  server_params_.version_information.value().other_versions =
       CreateQuicVersionLabelVector(session()->supported_versions());
 
-  if (!handshaker_delegate()->FillTransportParameters(&server_params)) {
+  if (!handshaker_delegate()->FillTransportParameters(&server_params_)) {
     return result;
   }
 
   // Notify QuicConnectionDebugVisitor.
-  session()->connection()->OnTransportParametersSent(server_params);
+  session()->connection()->OnTransportParametersSent(server_params_);
 
   {  // Ensure |server_params_bytes| is not accessed out of the scope.
     std::vector<uint8_t> server_params_bytes;
-    if (!SerializeTransportParameters(server_params, &server_params_bytes) ||
+    if (!SerializeTransportParameters(server_params_, &server_params_bytes) ||
         SSL_set_quic_transport_params(ssl(), server_params_bytes.data(),
                                       server_params_bytes.size()) != 1) {
       return result;
@@ -539,7 +539,7 @@
   if (application_state_) {
     std::vector<uint8_t> early_data_context;
     if (!SerializeTransportParametersForTicket(
-            server_params, *application_state_, &early_data_context)) {
+            server_params_, *application_state_, &early_data_context)) {
       QUIC_BUG(quic_bug_10341_4)
           << "Failed to serialize Transport Parameters for ticket.";
       result.early_data_context = std::vector<uint8_t>();
@@ -554,6 +554,24 @@
   return result;
 }
 
+bool TlsServerHandshaker::TransportParametersMatch(
+    absl::Span<const uint8_t> serialized_params) const {
+  TransportParameters params;
+  std::string error_details;
+
+  bool parse_ok = ParseTransportParameters(
+      session()->version(), Perspective::IS_SERVER, serialized_params.data(),
+      serialized_params.size(), &params, &error_details);
+
+  if (!parse_ok) {
+    return false;
+  }
+
+  DegreaseTransportParameters(params);
+
+  return params == server_params_;
+}
+
 void TlsServerHandshaker::SetWriteSecret(
     EncryptionLevel level, const SSL_CIPHER* cipher,
     absl::Span<const uint8_t> write_secret) {
@@ -968,6 +986,22 @@
   select_cert_status_ = QUIC_FAILURE;
   cert_matched_sni_ = cert_matched_sni;
 
+  if (delayed_ssl_config.quic_transport_parameters.has_value()) {
+    // In case of any error the SSL object is still valid. Handshaker may need
+    // to call ComputeSignature but otherwise can proceed.
+    if (TransportParametersMatch(
+            absl::MakeSpan(*delayed_ssl_config.quic_transport_parameters))) {
+      if (SSL_set_quic_transport_params(
+              ssl(), delayed_ssl_config.quic_transport_parameters->data(),
+              delayed_ssl_config.quic_transport_parameters->size()) != 1) {
+        QUIC_DVLOG(1) << "SSL_set_quic_transport_params override failed";
+      }
+    } else {
+      QUIC_DVLOG(1)
+          << "QUIC transport parameters mismatch with ProofSourceHandle";
+    }
+  }
+
   if (delayed_ssl_config.client_cert_mode.has_value()) {
     tls_connection_.SetClientCertMode(*delayed_ssl_config.client_cert_mode);
     QUIC_DVLOG(1) << "client_cert_mode after cert selection: "
diff --git a/quiche/quic/core/tls_server_handshaker.h b/quiche/quic/core/tls_server_handshaker.h
index f82c022..9863ea7 100644
--- a/quiche/quic/core/tls_server_handshaker.h
+++ b/quiche/quic/core/tls_server_handshaker.h
@@ -303,6 +303,10 @@
   SetTransportParametersResult SetTransportParameters();
   bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello,
                                   std::string* error_details);
+  // Compares |serialized_params| with |server_params_|.
+  // Returns true if handshaker serialization is equivalent.
+  bool TransportParametersMatch(
+      absl::Span<const uint8_t> serialized_params) const;
 
   struct QUIC_NO_EXPORT SetApplicationSettingsResult {
     bool success = false;
@@ -374,6 +378,7 @@
       last_received_cached_network_params_;
 
   bool cert_matched_sni_ = false;
+  TransportParameters server_params_;
 };
 
 }  // namespace quic
diff --git a/quiche/quic/core/tls_server_handshaker_test.cc b/quiche/quic/core/tls_server_handshaker_test.cc
index b210c6e..fe3f982 100644
--- a/quiche/quic/core/tls_server_handshaker_test.cc
+++ b/quiche/quic/core/tls_server_handshaker_test.cc
@@ -945,6 +945,81 @@
   EXPECT_TRUE(server_handshaker_->received_client_cert());
 }
 
+TEST_P(TlsServerHandshakerTest,
+       SetInvalidServerTransportParamsByDelayedSslConfig) {
+  ASSERT_TRUE(SetupClientCert());
+  InitializeFakeClient();
+
+  QuicDelayedSSLConfig delayed_ssl_config;
+  delayed_ssl_config.quic_transport_parameters = {1, 2, 3};
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      delayed_ssl_config);
+
+  AdvanceHandshakeWithFakeClient();
+  ASSERT_TRUE(
+      server_handshaker_->fake_proof_source_handle()->HasPendingOperation());
+  server_handshaker_->fake_proof_source_handle()->CompletePendingOperation();
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_FALSE(server_handshaker_->fake_proof_source_handle()
+                   ->all_compute_signature_args()
+                   .empty());
+}
+
+TEST_P(TlsServerHandshakerTest,
+       SetValidServerTransportParamsByDelayedSslConfig) {
+  ParsedQuicVersion version = GetParam().version;
+
+  TransportParameters server_params;
+  std::string error_details;
+  server_params.perspective = quic::Perspective::IS_SERVER;
+  server_params.legacy_version_information =
+      TransportParameters::LegacyVersionInformation();
+  server_params.legacy_version_information.value().supported_versions =
+      quic::CreateQuicVersionLabelVector(
+          quic::ParsedQuicVersionVector{version});
+  server_params.legacy_version_information.value().version =
+      quic::CreateQuicVersionLabel(version);
+  server_params.version_information = TransportParameters::VersionInformation();
+  server_params.version_information.value().chosen_version =
+      quic::CreateQuicVersionLabel(version);
+  server_params.version_information.value().other_versions =
+      quic::CreateQuicVersionLabelVector(
+          quic::ParsedQuicVersionVector{version});
+
+  ASSERT_TRUE(server_params.AreValid(&error_details)) << error_details;
+
+  std::vector<uint8_t> server_params_bytes;
+  ASSERT_TRUE(
+      SerializeTransportParameters(server_params, &server_params_bytes));
+
+  ASSERT_TRUE(SetupClientCert());
+  InitializeFakeClient();
+
+  QuicDelayedSSLConfig delayed_ssl_config;
+  delayed_ssl_config.quic_transport_parameters = server_params_bytes;
+  InitializeServerWithFakeProofSourceHandle();
+  server_handshaker_->SetupProofSourceHandle(
+      /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC,
+      /*compute_signature_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC,
+      delayed_ssl_config);
+
+  AdvanceHandshakeWithFakeClient();
+  ASSERT_TRUE(
+      server_handshaker_->fake_proof_source_handle()->HasPendingOperation());
+  server_handshaker_->fake_proof_source_handle()->CompletePendingOperation();
+
+  CompleteCryptoHandshake();
+  ExpectHandshakeSuccessful();
+  EXPECT_FALSE(server_handshaker_->fake_proof_source_handle()
+                   ->all_compute_signature_args()
+                   .empty());
+}
+
 TEST_P(TlsServerHandshakerTest, RequestClientCertByDelayedSslConfig) {
   ASSERT_TRUE(SetupClientCert());
   InitializeFakeClient();