Extract Quiche default connection ID generation algorithms into a library. When integrated into quiche, similar functions can be removed.

PiperOrigin-RevId: 465353115
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 43d246d..7c1b37c 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -186,6 +186,7 @@
     "quic/core/crypto/tls_server_connection.h",
     "quic/core/crypto/transport_parameters.h",
     "quic/core/crypto/web_transport_fingerprint_proof_verifier.h",
+    "quic/core/deterministic_connection_id_generator.h",
     "quic/core/frames/quic_ack_frame.h",
     "quic/core/frames/quic_ack_frequency_frame.h",
     "quic/core/frames/quic_blocked_frame.h",
@@ -527,6 +528,7 @@
     "quic/core/crypto/tls_server_connection.cc",
     "quic/core/crypto/transport_parameters.cc",
     "quic/core/crypto/web_transport_fingerprint_proof_verifier.cc",
+    "quic/core/deterministic_connection_id_generator.cc",
     "quic/core/frames/quic_ack_frame.cc",
     "quic/core/frames/quic_ack_frequency_frame.cc",
     "quic/core/frames/quic_blocked_frame.cc",
@@ -1153,6 +1155,7 @@
     "quic/core/crypto/quic_random_test.cc",
     "quic/core/crypto/transport_parameters_test.cc",
     "quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc",
+    "quic/core/deterministic_connection_id_generator_test.cc",
     "quic/core/frames/quic_frames_test.cc",
     "quic/core/http/capsule_test.cc",
     "quic/core/http/http_decoder_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index ba93bf9..76219c9 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -186,6 +186,7 @@
     "src/quiche/quic/core/crypto/tls_server_connection.h",
     "src/quiche/quic/core/crypto/transport_parameters.h",
     "src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h",
+    "src/quiche/quic/core/deterministic_connection_id_generator.h",
     "src/quiche/quic/core/frames/quic_ack_frame.h",
     "src/quiche/quic/core/frames/quic_ack_frequency_frame.h",
     "src/quiche/quic/core/frames/quic_blocked_frame.h",
@@ -527,6 +528,7 @@
     "src/quiche/quic/core/crypto/tls_server_connection.cc",
     "src/quiche/quic/core/crypto/transport_parameters.cc",
     "src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc",
+    "src/quiche/quic/core/deterministic_connection_id_generator.cc",
     "src/quiche/quic/core/frames/quic_ack_frame.cc",
     "src/quiche/quic/core/frames/quic_ack_frequency_frame.cc",
     "src/quiche/quic/core/frames/quic_blocked_frame.cc",
@@ -1153,6 +1155,7 @@
     "src/quiche/quic/core/crypto/quic_random_test.cc",
     "src/quiche/quic/core/crypto/transport_parameters_test.cc",
     "src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc",
+    "src/quiche/quic/core/deterministic_connection_id_generator_test.cc",
     "src/quiche/quic/core/frames/quic_frames_test.cc",
     "src/quiche/quic/core/http/capsule_test.cc",
     "src/quiche/quic/core/http/http_decoder_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index e3b983c..8e32230 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -185,6 +185,7 @@
     "quiche/quic/core/crypto/tls_server_connection.h",
     "quiche/quic/core/crypto/transport_parameters.h",
     "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.h",
+    "quiche/quic/core/deterministic_connection_id_generator.h",
     "quiche/quic/core/frames/quic_ack_frame.h",
     "quiche/quic/core/frames/quic_ack_frequency_frame.h",
     "quiche/quic/core/frames/quic_blocked_frame.h",
@@ -526,6 +527,7 @@
     "quiche/quic/core/crypto/tls_server_connection.cc",
     "quiche/quic/core/crypto/transport_parameters.cc",
     "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc",
+    "quiche/quic/core/deterministic_connection_id_generator.cc",
     "quiche/quic/core/frames/quic_ack_frame.cc",
     "quiche/quic/core/frames/quic_ack_frequency_frame.cc",
     "quiche/quic/core/frames/quic_blocked_frame.cc",
@@ -1152,6 +1154,7 @@
     "quiche/quic/core/crypto/quic_random_test.cc",
     "quiche/quic/core/crypto/transport_parameters_test.cc",
     "quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier_test.cc",
+    "quiche/quic/core/deterministic_connection_id_generator_test.cc",
     "quiche/quic/core/frames/quic_frames_test.cc",
     "quiche/quic/core/http/capsule_test.cc",
     "quiche/quic/core/http/http_decoder_test.cc",
