Add QUIC+TLS key_update_not_yet_supported transport parameter

This will allow us to detect whether our own implementation supports IETF QUIC Key Update or not, which will let us safely deploy it.

Add new QUIC transport parameter, protected by gfe2_reloadable_flag_quic_send_key_update_not_yet_supported

PiperOrigin-RevId: 326322311
Change-Id: Ib5b55ae904850e601cd4321906e5859c288d51b3
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc
index d2a0edf..60f6c26 100644
--- a/quic/core/crypto/transport_parameters.cc
+++ b/quic/core/crypto/transport_parameters.cc
@@ -57,11 +57,12 @@
   kGoogleConnectionOptions = 0x3128,
   kGoogleUserAgentId = 0x3129,
   kGoogleSupportHandshakeDone = 0x312A,  // Only used in T050.
-  kGoogleQuicParam = 18257,  // Used for non-standard Google-specific params.
+  kGoogleKeyUpdateNotYetSupported = 0x312B,
+  kGoogleQuicParam = 0x4751,  // Used for non-standard Google-specific params.
   kGoogleQuicVersion =
-      18258,  // Used to transmit version and supported_versions.
+      0x4752,  // Used to transmit version and supported_versions.
 
-  kMinAckDelay = 0xde1a  // An IETF QUIC extension.
+  kMinAckDelay = 0xDE1A  // draft-iyengar-quic-delayed-ack.
 };
 
 namespace {
@@ -125,6 +126,8 @@
       return "user_agent_id";
     case TransportParameters::kGoogleSupportHandshakeDone:
       return "support_handshake_done";
+    case TransportParameters::kGoogleKeyUpdateNotYetSupported:
+      return "key_update_not_yet_supported";
     case TransportParameters::kGoogleQuicParam:
       return "google";
     case TransportParameters::kGoogleQuicVersion:
@@ -160,6 +163,7 @@
     case TransportParameters::kGoogleConnectionOptions:
     case TransportParameters::kGoogleUserAgentId:
     case TransportParameters::kGoogleSupportHandshakeDone:
+    case TransportParameters::kGoogleKeyUpdateNotYetSupported:
     case TransportParameters::kGoogleQuicParam:
     case TransportParameters::kGoogleQuicVersion:
     case TransportParameters::kMinAckDelay:
@@ -478,6 +482,9 @@
   if (support_handshake_done) {
     rv += " " + TransportParameterIdToString(kGoogleSupportHandshakeDone);
   }
+  if (key_update_not_yet_supported) {
+    rv += " " + TransportParameterIdToString(kGoogleKeyUpdateNotYetSupported);
+  }
   if (google_quic_params) {
     rv += " " + TransportParameterIdToString(kGoogleQuicParam);
   }
@@ -530,7 +537,8 @@
                                  kVarInt62MaxValue),
       max_datagram_frame_size(kMaxDatagramFrameSize),
       initial_round_trip_time_us(kInitialRoundTripTime),
-      support_handshake_done(false)
+      support_handshake_done(false),
+      key_update_not_yet_supported(false)
 // Important note: any new transport parameters must be added
 // to TransportParameters::AreValid, SerializeTransportParameters and
 // ParseTransportParameters, TransportParameters's custom copy constructor, the
@@ -566,6 +574,7 @@
       google_connection_options(other.google_connection_options),
       user_agent_id(other.user_agent_id),
       support_handshake_done(other.support_handshake_done),
+      key_update_not_yet_supported(other.key_update_not_yet_supported),
       custom_parameters(other.custom_parameters) {
   if (other.preferred_address) {
     preferred_address = std::make_unique<TransportParameters::PreferredAddress>(
@@ -611,6 +620,7 @@
         google_connection_options == rhs.google_connection_options &&
         user_agent_id == rhs.user_agent_id &&
         support_handshake_done == rhs.support_handshake_done &&
+        key_update_not_yet_supported == rhs.key_update_not_yet_supported &&
         custom_parameters == rhs.custom_parameters)) {
     return false;
   }
@@ -772,6 +782,7 @@
       kTypeAndValueLength +               // google_connection_options
       kTypeAndValueLength +               // user_agent_id
       kTypeAndValueLength +               // support_handshake_done
+      kTypeAndValueLength +               // key_update_not_yet_supported
       kTypeAndValueLength +               // google
       kTypeAndValueLength +               // google-version
       kGreaseParameterLength;             // GREASE
@@ -1005,6 +1016,17 @@
     }
   }
 
