Randomize the order of sent transport parameters

As per RFC 9000, the order in which transport parameters are sent carries no semantics. To avoid ossification or fingerprinting, this CL randomizes the order they are sent in.

Protected by FLAGS_quic_reloadable_flag_quic_randomize_transport_parameter_order.

PiperOrigin-RevId: 390717555
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index 1d7ecc4..f1f01e6 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -740,6 +740,16 @@
     parameter_ids.push_back(kv.first);
   }
 
+  if (GetQuicReloadableFlag(quic_randomize_transport_parameter_order)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_randomize_transport_parameter_order);
+    // Randomize order of sent transport parameters by walking the array
+    // backwards and swapping each element with a random earlier one.
+    for (size_t i = parameter_ids.size() - 1; i > 0; i--) {
+      std::swap(parameter_ids[i],
+                parameter_ids[random->InsecureRandUint64() % (i + 1)]);
+    }
+  }
+
   out->resize(max_transport_param_length);
   QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data()));
 
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc
index 81707c5..ffeb4aa 100644
--- a/quic/core/crypto/transport_parameters_test.cc
+++ b/quic/core/crypto/transport_parameters_test.cc
@@ -932,6 +932,53 @@
   EXPECT_EQ(new_params, orig_params);
 }
 
+TEST_P(TransportParametersTest, SerializationOrderIsRandom) {
+  SetQuicReloadableFlag(quic_randomize_transport_parameter_order, true);
+  TransportParameters orig_params;
+  orig_params.perspective = Perspective::IS_CLIENT;
+  orig_params.version = kFakeVersionLabel;
+  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_connection_options = CreateFakeGoogleConnectionOptions();
+  orig_params.user_agent_id = CreateFakeUserAgentId();
+  orig_params.key_update_not_yet_supported = kFakeKeyUpdateNotYetSupported;
+  orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
+  orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
+
+  std::vector<uint8_t> first_serialized;
+  ASSERT_TRUE(
+      SerializeTransportParameters(version_, orig_params, &first_serialized));
+  // Test that a subsequent serialization is different from the first.
+  // Run in a loop to avoid a failure in the unlikely event that randomization
+  // produces the same result multiple times.
+  for (int i = 0; i < 1000; i++) {
+    std::vector<uint8_t> serialized;
+    ASSERT_TRUE(
+        SerializeTransportParameters(version_, orig_params, &serialized));
+    if (serialized != first_serialized) {
+      return;
+    }
+  }
+}
+
 class TransportParametersTicketSerializationTest : public QuicTest {
  protected:
   void SetUp() override {
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index c44f213..1af71b6 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -125,6 +125,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false)
 // When true, prevents QUIC\'s PacingSender from generating bursts when the congestion controller is CWND limited and not pacing limited.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_pacing_sender_bursts, false)
+// When true, randomizes the order of sent transport parameters in QUIC+TLS.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_randomize_transport_parameter_order, false)
 // When true, set the initial congestion control window from connection options in QuicSentPacketManager rather than TcpCubicSenderBytes.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)