diff --git a/quiche/quic/core/deterministic_connection_id_generator.cc b/quiche/quic/core/deterministic_connection_id_generator.cc
new file mode 100644
index 0000000..fd86dc7
--- /dev/null
+++ b/quiche/quic/core/deterministic_connection_id_generator.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/deterministic_connection_id_generator.h"
+
+#include "quiche/quic/core/quic_utils.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+DeterministicConnectionIdGenerator::DeterministicConnectionIdGenerator(
+    uint8_t expected_connection_id_length)
+    : expected_connection_id_length_(expected_connection_id_length) {
+  if (expected_connection_id_length_ >
+      kQuicMaxConnectionIdWithLengthPrefixLength) {
+    QUIC_BUG(quic_bug_465151159_01)
+        << "Issuing connection IDs longer than allowed in RFC9000";
+  }
+}
+
+absl::optional<QuicConnectionId>
+DeterministicConnectionIdGenerator::GenerateNextConnectionId(
+    const QuicConnectionId& original) {
+  if (expected_connection_id_length_ == 0) {
+    return EmptyQuicConnectionId();
+  }
+  const uint64_t connection_id_hash64 = QuicUtils::FNV1a_64_Hash(
+      absl::string_view(original.data(), original.length()));
+  if (expected_connection_id_length_ <= sizeof(uint64_t)) {
+    return QuicConnectionId(
+        reinterpret_cast<const char*>(&connection_id_hash64),
+        expected_connection_id_length_);
+  }
+  char new_connection_id_data[255] = {};
+  const absl::uint128 connection_id_hash128 = QuicUtils::FNV1a_128_Hash(
+      absl::string_view(original.data(), original.length()));
+  static_assert(sizeof(connection_id_hash64) + sizeof(connection_id_hash128) <=
+                    sizeof(new_connection_id_data),
+                "bad size");
+  memcpy(new_connection_id_data, &connection_id_hash64,
+         sizeof(connection_id_hash64));
+  // TODO(martinduke): We don't have any test coverage of the line below. In
+  // particular, if the memcpy somehow misses a byte, a test could check if one
+  // byte position in generated connection IDs is always the same.
+  memcpy(new_connection_id_data + sizeof(connection_id_hash64),
+         &connection_id_hash128, sizeof(connection_id_hash128));
+  return QuicConnectionId(new_connection_id_data,
+                          expected_connection_id_length_);
+}
+
+absl::optional<QuicConnectionId>
+DeterministicConnectionIdGenerator::MaybeReplaceConnectionId(
+    const QuicConnectionId& original, const ParsedQuicVersion& version) {
+  if (original.length() == expected_connection_id_length_) {
+    return absl::optional<QuicConnectionId>();
+  }
+  QUICHE_DCHECK(version.AllowsVariableLengthConnectionIds());
+  absl::optional<QuicConnectionId> new_connection_id =
+      GenerateNextConnectionId(original);
+  // Verify that ReplaceShortServerConnectionId is deterministic.
+  QUICHE_DCHECK(new_connection_id.has_value());
+  QUICHE_DCHECK_EQ(
+      *new_connection_id,
+      static_cast<QuicConnectionId>(*GenerateNextConnectionId(original)));
+  QUICHE_DCHECK_EQ(expected_connection_id_length_, new_connection_id->length());
+  QUIC_DLOG(INFO) << "Replacing incoming connection ID " << original << " with "
+                  << new_connection_id.value();
+  return new_connection_id;
+}
+
+}  // namespace quic
diff --git a/quiche/quic/core/deterministic_connection_id_generator.h b/quiche/quic/core/deterministic_connection_id_generator.h
new file mode 100644
index 0000000..cd27811
--- /dev/null
+++ b/quiche/quic/core/deterministic_connection_id_generator.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A Connection ID generator that generates deterministic connection IDs for
+// QUIC servers.
+
+#ifndef QUICHE_QUIC_CORE_CONNECTION_ID_GENERATOR_DETERMINISTIC_H_
+#define QUICHE_QUIC_CORE_CONNECTION_ID_GENERATOR_DETERMINISTIC_H_
+
+#include "quiche/quic/core/connection_id_generator.h"
+
+namespace quic {
+
+// Generates connection IDs deterministically from the provided original
+// connection ID.
+class QUIC_EXPORT_PRIVATE DeterministicConnectionIdGenerator
+    : public ConnectionIdGeneratorInterface {
+ public:
+  DeterministicConnectionIdGenerator(uint8_t expected_connection_id_length);
+
+  // Hashes |original| to create a new connection ID.
+  absl::optional<QuicConnectionId> GenerateNextConnectionId(
+      const QuicConnectionId& original) override;
+  // Replace the connection ID if and only if |original| is not of the expected
+  // length.
+  absl::optional<QuicConnectionId> MaybeReplaceConnectionId(
+      const QuicConnectionId& original,
+      const ParsedQuicVersion& version) override;
+
+ private:
+  const uint8_t expected_connection_id_length_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE__CONNECTION_ID_GENERATOR_DETERMINISTIC_H_
diff --git a/quiche/quic/core/deterministic_connection_id_generator_test.cc b/quiche/quic/core/deterministic_connection_id_generator_test.cc
new file mode 100644
index 0000000..67016b5
--- /dev/null
+++ b/quiche/quic/core/deterministic_connection_id_generator_test.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/deterministic_connection_id_generator.h"
+
+#include <optional>
+
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+struct TestParams {
+  TestParams(int connection_id_length)
+      : connection_id_length_(connection_id_length) {}
+  TestParams() : TestParams(kQuicDefaultConnectionIdLength) {}
+
+  friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+    os << "{ connection ID length: " << p.connection_id_length_ << " }";
+    return os;
+  }
+
+  int connection_id_length_;
+};
+
+// Constructs various test permutations.
+std::vector<struct TestParams> GetTestParams() {
+  std::vector<struct TestParams> params;
+  std::vector<int> connection_id_lengths{7, 8, 9, 16, 20};
+  for (int connection_id_length : connection_id_lengths) {
+    params.push_back(TestParams(connection_id_length));
+  }
+  return params;
+}
+
+class DeterministicConnectionIdGeneratorTest
+    : public QuicTestWithParam<TestParams> {
+ public:
+  DeterministicConnectionIdGeneratorTest()
+      : connection_id_length_(GetParam().connection_id_length_),
+        generator_(DeterministicConnectionIdGenerator(connection_id_length_)),
+        version_(ParsedQuicVersion::RFCv1()) {}
+
+ protected:
+  int connection_id_length_;
+  DeterministicConnectionIdGenerator generator_;
+  ParsedQuicVersion version_;
+};
+
+INSTANTIATE_TEST_SUITE_P(DeterministicConnectionIdGeneratorTests,
+                         DeterministicConnectionIdGeneratorTest,
+                         ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(DeterministicConnectionIdGeneratorTest,
+       NextConnectionIdIsDeterministic) {
+  // Verify that two equal connection IDs get the same replacement.
+  QuicConnectionId connection_id64a = TestConnectionId(33);
+  QuicConnectionId connection_id64b = TestConnectionId(33);
+  EXPECT_EQ(connection_id64a, connection_id64b);
+  EXPECT_EQ(*generator_.GenerateNextConnectionId(connection_id64a),
+            *generator_.GenerateNextConnectionId(connection_id64b));
+  QuicConnectionId connection_id72a = TestConnectionIdNineBytesLong(42);
+  QuicConnectionId connection_id72b = TestConnectionIdNineBytesLong(42);
+  EXPECT_EQ(connection_id72a, connection_id72b);
+  EXPECT_EQ(*generator_.GenerateNextConnectionId(connection_id72a),
+            *generator_.GenerateNextConnectionId(connection_id72b));
+}
+
+TEST_P(DeterministicConnectionIdGeneratorTest,
+       NextConnectionIdLengthIsCorrect) {
+  // Verify that all generated IDs are of the correct length.
+  const char connection_id_bytes[255] = {};
+  for (uint8_t i = 0; i < sizeof(connection_id_bytes) - 1; ++i) {
+    QuicConnectionId connection_id(connection_id_bytes, i);
+    absl::optional<QuicConnectionId> replacement_connection_id =
+        generator_.GenerateNextConnectionId(connection_id);
+    ASSERT_TRUE(replacement_connection_id.has_value());
+    EXPECT_EQ(connection_id_length_, replacement_connection_id->length());
+  }
+}
+
+TEST_P(DeterministicConnectionIdGeneratorTest, NextConnectionIdHasEntropy) {
+  // Make sure all these test connection IDs have different replacements.
+  for (uint64_t i = 0; i < 256; ++i) {
+    QuicConnectionId connection_id_i = TestConnectionId(i);
+    absl::optional<QuicConnectionId> new_i =
+        generator_.GenerateNextConnectionId(connection_id_i);
+    ASSERT_TRUE(new_i.has_value());
+    EXPECT_NE(connection_id_i, *new_i);
+    for (uint64_t j = i + 1; j <= 256; ++j) {
+      QuicConnectionId connection_id_j = TestConnectionId(j);
+      EXPECT_NE(connection_id_i, connection_id_j);
+      absl::optional<QuicConnectionId> new_j =
+          generator_.GenerateNextConnectionId(connection_id_j);
+      ASSERT_TRUE(new_j.has_value());
+      EXPECT_NE(*new_i, *new_j);
+    }
+  }
+}
+
+TEST_P(DeterministicConnectionIdGeneratorTest,
+       OnlyReplaceConnectionIdWithWrongLength) {
+  const char connection_id_input[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                      0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+                                      0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14};
+  for (int i = 0; i < kQuicMaxConnectionIdWithLengthPrefixLength; i++) {
+    QuicConnectionId input = QuicConnectionId(connection_id_input, i);
+    absl::optional<QuicConnectionId> output =
+        generator_.MaybeReplaceConnectionId(input, version_);
+    if (i == connection_id_length_) {
+      EXPECT_FALSE(output.has_value());
+    } else {
+      ASSERT_TRUE(output.has_value());
+      EXPECT_EQ(*output, generator_.GenerateNextConnectionId(input));
+    }
+  }
+}
+
+}  // namespace test
+}  // namespace quic