Fully support draft-28 transport parameters

This CL adopts the new transport parameters from
QuicConnection and QuicSession.

Add draft-28 transport parameters, protected by draft-28 flag

PiperOrigin-RevId: 313860932
Change-Id: I1384f3457c254802fec33b24fb62cf555a772051
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 1e36258..ba22c81 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -91,6 +91,17 @@
         kInitialStreamFlowControlWindowForTest);
     session()->config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
+    if (session()->version().AuthenticatesHandshakeConnectionIds()) {
+      if (session()->perspective() == Perspective::IS_CLIENT) {
+        session()->config()->SetOriginalConnectionIdToSend(
+            session()->connection()->connection_id());
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->connection_id());
+      } else {
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->client_connection_id());
+      }
+    }
     if (session()->connection()->version().handshake_protocol ==
         PROTOCOL_TLS1_3) {
       TransportParameters transport_parameters;
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 41c204f..7ab9aa0 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -78,6 +78,17 @@
         kInitialStreamFlowControlWindowForTest);
     session()->config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
+    if (session()->version().AuthenticatesHandshakeConnectionIds()) {
+      if (session()->perspective() == Perspective::IS_CLIENT) {
+        session()->config()->SetOriginalConnectionIdToSend(
+            session()->connection()->connection_id());
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->connection_id());
+      } else {
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->client_connection_id());
+      }
+    }
     if (session()->connection()->version().handshake_protocol ==
         PROTOCOL_TLS1_3) {
       TransportParameters transport_parameters;
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 1ed056d..17b3fee 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -402,6 +402,141 @@
   buffered_packets_.clear();
 }
 