+  // Google-specific indicator for key update not yet supported.
+  if (in.key_update_not_yet_supported) {
+    if (!WriteTransportParameterId(
+            &writer, TransportParameters::kGoogleKeyUpdateNotYetSupported,
+            version) ||
+        !WriteTransportParameterLength(&writer, /*length=*/0, version)) {
+      QUIC_BUG << "Failed to write key_update_not_yet_supported for " << in;
+      return false;
+    }
+  }
+
   // Google-specific non-standard parameter.
   if (in.google_quic_params) {
     const QuicData& serialized_google_quic_params =
@@ -1373,6 +1395,13 @@
         }
         out->support_handshake_done = true;
         break;
+      case TransportParameters::kGoogleKeyUpdateNotYetSupported:
+        if (out->key_update_not_yet_supported) {
+          *error_details = "Received a second key_update_not_yet_supported";
+          return false;
+        }
+        out->key_update_not_yet_supported = true;
+        break;
       case TransportParameters::kGoogleQuicParam: {
         if (out->google_quic_params) {
           *error_details = "Received a second Google parameter";
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h
index 8c3b565..8034dd1 100644
--- a/quic/core/crypto/transport_parameters.h
+++ b/quic/core/crypto/transport_parameters.h
@@ -209,6 +209,10 @@
   // Google-specific handshake done support. This is only used for T050.
   bool support_handshake_done;
 
+  // Google-specific mechanism to indicate that IETF QUIC Key Update has not
+  // yet been implemented. This will be removed once we implement it.
+  bool key_update_not_yet_supported;
+
   // Transport parameters used by Google QUIC but not IETF QUIC. This is
   // serialized into a TransportParameter struct with a TransportParameterId of
   // kGoogleQuicParamId.
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc
index 84b50f3..a87ff1b 100644
--- a/quic/core/crypto/transport_parameters_test.cc
+++ b/quic/core/crypto/transport_parameters_test.cc
@@ -38,6 +38,7 @@
     0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
     0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F};
 const bool kFakeSupportHandshakeDone = true;
+const bool kFakeKeyUpdateNotYetSupported = true;
 
 const auto kCustomParameter1 =
     static_cast<TransportParameters::TransportParameterId>(0xffcd);
@@ -271,6 +272,7 @@
   orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
   orig_params.user_agent_id = CreateFakeUserAgentId();
   orig_params.support_handshake_done = kFakeSupportHandshakeDone;
+  orig_params.key_update_not_yet_supported = kFakeKeyUpdateNotYetSupported;
   orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
   orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
 
@@ -305,6 +307,7 @@
   orig_params.google_connection_options = CreateFakeGoogleConnectionOptions();
   orig_params.user_agent_id = CreateFakeUserAgentId();
   orig_params.support_handshake_done = kFakeSupportHandshakeDone;
+  orig_params.key_update_not_yet_supported = kFakeKeyUpdateNotYetSupported;
   orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value;
   orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value;
 
@@ -480,7 +483,7 @@
 TEST_P(TransportParametersTest, ParseClientParams) {
   // clang-format off
   const uint8_t kClientParamsOld[] = {
-      0x00, 0x80,              // length of the parameters array that follows
+      0x00, 0x84,              // length of the parameters array that follows
       // max_idle_timeout
       0x00, 0x01,  // parameter id
       0x00, 0x02,  // length
@@ -553,6 +556,9 @@
       // support_handshake_done
       0x31, 0x2A,  // parameter id
       0x00, 0x00,  // value
+      // key_update_not_yet_supported
+      0x31, 0x2B,  // parameter id
+      0x00, 0x00,  // value
       // Google version extension
       0x47, 0x52,  // parameter id
       0x00, 0x04,  // length
@@ -631,6 +637,9 @@
       // support_handshake_done
       0x71, 0x2A,  // parameter id
       0x00,  // length
+      // key_update_not_yet_supported
+      0x71, 0x2B,  // parameter id
+      0x00,  // length
       // Google version extension
       0x80, 0x00, 0x47, 0x52,  // parameter id
       0x04,  // length
@@ -688,6 +697,7 @@
   ASSERT_TRUE(new_params.user_agent_id.has_value());
   EXPECT_EQ(CreateFakeUserAgentId(), new_params.user_agent_id.value());
   EXPECT_TRUE(new_params.support_handshake_done);
+  EXPECT_TRUE(new_params.key_update_not_yet_supported);
 }
 
 TEST_P(TransportParametersTest,
@@ -855,7 +865,7 @@
 TEST_P(TransportParametersTest, ParseServerParams) {
   // clang-format off
   const uint8_t kServerParamsOld[] = {
-      0x00, 0xd9,  // length of parameters array that follows
+      0x00, 0xdd,  // length of parameters array that follows
       // original_destination_connection_id
       0x00, 0x00,  // parameter id
       0x00, 0x08,  // length
@@ -945,6 +955,9 @@
       // support_handshake_done
       0x31, 0x2A,  // parameter id
       0x00, 0x00,  // value
+      // key_update_not_yet_supported
+      0x31, 0x2B,  // parameter id
+      0x00, 0x00,  // value
       // Google version extension
       0x47, 0x52,  // parameter id
       0x00, 0x0d,  // length
@@ -1043,6 +1056,9 @@
       // support_handshake_done
       0x71, 0x2A,  // parameter id
       0x00,  // length
+      // key_update_not_yet_supported
+      0x71, 0x2B,  // parameter id
+      0x00,  // length
       // Google version extension
       0x80, 0x00, 0x47, 0x52,  // parameter id
       0x0d,  // length
@@ -1116,6 +1132,7 @@
             new_params.google_connection_options.value());
   EXPECT_FALSE(new_params.user_agent_id.has_value());
   EXPECT_TRUE(new_params.support_handshake_done);
+  EXPECT_TRUE(new_params.key_update_not_yet_supported);
 }
 
 TEST_P(TransportParametersTest, ParseServerParametersRepeated) {
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index a43a1ef..0fd26a9 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -1239,6 +1239,11 @@
     QUIC_RESTART_FLAG_COUNT_N(quic_google_transport_param_omit_old, 1, 3);
   }
 
+  if (GetQuicReloadableFlag(quic_send_key_update_not_yet_supported)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_send_key_update_not_yet_supported);
+    params->key_update_not_yet_supported = true;
+  }
+
   params->custom_parameters = custom_transport_parameters_to_send_;
 
   return true;
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc
index 6c1d829..9a4d951 100644
--- a/quic/core/quic_config_test.cc
+++ b/quic/core/quic_config_test.cc
@@ -429,7 +429,7 @@
             config_.IdleNetworkTimeout());
 }
 
-TEST_P(QuicConfigTest, RecievedInvalidMinAckDelayInTransportParameter) {
+TEST_P(QuicConfigTest, ReceivedInvalidMinAckDelayInTransportParameter) {
   if (!version_.UsesTls()) {
     // TransportParameters are only used for QUIC+TLS.
     return;
@@ -505,6 +505,8 @@
   EXPECT_EQ(
       static_cast<uint64_t>(kDefaultMinAckDelayTimeMs) * kNumMicrosPerMilli,
       params.min_ack_delay_us.value());
+  EXPECT_EQ(params.key_update_not_yet_supported,
+            GetQuicReloadableFlag(quic_send_key_update_not_yet_supported));
 }
 
 TEST_P(QuicConfigTest, ProcessTransportParametersServer) {