Introduce QUIC client connection option BPTE to randomize TLS extensions

This allows use to ensure that servers and middleboxes do not make assumptions on the ordering of extensions which would prevent us from deploying new extensions as it has sometimes happened in the past.

PiperOrigin-RevId: 384700317
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 6a4f162..c48aaa8 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -341,6 +341,8 @@
 const QuicTag kNSLC = TAG('N', 'S', 'L', 'C');  // Always send connection close
                                                 // for idle timeout.
 const QuicTag kCHSP = TAG('C', 'H', 'S', 'P');  // Chaos protection.
+const QuicTag kBPTE = TAG('B', 'P', 'T', 'E');  // BoringSSL Permutes
+                                                // TLS Extensions.
 
 // Proof types (i.e. certificate types)
 // NOTE: although it would be silly to do so, specifying both kX509 and kX59R
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 15aa2cb..df52e0f 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -5697,6 +5697,18 @@
   SendSynchronousFooRequestAndCheckResponse();
 }
 
+TEST_P(EndToEndTest, PermuteTlsExtensions) {
+  if (!version_.UsesTls()) {
+    ASSERT_TRUE(Initialize());
+    return;
+  }
+  // Enable TLS extension permutation and perform an HTTP request.
+  client_config_.SetClientConnectionOptions(QuicTagVector{kBPTE});
+  ASSERT_TRUE(Initialize());
+  EXPECT_TRUE(GetClientSession()->permutes_tls_extensions());
+  SendSynchronousFooRequestAndCheckResponse();
+}
+
 TEST_P(EndToEndTest, KeyUpdateInitiatedByClient) {
   if (!version_.UsesTls()) {
     // Key Update is only supported in TLS handshake.
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index c473ced..b9dd7e7 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -134,11 +134,15 @@
   connection_->SetDataProducer(this);
   connection_->SetUnackedMapInitialCapacity();
   connection_->SetFromConfig(config_);
-  if (perspective_ == Perspective::IS_CLIENT &&
-      config_.HasClientRequestedIndependentOption(kAFFE, perspective_) &&
-      version().HasIetfQuicFrames()) {
-    connection_->set_can_receive_ack_frequency_frame();
-    config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs);
+  if (perspective_ == Perspective::IS_CLIENT) {
+    if (config_.HasClientRequestedIndependentOption(kAFFE, perspective_) &&
+        version().HasIetfQuicFrames()) {
+      connection_->set_can_receive_ack_frequency_frame();
+      config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs);
+    }
+    if (config_.HasClientRequestedIndependentOption(kBPTE, perspective_)) {
+      permutes_tls_extensions_ = true;
+    }
   }
 
   connection_->CreateConnectionIdManager();
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 89c74db..3fb03b9 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -620,6 +620,8 @@
            use_write_or_buffer_data_at_level_;
   }
 
+  bool permutes_tls_extensions() const { return permutes_tls_extensions_; }
+
   virtual QuicSSLConfig GetSSLConfig() const { return QuicSSLConfig(); }
 
  protected:
@@ -951,6 +953,9 @@
   // creation of new outgoing bidirectional streams.
   bool liveness_testing_in_progress_;
 
+  // Whether BoringSSL randomizes the order of TLS extensions.
+  bool permutes_tls_extensions_ = false;
+
   const bool use_write_or_buffer_data_at_level_ =
       GetQuicReloadableFlag(quic_use_write_or_buffer_data_at_level);
 };
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 23a7955..932737a 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -68,6 +68,16 @@
   }
   SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);
 
+  // TODO(b/193650832) Add SetFromConfig to QUIC handshakers and remove reliance
+  // on session pointer.
+  if (session()->permutes_tls_extensions()) {
+    // Ask BoringSSL to randomize the order of TLS extensions.
+#if BORINGSSL_API_VERSION >= 16
+    QUIC_DLOG(INFO) << "Enabling TLS extension permutation";
+    SSL_set_permute_extensions(ssl(), true);
+#endif  // BORINGSSL_API_VERSION
+  }
+
   // Set the SNI to send, if any.
   SSL_set_connect_state(ssl());
   if (QUIC_DLOG_INFO_IS_ON() &&