+bool QuicConnection::ValidateConfigConnectionIdsOld(const QuicConfig& config) {
+  // This function validates connection IDs as defined in IETF draft-27 and
+  // earlier.
+  DCHECK(config.negotiated());
+  DCHECK(!version().AuthenticatesHandshakeConnectionIds());
+  if (original_destination_connection_id_.has_value() &&
+      retry_source_connection_id_.has_value()) {
+    DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+    // We received a RETRY packet, validate that the original destination
+    // connection ID from the config matches the one from the RETRY.
+    if (!config.HasReceivedOriginalConnectionId() ||
+        config.ReceivedOriginalConnectionId() !=
+            original_destination_connection_id_.value()) {
+      std::string received_value;
+      if (config.HasReceivedOriginalConnectionId()) {
+        received_value = config.ReceivedOriginalConnectionId().ToString();
+      } else {
+        received_value = "none";
+      }
+      std::string error_details = quiche::QuicheStrCat(
+          "Bad original_connection_id: expected ",
+          original_destination_connection_id_.value().ToString(), ", received ",
+          received_value, ", RETRY used ", server_connection_id_.ToString());
+      CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+      return false;
+    }
+  } else {
+    // We did not receive a RETRY packet, make sure we did not receive the
+    // original_destination_connection_id transport parameter.
+    if (config.HasReceivedOriginalConnectionId()) {
+      std::string error_details = quiche::QuicheStrCat(
+          "Bad original_connection_id: did not receive RETRY but received ",
+          config.ReceivedOriginalConnectionId().ToString());
+      CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) {
+  DCHECK(config.negotiated());
+  if (!version().UsesTls()) {
+    // QUIC+TLS is required to transmit connection ID transport parameters.
+    return true;
+  }
+  if (!version().AuthenticatesHandshakeConnectionIds()) {
+    return ValidateConfigConnectionIdsOld(config);
+  }
+  // This function validates connection IDs as defined in IETF draft-28 and
+  // later.
+
+  // Validate initial_source_connection_id.
+  QuicConnectionId expected_initial_source_connection_id;
+  if (perspective_ == Perspective::IS_CLIENT) {
+    expected_initial_source_connection_id = server_connection_id_;
+  } else {
+    expected_initial_source_connection_id = client_connection_id_;
+  }
+  if (!config.HasReceivedInitialSourceConnectionId() ||
+      config.ReceivedInitialSourceConnectionId() !=
+          expected_initial_source_connection_id) {
+    std::string received_value;
+    if (config.HasReceivedInitialSourceConnectionId()) {
+      received_value = config.ReceivedInitialSourceConnectionId().ToString();
+    } else {
+      received_value = "none";
+    }
+    std::string error_details =
+        quiche::QuicheStrCat("Bad initial_source_connection_id: expected ",
+                             expected_initial_source_connection_id.ToString(),
+                             ", received ", received_value);
+    CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                    ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return false;
+  }
+  if (perspective_ == Perspective::IS_CLIENT) {
+    // Validate original_destination_connection_id.
+    if (!config.HasReceivedOriginalConnectionId() ||
+        config.ReceivedOriginalConnectionId() !=
+            GetOriginalDestinationConnectionId()) {
+      std::string received_value;
+      if (config.HasReceivedOriginalConnectionId()) {
+        received_value = config.ReceivedOriginalConnectionId().ToString();
+      } else {
+        received_value = "none";
+      }
+      std::string error_details = quiche::QuicheStrCat(
+          "Bad original_destination_connection_id: expected ",
+          GetOriginalDestinationConnectionId().ToString(), ", received ",
+          received_value);
+      CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+      return false;
+    }
+    // Validate retry_source_connection_id.
+    if (retry_source_connection_id_.has_value()) {
+      // We received a RETRY packet, validate that the retry source
+      // connection ID from the config matches the one from the RETRY.
+      if (!config.HasReceivedRetrySourceConnectionId() ||
+          config.ReceivedRetrySourceConnectionId() !=
+              retry_source_connection_id_.value()) {
+        std::string received_value;
+        if (config.HasReceivedRetrySourceConnectionId()) {
+          received_value = config.ReceivedRetrySourceConnectionId().ToString();
+        } else {
+          received_value = "none";
+        }
+        std::string error_details =
+            quiche::QuicheStrCat("Bad retry_source_connection_id: expected ",
+                                 retry_source_connection_id_.value().ToString(),
+                                 ", received ", received_value);
+        CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        return false;
+      }
+    } else {
+      // We did not receive a RETRY packet, make sure we did not receive the
+      // retry_source_connection_id transport parameter.
+      if (config.HasReceivedRetrySourceConnectionId()) {
+        std::string error_details = quiche::QuicheStrCat(
+            "Bad retry_source_connection_id: did not receive RETRY but "
+            "received ",
+            config.ReceivedRetrySourceConnectionId().ToString());
+        CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
+                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 void QuicConnection::SetFromConfig(const QuicConfig& config) {
   if (config.negotiated()) {
     // Handshake complete, set handshake timeout to Infinite.
@@ -409,38 +544,8 @@
                        config.IdleNetworkTimeout());
     idle_timeout_connection_close_behavior_ =
         ConnectionCloseBehavior::SILENT_CLOSE;
-    if (original_connection_id_.has_value()) {
-      DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
-      // We received a RETRY packet, validate that the |original_connection_id|
-      // from the config matches the one from the RETRY.
-      if (!config.HasReceivedOriginalConnectionId() ||
-          config.ReceivedOriginalConnectionId() !=
-              original_connection_id_.value()) {
-        std::string received_value;
-        if (config.HasReceivedOriginalConnectionId()) {
-          received_value = config.ReceivedOriginalConnectionId().ToString();
-        } else {
-          received_value = "none";
-        }
-        std::string error_details = quiche::QuicheStrCat(
-            "Bad original_connection_id: expected ",
-            original_connection_id_.value().ToString(), ", received ",
-            received_value, ", RETRY used ", server_connection_id_.ToString());
-        CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
-                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-        return;
-      }
-    } else {
-      // We did not receive a RETRY packet, make sure we did not receive the
-      // original_connection_id transport parameter.
-      if (config.HasReceivedOriginalConnectionId()) {
-        std::string error_details = quiche::QuicheStrCat(
-            "Bad original_connection_id: did not receive RETRY but received ",
-            config.ReceivedOriginalConnectionId().ToString());
-        CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
-                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-        return;
-      }
+    if (!ValidateConfigConnectionIds(config)) {
+      return;
     }
   } else {
     SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
@@ -719,8 +824,12 @@
                   << server_connection_id_ << " with " << new_connection_id
                   << ", received token "
                   << quiche::QuicheTextUtils::HexEncode(retry_token);
-  DCHECK(!original_connection_id_.has_value());
-  original_connection_id_ = server_connection_id_;
+  if (!original_destination_connection_id_.has_value()) {
+    original_destination_connection_id_ = server_connection_id_;
+  }
+  DCHECK(!retry_source_connection_id_.has_value())
+      << retry_source_connection_id_.value();
+  retry_source_connection_id_ = new_connection_id;
   server_connection_id_ = new_connection_id;
   packet_creator_.SetServerConnectionId(server_connection_id_);
   packet_creator_.SetRetryToken(retry_token);
@@ -741,11 +850,25 @@
 
 void QuicConnection::SetOriginalDestinationConnectionId(
     const QuicConnectionId& original_destination_connection_id) {
+  QUIC_DLOG(INFO) << "Setting original_destination_connection_id to "
+                  << original_destination_connection_id
+                  << " on connection with server_connection_id "
+                  << server_connection_id_;
   DCHECK_NE(original_destination_connection_id, server_connection_id_);
   if (!HasIncomingConnectionId(original_destination_connection_id)) {
     incoming_connection_ids_.push_back(original_destination_connection_id);
   }
   InstallInitialCrypters(original_destination_connection_id);
+  DCHECK(!original_destination_connection_id_.has_value())
+      << original_destination_connection_id_.value();
+  original_destination_connection_id_ = original_destination_connection_id;
+}
+
+QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() {
+  if (original_destination_connection_id_.has_value()) {
+    return original_destination_connection_id_.value();
+  }
+  return server_connection_id_;
 }
 
 bool QuicConnection::OnUnauthenticatedPublicHeader(
@@ -2068,6 +2191,9 @@
     QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID "
                     << server_connection_id_ << " with "
                     << header.source_connection_id;
+    if (!original_destination_connection_id_.has_value()) {
+      original_destination_connection_id_ = server_connection_id_;
+    }
     server_connection_id_ = header.source_connection_id;
     packet_creator_.SetServerConnectionId(server_connection_id_);
   }
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 589568a..6782e06 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -952,6 +952,9 @@
   void SetOriginalDestinationConnectionId(
       const QuicConnectionId& original_destination_connection_id);
 
+  // Returns the original destination connection ID used for this connection.
+  QuicConnectionId GetOriginalDestinationConnectionId();
+
   // Called when ACK alarm goes off. Sends ACKs of those packet number spaces
   // which have expired ACK timeout. Only used when this connection supports
   // multiple packet number spaces.
@@ -1303,6 +1306,11 @@
   QuicTime::Delta GetHandshakeTimeout() const;
   QuicTime GetTimeOfLastReceivedPacket() const;
 
+  // Validate connection IDs used during the handshake. Closes the connection
+  // on validation failure.
+  bool ValidateConfigConnectionIds(const QuicConfig& config);
+  bool ValidateConfigConnectionIdsOld(const QuicConfig& config);
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
@@ -1619,10 +1627,15 @@
   // vector to improve performance since it is expected to be very small.
   std::vector<QuicConnectionId> incoming_connection_ids_;
 
-  // When we receive a RETRY packet, we replace |server_connection_id_| with the
-  // value from the RETRY packet and save off the original value of
-  // |server_connection_id_| into |original_connection_id_| for validation.
-  quiche::QuicheOptional<QuicConnectionId> original_connection_id_;
+  // When we receive a RETRY packet or some INITIAL packets, we replace
+  // |server_connection_id_| with the value from that packet and save off the
+  // original value of |server_connection_id_| into
+  // |original_destination_connection_id_| for validation.
+  quiche::QuicheOptional<QuicConnectionId> original_destination_connection_id_;
+
+  // After we receive a RETRY packet, |retry_source_connection_id_| contains
+  // the source connection ID from that packet.
+  quiche::QuicheOptional<QuicConnectionId> retry_source_connection_id_;
 
   // Indicates whether received RETRY packets should be dropped.
   bool drop_incoming_retry_packets_;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index ff29c26..582bb54 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1774,8 +1774,10 @@
   }
 
   void TestClientRetryHandling(bool invalid_retry_tag,
-                               bool missing_id_in_config,
-                               bool wrong_id_in_config);
+                               bool missing_original_id_in_config,
+                               bool wrong_original_id_in_config,
+                               bool missing_retry_id_in_config,
+                               bool wrong_retry_id_in_config);
 
   QuicConnectionId connection_id_;
   QuicFramer framer_;
@@ -4309,6 +4311,12 @@
   options.push_back(kTLPR);
   config.SetConnectionOptionsToSend(options);
   QuicConfigPeer::SetNegotiated(&config, true);
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   connection_.SetFromConfig(config);
   connection_.SetMaxTailLossProbes(1);
 
@@ -5791,6 +5799,12 @@
       config.ProcessPeerHello(msg, CLIENT, &error_details);
   EXPECT_THAT(error, IsQuicNoError());
 
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   connection_.SetFromConfig(config);
 
   const QuicTime::Delta default_idle_timeout =
@@ -5937,6 +5951,12 @@
       config.ProcessPeerHello(msg, CLIENT, &error_details);
   EXPECT_THAT(error, IsQuicNoError());
 
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   connection_.SetFromConfig(config);
 
   const QuicTime::Delta default_idle_timeout =
@@ -6101,6 +6121,12 @@
   connection_options.push_back(k5RTO);
   config.SetConnectionOptionsToSend(connection_options);
   QuicConfigPeer::SetNegotiated(&config, true);
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   connection_.SetFromConfig(config);
 
   // Send stream data.
@@ -9901,6 +9927,12 @@
   connection_options.push_back(k6PTO);
   config.SetConnectionOptionsToSend(connection_options);
   QuicConfigPeer::SetNegotiated(&config, true);
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -9942,6 +9974,12 @@
   connection_options.push_back(k7PTO);
   config.SetConnectionOptionsToSend(connection_options);
   QuicConfigPeer::SetNegotiated(&config, true);
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
@@ -9981,6 +10019,12 @@
   connection_options.push_back(k2PTO);
   connection_options.push_back(k8PTO);
   QuicConfigPeer::SetNegotiated(&config, true);
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedOriginalConnectionId(
+        &config, connection_.connection_id());
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &config, connection_.connection_id());
+  }
   config.SetConnectionOptionsToSend(connection_options);
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
@@ -10385,14 +10429,20 @@
   EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
 }
 
-void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag,
-                                                 bool missing_id_in_config,
-                                                 bool wrong_id_in_config) {
+void QuicConnectionTest::TestClientRetryHandling(
+    bool invalid_retry_tag,
+    bool missing_original_id_in_config,
+    bool wrong_original_id_in_config,
+    bool missing_retry_id_in_config,
+    bool wrong_retry_id_in_config) {
   if (invalid_retry_tag) {
-    ASSERT_FALSE(missing_id_in_config);
-    ASSERT_FALSE(wrong_id_in_config);
+    ASSERT_FALSE(missing_original_id_in_config);
+    ASSERT_FALSE(wrong_original_id_in_config);
+    ASSERT_FALSE(missing_retry_id_in_config);
+    ASSERT_FALSE(wrong_retry_id_in_config);
   } else {
-    ASSERT_FALSE(missing_id_in_config && wrong_id_in_config);
+    ASSERT_FALSE(missing_original_id_in_config && wrong_original_id_in_config);
+    ASSERT_FALSE(missing_retry_id_in_config && wrong_retry_id_in_config);
   }
   if (!version().HasRetryIntegrityTag()) {
     return;
@@ -10454,11 +10504,17 @@
   }
 
   QuicConnectionId config_original_connection_id = original_connection_id;
-  if (wrong_id_in_config) {
+  if (wrong_original_id_in_config) {
     // Flip the first bit of the connection ID.
     ASSERT_FALSE(config_original_connection_id.IsEmpty());
     config_original_connection_id.mutable_data()[0] ^= 0x80;
   }
+  QuicConnectionId config_retry_source_connection_id = new_connection_id;
+  if (wrong_retry_id_in_config) {
+    // Flip the first bit of the connection ID.
+    ASSERT_FALSE(config_retry_source_connection_id.IsEmpty());
+    config_retry_source_connection_id.mutable_data()[0] ^= 0x80;
+  }
 
   // Make sure the connection uses the connection ID from the test vectors,
   QuicConnectionPeer::SetServerConnectionId(&connection_,
@@ -10491,11 +10547,21 @@
   // Test validating the original_connection_id from the config.
   QuicConfig received_config;
   QuicConfigPeer::SetNegotiated(&received_config, true);
-  if (!missing_id_in_config) {
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    QuicConfigPeer::SetReceivedInitialSourceConnectionId(
+        &received_config, connection_.connection_id());
+    if (!missing_retry_id_in_config) {
+      QuicConfigPeer::SetReceivedRetrySourceConnectionId(
+          &received_config, config_retry_source_connection_id);
+    }
+  }
+  if (!missing_original_id_in_config) {
     QuicConfigPeer::SetReceivedOriginalConnectionId(
         &received_config, config_original_connection_id);
   }
-  if (missing_id_in_config || wrong_id_in_config) {
+
+  if (missing_original_id_in_config || wrong_original_id_in_config ||
+      missing_retry_id_in_config || wrong_retry_id_in_config) {
     EXPECT_CALL(visitor_,
                 OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
         .Times(1);
@@ -10506,8 +10572,9 @@
   }
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
   connection_.SetFromConfig(received_config);
-  if (missing_id_in_config || wrong_id_in_config) {
-    EXPECT_FALSE(connection_.connected());
+  if (missing_original_id_in_config || wrong_original_id_in_config ||
+      missing_retry_id_in_config || wrong_retry_id_in_config) {
+    ASSERT_FALSE(connection_.connected());
     TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION);
   } else {
     EXPECT_TRUE(connection_.connected());
@@ -10516,31 +10583,74 @@
 
 TEST_P(QuicConnectionTest, ClientParsesRetry) {
   TestClientRetryHandling(/*invalid_retry_tag=*/false,
-                          /*missing_id_in_config=*/false,
-                          /*wrong_id_in_config=*/false);
+                          /*missing_original_id_in_config=*/false,
+                          /*wrong_original_id_in_config=*/false,
+                          /*missing_retry_id_in_config=*/false,
+                          /*wrong_retry_id_in_config=*/false);
 }
 
-TEST_P(QuicConnectionTest, ClientParsesInvalidRetry) {
+TEST_P(QuicConnectionTest, ClientParsesRetryInvalidTag) {
   TestClientRetryHandling(/*invalid_retry_tag=*/true,
-                          /*missing_id_in_config=*/false,
-                          /*wrong_id_in_config=*/false);
+                          /*missing_original_id_in_config=*/false,
+                          /*wrong_original_id_in_config=*/false,
+                          /*missing_retry_id_in_config=*/false,
+                          /*wrong_retry_id_in_config=*/false);
 }
 
-TEST_P(QuicConnectionTest, ClientParsesRetryMissingId) {
+TEST_P(QuicConnectionTest, ClientParsesRetryMissingOriginalId) {
   TestClientRetryHandling(/*invalid_retry_tag=*/false,
-                          /*missing_id_in_config=*/true,
-                          /*wrong_id_in_config=*/false);
+                          /*missing_original_id_in_config=*/true,
+                          /*wrong_original_id_in_config=*/false,
+                          /*missing_retry_id_in_config=*/false,
+                          /*wrong_retry_id_in_config=*/false);
 }
 
-TEST_P(QuicConnectionTest, ClientParsesRetryWrongId) {
+TEST_P(QuicConnectionTest, ClientParsesRetryWrongOriginalId) {
   TestClientRetryHandling(/*invalid_retry_tag=*/false,
-                          /*missing_id_in_config=*/false,
-                          /*wrong_id_in_config=*/true);
+                          /*missing_original_id_in_config=*/false,
+                          /*wrong_original_id_in_config=*/true,
+                          /*missing_retry_id_in_config=*/false,
+                          /*wrong_retry_id_in_config=*/false);
+}
+
+TEST_P(QuicConnectionTest, ClientParsesRetryMissingRetryId) {
+  if (!connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    // Versions that do not authenticate connection IDs never send the
+    // retry_source_connection_id transport parameter.
+    return;
+  }
+  TestClientRetryHandling(/*invalid_retry_tag=*/false,
+                          /*missing_original_id_in_config=*/false,
+                          /*wrong_original_id_in_config=*/false,
+                          /*missing_retry_id_in_config=*/true,
+                          /*wrong_retry_id_in_config=*/false);
+}
+
+TEST_P(QuicConnectionTest, ClientParsesRetryWrongRetryId) {
+  if (!connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    // Versions that do not authenticate connection IDs never send the
+    // retry_source_connection_id transport parameter.
+    return;
+  }
+  TestClientRetryHandling(/*invalid_retry_tag=*/false,
+                          /*missing_original_id_in_config=*/false,
+                          /*wrong_original_id_in_config=*/false,
+                          /*missing_retry_id_in_config=*/false,
+                          /*wrong_retry_id_in_config=*/true);
 }
 
 TEST_P(QuicConnectionTest, ClientReceivesOriginalConnectionIdWithoutRetry) {
-  // Make sure that receiving the original_connection_id transport parameter
-  // fails the handshake when no RETRY packet was received before it.
+  if (!connection_.version().UsesTls()) {
+    // QUIC+TLS is required to transmit connection ID transport parameters.
+    return;
+  }
+  if (connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    // Versions that authenticate connection IDs always send the
+    // original_destination_connection_id transport parameter.
+    return;
+  }
+  // Make sure that receiving the original_destination_connection_id transport
+  // parameter fails the handshake when no RETRY packet was received before it.
   QuicConfig received_config;
   QuicConfigPeer::SetNegotiated(&received_config, true);
   QuicConfigPeer::SetReceivedOriginalConnectionId(&received_config,
@@ -10553,6 +10663,26 @@
   TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION);
 }
 
+TEST_P(QuicConnectionTest, ClientReceivesRetrySourceConnectionIdWithoutRetry) {
+  if (!connection_.version().AuthenticatesHandshakeConnectionIds()) {
+    // Versions that do not authenticate connection IDs never send the
+    // retry_source_connection_id transport parameter.
+    return;
+  }
+  // Make sure that receiving the retry_source_connection_id transport parameter
+  // fails the handshake when no RETRY packet was received before it.
+  QuicConfig received_config;
+  QuicConfigPeer::SetNegotiated(&received_config, true);
+  QuicConfigPeer::SetReceivedRetrySourceConnectionId(&received_config,
+                                                     TestConnectionId(0x12345));
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .Times(1);
+  connection_.SetFromConfig(received_config);
+  EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION);
+}
+
 // Regression test for http://crbug/1047977
 TEST_P(QuicConnectionTest, MaxStreamsFrameCausesConnectionClose) {
   if (!VersionHasIetfQuicFrames(connection_.transport_version())) {
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 76869a3..a1f20c6 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1623,6 +1623,16 @@
 }
 
 bool QuicSession::FillTransportParameters(TransportParameters* params) {
+  if (version().AuthenticatesHandshakeConnectionIds()) {
+    if (perspective() == Perspective::IS_SERVER) {
+      config_.SetOriginalConnectionIdToSend(
+          connection_->GetOriginalDestinationConnectionId());
+      config_.SetInitialSourceConnectionIdToSend(connection_->connection_id());
+    } else {
+      config_.SetInitialSourceConnectionIdToSend(
+          connection_->client_connection_id());
+    }
+  }
   return config_.FillTransportParameters(params);
 }
 
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index ddebac2..d57180b 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -77,6 +77,17 @@
         kInitialStreamFlowControlWindowForTest);
     session()->config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
+    if (session()->version().AuthenticatesHandshakeConnectionIds()) {
+      if (session()->perspective() == Perspective::IS_CLIENT) {
+        session()->config()->SetOriginalConnectionIdToSend(
+            session()->connection()->connection_id());
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->connection_id());
+      } else {
+        session()->config()->SetInitialSourceConnectionIdToSend(
+            session()->connection()->client_connection_id());
+      }
+    }
     if (session()->connection()->version().handshake_protocol ==
         PROTOCOL_TLS1_3) {
       TransportParameters transport_parameters;