OHTTP key config: add support for more crypto algorithms

PiperOrigin-RevId: 945229893
diff --git a/quiche/oblivious_http/common/oblivious_http_header_key_config.cc b/quiche/oblivious_http/common/oblivious_http_header_key_config.cc
index 4091ba2..e1a6218 100644
--- a/quiche/oblivious_http/common/oblivious_http_header_key_config.cc
+++ b/quiche/oblivious_http/common/oblivious_http_header_key_config.cc
@@ -40,8 +40,16 @@
 
 absl::StatusOr<const EVP_HPKE_KEM*> CheckKemId(uint16_t kem_id) {
   switch (kem_id) {
+    case EVP_HPKE_DHKEM_P256_HKDF_SHA256:
+      return EVP_hpke_p256_hkdf_sha256();
     case EVP_HPKE_DHKEM_X25519_HKDF_SHA256:
       return EVP_hpke_x25519_hkdf_sha256();
+    case EVP_HPKE_XWING:
+      return EVP_hpke_xwing();
+    case EVP_HPKE_MLKEM768:
+      return EVP_hpke_mlkem768();
+    case EVP_HPKE_MLKEM1024:
+      return EVP_hpke_mlkem1024();
     default:
       return absl::UnimplementedError(
           absl::StrCat("KEM ID", absl::Hex(kem_id), " not supported"));
@@ -52,6 +60,8 @@
   switch (kdf_id) {
     case EVP_HPKE_HKDF_SHA256:
       return EVP_hpke_hkdf_sha256();
+    case EVP_HPKE_HKDF_SHA384:
+      return EVP_hpke_hkdf_sha384();
     default:
       return absl::UnimplementedError(
           absl::StrCat("KDF ID ", absl::Hex(kdf_id), " not supported"));
@@ -466,10 +476,16 @@
 
 std::string ObliviousHttpKemIdToString(uint16_t kem_id) {
   switch (kem_id) {
-    case EVP_HPKE_DHKEM_X25519_HKDF_SHA256:
-      return "X25519-SHA256";
     case EVP_HPKE_DHKEM_P256_HKDF_SHA256:
       return "P256-SHA256";
+    case EVP_HPKE_DHKEM_X25519_HKDF_SHA256:
+      return "X25519-SHA256";
+    case EVP_HPKE_XWING:
+      return "XWING";
+    case EVP_HPKE_MLKEM768:
+      return "MLKEM768";
+    case EVP_HPKE_MLKEM1024:
+      return "MLKEM1024";
     default:
       return absl::StrCat("UnknownKEM(", kem_id, ")");
   }
@@ -479,6 +495,8 @@
   switch (kdf_id) {
     case EVP_HPKE_HKDF_SHA256:
       return "SHA256";
+    case EVP_HPKE_HKDF_SHA384:
+      return "SHA384";
     default:
       return absl::StrCat("UnknownKDF(", kdf_id, ")");
   }
diff --git a/quiche/oblivious_http/common/oblivious_http_header_key_config_test.cc b/quiche/oblivious_http/common/oblivious_http_header_key_config_test.cc
index 40b076d..9d0c63d 100644
--- a/quiche/oblivious_http/common/oblivious_http_header_key_config_test.cc
+++ b/quiche/oblivious_http/common/oblivious_http_header_key_config_test.cc
@@ -109,6 +109,91 @@
           .ok());
 }
 
+struct KemTestCase {
+  uint16_t kem_id;
+  absl::string_view expected_name;
+};
+
+TEST(ObliviousHttpHeaderKeyConfig, TestKemAlgorithms) {
+  const KemTestCase kTestCases[] = {
+      {EVP_HPKE_DHKEM_P256_HKDF_SHA256, "P256-SHA256"},
+      {EVP_HPKE_DHKEM_X25519_HKDF_SHA256, "X25519-SHA256"},
+      {EVP_HPKE_XWING, "XWING"},
+      {EVP_HPKE_MLKEM768, "MLKEM768"},
+      {EVP_HPKE_MLKEM1024, "MLKEM1024"},
+  };
+  for (const KemTestCase& test_case : kTestCases) {
+    auto config = ObliviousHttpHeaderKeyConfig::Create(
+        1, test_case.kem_id, EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
+    QUICHE_ASSERT_OK(config) << test_case.expected_name;
+    EXPECT_THAT(config->DebugString(), HasSubstr(test_case.expected_name));
+    EXPECT_NE(config->GetHpkeKem(), nullptr) << test_case.expected_name;
+    EXPECT_EQ(config->GetHpkeKemId(), test_case.kem_id)
+        << test_case.expected_name;
+
+    std::string serialized_hdr = config->SerializeOhttpPayloadHeader();
+    EXPECT_EQ(serialized_hdr.size(),
+              ObliviousHttpHeaderKeyConfig::kHeaderLength)
+        << test_case.expected_name;
+    QUICHE_EXPECT_OK(config->ParseOhttpPayloadHeader(serialized_hdr))
+        << test_case.expected_name;
+  }
+}
+
+struct KdfTestCase {
+  uint16_t kdf_id;
+  absl::string_view expected_name;
+};
+
+TEST(ObliviousHttpHeaderKeyConfig, TestKdfAlgorithms) {
+  const KdfTestCase kTestCases[] = {
+      {EVP_HPKE_HKDF_SHA256, "SHA256"},
+      {EVP_HPKE_HKDF_SHA384, "SHA384"},
+  };
+  for (const KdfTestCase& test_case : kTestCases) {
+    auto config = ObliviousHttpHeaderKeyConfig::Create(
+        1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, test_case.kdf_id,
+        EVP_HPKE_AES_256_GCM);
+    QUICHE_ASSERT_OK(config) << test_case.expected_name;
+    EXPECT_THAT(config->DebugString(), HasSubstr(test_case.expected_name));
+    EXPECT_NE(config->GetHpkeKdf(), nullptr) << test_case.expected_name;
+    EXPECT_EQ(config->GetHpkeKdfId(), test_case.kdf_id)
+        << test_case.expected_name;
+
+    std::string serialized_hdr = config->SerializeOhttpPayloadHeader();
+    EXPECT_EQ(serialized_hdr.size(),
+              ObliviousHttpHeaderKeyConfig::kHeaderLength)
+        << test_case.expected_name;
+    QUICHE_EXPECT_OK(config->ParseOhttpPayloadHeader(serialized_hdr))
+        << test_case.expected_name;
+  }
+}
+
+TEST(ObliviousHttpHeaderKeyConfig, KemIdToString) {
+  EXPECT_EQ(ObliviousHttpKemIdToString(EVP_HPKE_DHKEM_P256_HKDF_SHA256),
+            "P256-SHA256");
+  EXPECT_EQ(ObliviousHttpKemIdToString(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
+            "X25519-SHA256");
+  EXPECT_EQ(ObliviousHttpKemIdToString(EVP_HPKE_XWING), "XWING");
+  EXPECT_EQ(ObliviousHttpKemIdToString(EVP_HPKE_MLKEM768), "MLKEM768");
+  EXPECT_EQ(ObliviousHttpKemIdToString(EVP_HPKE_MLKEM1024), "MLKEM1024");
+  EXPECT_EQ(ObliviousHttpKemIdToString(0xffff), "UnknownKEM(65535)");
+}
+
+TEST(ObliviousHttpHeaderKeyConfig, KdfIdToString) {
+  EXPECT_EQ(ObliviousHttpKdfIdToString(EVP_HPKE_HKDF_SHA256), "SHA256");
+  EXPECT_EQ(ObliviousHttpKdfIdToString(EVP_HPKE_HKDF_SHA384), "SHA384");
+  EXPECT_EQ(ObliviousHttpKdfIdToString(0xffff), "UnknownKDF(65535)");
+}
+
+TEST(ObliviousHttpHeaderKeyConfig, AeadIdToString) {
+  EXPECT_EQ(ObliviousHttpAeadIdToString(EVP_HPKE_AES_128_GCM), "AES-128-GCM");
+  EXPECT_EQ(ObliviousHttpAeadIdToString(EVP_HPKE_AES_256_GCM), "AES-256-GCM");
+  EXPECT_EQ(ObliviousHttpAeadIdToString(EVP_HPKE_CHACHA20_POLY1305),
+            "CHACHA20-POLY1305");
+  EXPECT_EQ(ObliviousHttpAeadIdToString(0xffff), "UnknownAEAD(65535)");
+}
+
 TEST(ObliviousHttpHeaderKeyConfig, TestParsingValidHeader) {
   auto instance = ObliviousHttpHeaderKeyConfig::Create(
       5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
@@ -251,6 +336,105 @@
   EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
 }
 
+TEST(ObliviousHttpKeyConfigs,
+     SomeUnsupportedSymmetricAlgorithmsWithLengthPrefix) {
+  std::string key;
+  ASSERT_TRUE(
+      absl::HexStringToBytes("002d"                              // length
+                             "4b"                                // key_id
+                             "0020"                              // kem_id
+                             "606162636465666768696a6b6c6d6e6f"  // public_key
+                             "707172737475767778797a7b7c7d7e7f"  // public_key
+                             "0008"       // len(symmetric_algorithms)
+                             "0001BEEF"   // HKDF_SHA256, Unsupported
+                             "00010002",  // HKDF_SHA256, AES_256_GCM
+                             &key));
+  auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key);
+  QUICHE_ASSERT_OK(configs);
+  EXPECT_THAT(*configs, Property(&ObliviousHttpKeyConfigs::NumKeys, 1));
+  EXPECT_THAT(
+      configs->PreferredConfig(),
+      AllOf(HasKeyId(0x4b), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
+            HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM)));
+  std::string expected_public_key;
+  ASSERT_TRUE(absl::HexStringToBytes(
+      "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f",
+      &expected_public_key));
+  EXPECT_THAT(configs->GetPublicKeyForId(configs->PreferredConfig().GetKeyId()),
+              IsOkAndHolds(expected_public_key));
+}
+
+TEST(ObliviousHttpKeyConfigs, NoSupportedSymmetricAlgorithmsWithLengthPrefix) {
+  std::string key;
+  ASSERT_TRUE(
+      absl::HexStringToBytes("002d"                              // length
+                             "4b"                                // key_id
+                             "0020"                              // kem_id
+                             "606162636465666768696a6b6c6d6e6f"  // public_key
+                             "707172737475767778797a7b7c7d7e7f"  // public_key
+                             "0008"       // len(symmetric_algorithms)
+                             "0001DEAD"   // HKDF_SHA256, Unsupported
+                             "0001BEEF",  // HKDF_SHA256, Unsupported
+                             &key));
+  EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
+}
+
+TEST(ObliviousHttpKeyConfigs, SomeUnsupportedKemWithLengthPrefix) {
+  std::string key;
+  ASSERT_TRUE(absl::HexStringToBytes(
+      // First key config with unknown/unsupported KEM (0x9999).
+      "0029"                              // length of this key config
+      "4b"                                // key_id
+      "9999"                              // kem_id (unsupported)
+      "606162636465666768696a6b6c6d6e6f"  // public_key
+      "707172737475767778797a7b7c7d7e7f"  // public_key
+      "0004"                              // len(symmetric_algorithms)
+      "00010002"                          // HKDF_SHA256, AES_256_GCM
+      // Second key config with supported KEM (X25519).
+      "0029"                              // length of this key config
+      "4f"                                // key_id
+      "0020"                              // kem_id
+      "606162636465666768696a6b6c6d6e6f"  // public_key
+      "707172737475767778797a7b7c7d7e7f"  // public_key
+      "0004"                              // len(symmetric_algorithms)
+      "00010001",                         // HKDF_SHA256, AES_128_GCM
+      &key));
+  auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key);
+  QUICHE_ASSERT_OK(configs);
+  EXPECT_THAT(*configs, Property(&ObliviousHttpKeyConfigs::NumKeys, 1));
+  EXPECT_THAT(
+      configs->PreferredConfig(),
+      AllOf(HasKeyId(0x4f), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
+            HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM)));
+}
+
+TEST(ObliviousHttpKeyConfigs, NoSupportedKemWithLengthPrefix) {
+  std::string key;
+  ASSERT_TRUE(
+      absl::HexStringToBytes("0029"  // length of this key config
+                             "4b"    // key_id
+                             "9999"  // kem_id (unsupported)
+                             "606162636465666768696a6b6c6d6e6f"  // public_key
+                             "707172737475767778797a7b7c7d7e7f"  // public_key
+                             "0004"       // len(symmetric_algorithms)
+                             "00010002",  // HKDF_SHA256, AES_256_GCM
+                             &key));
+  EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
+}
+
+TEST(ObliviousHttpKeyConfigs, UnsupportedKemWithoutLengthPrefix) {
+  std::string key;
+  ASSERT_TRUE(
+      absl::HexStringToBytes("4b"    // key_id
+                             "9999"  // kem_id (unsupported)
+                             "606162636465666768696a6b6c6d6e6f"  // public_key
+                             "707172737475767778797a7b7c7d7e7f"  // public_key
+                             "0004"       // len(symmetric_algorithms)
+                             "00010002",  // HKDF_SHA256, AES_256_GCM
+                             &key));
+  EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
+}
+
 TEST(ObliviousHttpKeyConfigs, TwoSimilarKeyConfigs) {
   std::string key;
   ASSERT_TRUE(absl::HexStringToBytes(
@@ -554,6 +738,51 @@
       IsOkAndHolds(*serialized_key));
 }
 
+TEST(ObliviousHttpHeaderKeyConfigs,
+     RoundTripAllSupportedKemAndKdfCombinations) {
+  const uint16_t kKemIds[] = {
+      EVP_HPKE_DHKEM_P256_HKDF_SHA256,
+      EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
+      EVP_HPKE_XWING,
+      EVP_HPKE_MLKEM768,
+      EVP_HPKE_MLKEM1024,
+  };
+  const uint16_t kKdfIds[] = {
+      EVP_HPKE_HKDF_SHA256,
+      EVP_HPKE_HKDF_SHA384,
+  };
+  uint8_t key_id = 0;
+  for (uint16_t kem_id : kKemIds) {
+    for (uint16_t kdf_id : kKdfIds) {
+      ++key_id;
+      auto initial_config = ObliviousHttpHeaderKeyConfig::Create(
+          key_id, kem_id, kdf_id, EVP_HPKE_CHACHA20_POLY1305);
+      QUICHE_ASSERT_OK(initial_config);
+      std::string test_public_key(
+          EVP_HPKE_KEM_public_key_len(initial_config->GetHpkeKem()),
+          static_cast<char>('a' + (key_id % 20)));
+      auto configs =
+          ObliviousHttpKeyConfigs::Create(*initial_config, test_public_key);
+      QUICHE_ASSERT_OK(configs);
+
+      for (bool with_length_prefix : {false, true}) {
+        auto serialized_key =
+            configs->GenerateConcatenatedKeys(with_length_prefix);
+        QUICHE_ASSERT_OK(serialized_key);
+        auto parsed_configs =
+            ObliviousHttpKeyConfigs::ParseConcatenatedKeys(*serialized_key);
+        QUICHE_ASSERT_OK(parsed_configs);
+        EXPECT_EQ(parsed_configs->NumKeys(), 1);
+        EXPECT_THAT(parsed_configs->PreferredConfig(),
+                    AllOf(HasKeyId(key_id), HasKemId(kem_id), HasKdfId(kdf_id),
+                          HasAeadId(EVP_HPKE_CHACHA20_POLY1305)));
+        EXPECT_THAT(parsed_configs->GetPublicKeyForId(key_id),
+                    IsOkAndHolds(test_public_key));
+      }
+    }
+  }
+}
+
 TEST(ObliviousHttpHeaderKeyConfigs, TestCreateWithInvalidConfigs) {
   EXPECT_FALSE(ObliviousHttpKeyConfigs::Create({}).ok());
   EXPECT_FALSE(ObliviousHttpKeyConfigs::Create(