blob: 2aa0cf83fe1e36020babaf93dfdc93dc567be6dd [file]
#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
#include <cstdint>
#include <string>
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "openssl/hpke.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/quiche_data_writer.h"
#include "quiche/common/test_tools/quiche_test_utils.h"
namespace quiche {
namespace {
using ::quiche::test::IsOkAndHolds;
using ::quiche::test::StatusIs;
using ::testing::AllOf;
using ::testing::HasSubstr;
using ::testing::Property;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
std::string BuildHeader(uint8_t key_id, uint16_t kem_id, uint16_t kdf_id,
uint16_t aead_id) {
int buf_len =
sizeof(key_id) + sizeof(kem_id) + sizeof(kdf_id) + sizeof(aead_id);
std::string hdr(buf_len, '\0');
QuicheDataWriter writer(hdr.size(), hdr.data());
EXPECT_TRUE(writer.WriteUInt8(key_id));
EXPECT_TRUE(writer.WriteUInt16(kem_id));
EXPECT_TRUE(writer.WriteUInt16(kdf_id));
EXPECT_TRUE(writer.WriteUInt16(aead_id));
return hdr;
}
std::string GetSerializedKeyConfig(
ObliviousHttpKeyConfigs::OhttpKeyConfig& key_config) {
uint16_t symmetric_algs_length =
key_config.symmetric_algorithms.size() *
(sizeof(key_config.symmetric_algorithms.cbegin()->kdf_id) +
sizeof(key_config.symmetric_algorithms.cbegin()->aead_id));
int buf_len = sizeof(key_config.key_id) + sizeof(key_config.kem_id) +
key_config.public_key.size() + sizeof(symmetric_algs_length) +
symmetric_algs_length;
std::string ohttp_key(buf_len, '\0');
QuicheDataWriter writer(ohttp_key.size(), ohttp_key.data());
EXPECT_TRUE(writer.WriteUInt8(key_config.key_id));
EXPECT_TRUE(writer.WriteUInt16(key_config.kem_id));
EXPECT_TRUE(writer.WriteStringPiece(key_config.public_key));
EXPECT_TRUE(writer.WriteUInt16(symmetric_algs_length));
for (const auto& symmetric_alg : key_config.symmetric_algorithms) {
EXPECT_TRUE(writer.WriteUInt16(symmetric_alg.kdf_id));
EXPECT_TRUE(writer.WriteUInt16(symmetric_alg.aead_id));
}
return ohttp_key;
}
void ExpectSerializedRecipientContextInfo(absl::string_view ohttp_req_label) {
uint8_t key_id = 3;
uint16_t kem_id = EVP_HPKE_DHKEM_X25519_HKDF_SHA256;
uint16_t kdf_id = EVP_HPKE_HKDF_SHA256;
uint16_t aead_id = EVP_HPKE_AES_256_GCM;
std::string expected(ohttp_req_label);
uint8_t zero_byte = 0x00;
int buf_len = ohttp_req_label.size() + sizeof(zero_byte) + sizeof(key_id) +
sizeof(kem_id) + sizeof(kdf_id) + sizeof(aead_id);
expected.reserve(buf_len);
expected.push_back(zero_byte);
std::string ohttp_cfg(BuildHeader(key_id, kem_id, kdf_id, aead_id));
expected.insert(expected.end(), ohttp_cfg.begin(), ohttp_cfg.end());
auto instance =
ObliviousHttpHeaderKeyConfig::Create(key_id, kem_id, kdf_id, aead_id);
QUICHE_ASSERT_OK(instance);
EXPECT_EQ(instance->SerializeRecipientContextInfo(ohttp_req_label), expected);
EXPECT_THAT(instance->DebugString(), HasSubstr("AES-256-GCM"));
}
TEST(ObliviousHttpHeaderKeyConfig, TestIds) {
QUICHE_EXPECT_OK(CheckKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256));
EXPECT_THAT(CheckKemId(0xFFFF), StatusIs(absl::StatusCode::kUnimplemented));
QUICHE_EXPECT_OK(CheckKdfId(EVP_HPKE_HKDF_SHA256));
EXPECT_THAT(CheckKdfId(0xFFFF), StatusIs(absl::StatusCode::kUnimplemented));
QUICHE_EXPECT_OK(CheckAeadId(EVP_HPKE_AES_256_GCM));
EXPECT_THAT(CheckAeadId(0xFFFF), StatusIs(absl::StatusCode::kUnimplemented));
}
TEST(ObliviousHttpHeaderKeyConfig,
TestSerializeRecipientContextInfoStandardLabel) {
ExpectSerializedRecipientContextInfo("message/bhttp request");
}
TEST(ObliviousHttpHeaderKeyConfig,
TestSerializeRecipientContextInfoChunkedLabel) {
ExpectSerializedRecipientContextInfo("message/bhttp chunked request");
}
TEST(ObliviousHttpHeaderKeyConfig, TestValidKeyConfig) {
auto valid_key_config = ObliviousHttpHeaderKeyConfig::Create(
2, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(valid_key_config);
EXPECT_THAT(valid_key_config->DebugString(), HasSubstr("AES-256-GCM"));
}
TEST(ObliviousHttpHeaderKeyConfig, TestInvalidKeyConfig) {
EXPECT_FALSE(ObliviousHttpHeaderKeyConfig::Create(3, 0, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM)
.ok());
EXPECT_FALSE(
ObliviousHttpHeaderKeyConfig::Create(3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
0, EVP_HPKE_AES_256_GCM)
.ok());
EXPECT_FALSE(
ObliviousHttpHeaderKeyConfig::Create(3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, 0)
.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,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(instance);
std::string good_hdr =
BuildHeader(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(instance->ParseOhttpPayloadHeader(good_hdr));
}
TEST(ObliviousHttpHeaderKeyConfig, TestParsingInvalidHeader) {
auto instance = ObliviousHttpHeaderKeyConfig::Create(
8, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(instance);
std::string keyid_mismatch_hdr =
BuildHeader(0, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
EXPECT_EQ(instance->ParseOhttpPayloadHeader(keyid_mismatch_hdr).code(),
absl::StatusCode::kInvalidArgument);
std::string invalid_hpke_hdr = BuildHeader(8, 0, 0, 0);
EXPECT_EQ(instance->ParseOhttpPayloadHeader(invalid_hpke_hdr).code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ObliviousHttpHeaderKeyConfig, TestParsingKeyIdFromObliviousHttpRequest) {
std::string key_id(sizeof(uint8_t), '\0');
QuicheDataWriter writer(key_id.size(), key_id.data());
EXPECT_TRUE(writer.WriteUInt8(99));
auto parsed_key_id =
ObliviousHttpHeaderKeyConfig::ParseKeyIdFromObliviousHttpRequestPayload(
key_id);
QUICHE_ASSERT_OK(parsed_key_id);
EXPECT_EQ(*parsed_key_id, 99);
}
TEST(ObliviousHttpHeaderKeyConfig, TestCopyable) {
auto obj1 = ObliviousHttpHeaderKeyConfig::Create(
4, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(obj1);
auto copy_obj1_to_obj2 = *obj1;
EXPECT_EQ(copy_obj1_to_obj2.kHeaderLength, obj1->kHeaderLength);
EXPECT_EQ(copy_obj1_to_obj2.SerializeRecipientContextInfo(),
obj1->SerializeRecipientContextInfo());
}
TEST(ObliviousHttpHeaderKeyConfig, TestSerializeOhttpPayloadHeader) {
auto instance = ObliviousHttpHeaderKeyConfig::Create(
7, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM);
QUICHE_ASSERT_OK(instance);
EXPECT_EQ(instance->SerializeOhttpPayloadHeader(),
BuildHeader(7, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM));
EXPECT_THAT(instance->DebugString(), HasSubstr("SHA256"));
}
MATCHER_P(HasKeyId, id, "") {
*result_listener << "has key_id=" << arg.GetKeyId();
return arg.GetKeyId() == id;
}
MATCHER_P(HasKemId, id, "") {
*result_listener << "has kem_id=" << arg.GetHpkeKemId();
return arg.GetHpkeKemId() == id;
}
MATCHER_P(HasKdfId, id, "") {
*result_listener << "has kdf_id=" << arg.GetHpkeKdfId();
return arg.GetHpkeKdfId() == id;
}
MATCHER_P(HasAeadId, id, "") {
*result_listener << "has aead_id=" << arg.GetHpkeAeadId();
return arg.GetHpkeAeadId() == id;
}
TEST(ObliviousHttpKeyConfigs, SingleKeyConfig) {
std::string key;
ASSERT_TRUE(
absl::HexStringToBytes("4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"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, SomeUnsupportedSymmetricAlgorithms) {
std::string key;
ASSERT_TRUE(
absl::HexStringToBytes("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, NoSupportedSymmetricAlgorithms) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"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,
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(
// First key config.
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010002" // HKDF_SHA256, AES_256_GCM
// Second 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));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key),
IsOkAndHolds(Property(&ObliviousHttpKeyConfigs::NumKeys, 2)));
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key)->PreferredConfig(),
AllOf(HasKeyId(0x4b), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM)));
}
TEST(ObliviousHttpKeyConfigs, TwoSimilarKeyConfigsWithLengthPrefix) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"0029" // length of this key config
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010002" // HKDF_SHA256, AES_256_GCM
// Second key config.
"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));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key),
IsOkAndHolds(Property(&ObliviousHttpKeyConfigs::NumKeys, 2)));
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key)->PreferredConfig(),
AllOf(HasKeyId(0x4b), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM)));
}
TEST(ObliviousHttpKeyConfigs, TwoSimilarKeyConfigsReverseOrder) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"4f" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010001" // HKDF_SHA256, AES_128_GCM
// Second key config.
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010002", // HKDF_SHA256, AES_256_GCM
&key));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key),
IsOkAndHolds(Property(&ObliviousHttpKeyConfigs::NumKeys, 2)));
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key)->PreferredConfig(),
AllOf(HasKeyId(0x4f), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM)));
}
TEST(ObliviousHttpKeyConfigs,
TwoSimilarKeyConfigsReverseOrderWithLengthPrefix) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"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
// Second key config.
"0029" // length of this key config
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010002", // HKDF_SHA256, AES_256_GCM
&key));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key),
IsOkAndHolds(Property(&ObliviousHttpKeyConfigs::NumKeys, 2)));
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key)->PreferredConfig(),
AllOf(HasKeyId(0x4f), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM)));
}
TEST(ObliviousHttpKeyConfigs, UnsupportedKemWithLengthPrefix) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"0039" // length of this key config
"33" // key_id
"FFFF" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"808182838485868888898a8b8c8d8e8f" // public_key
"0004" // len(symmetric_algorithms)
"00010001" // HKDF_SHA256, AES_128_GCM
// Second key config.
"003d" // length of this key config
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0018" // len(symmetric_algorithms)
"DEAD0002" // Unsupported, AES_256_GCM
"0001BEEF" // HKDF_SHA256, Unsupported
"DEADBEEF" // Unsupported, Unsupported
"00020003" // HKDF_SHA384, CHACHA20_POLY1305
"00010001" // HKDF_SHA256, AES_128_GCM
"00010002" // HKDF_SHA256, AES_256_GCM
// Third key config.
"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));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key),
IsOkAndHolds(Property(&ObliviousHttpKeyConfigs::NumKeys, 2)));
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key)->PreferredConfig(),
AllOf(HasKeyId(0x4b), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA384),
HasAeadId(EVP_HPKE_CHACHA20_POLY1305)));
}
TEST(ObliviousHttpKeyConfigs, RFCExample) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
"01002031e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e79815500"
"080001000100010003",
&key));
auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key);
QUICHE_ASSERT_OK(configs);
EXPECT_THAT(*configs, Property(&ObliviousHttpKeyConfigs::NumKeys, 1));
EXPECT_THAT(
configs->PreferredConfig(),
AllOf(HasKeyId(0x01), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM)));
std::string expected_public_key;
ASSERT_TRUE(absl::HexStringToBytes(
"31e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e798155",
&expected_public_key));
EXPECT_THAT(configs->GetPublicKeyForId(configs->PreferredConfig().GetKeyId()),
IsOkAndHolds(expected_public_key));
EXPECT_THAT(configs->DebugString(), HasSubstr("AES-128-GCM"));
EXPECT_THAT(configs->DebugString(), HasSubstr("31e1f05a7401"));
}
TEST(ObliviousHttpKeyConfigs, RFCExampleWithLengthPrefix) {
std::string key;
ASSERT_TRUE(
absl::HexStringToBytes("002d01002031e1f05a740102115220e9af918f738674aec95"
"f54db6e04eb705aae8e79815500080001000100010003",
&key));
auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key);
QUICHE_ASSERT_OK(configs);
EXPECT_THAT(*configs, Property(&ObliviousHttpKeyConfigs::NumKeys, 1));
EXPECT_THAT(
configs->PreferredConfig(),
AllOf(HasKeyId(0x01), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM)));
std::string expected_public_key;
ASSERT_TRUE(absl::HexStringToBytes(
"31e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e798155",
&expected_public_key));
EXPECT_THAT(configs->GetPublicKeyForId(configs->PreferredConfig().GetKeyId()),
IsOkAndHolds(expected_public_key));
EXPECT_THAT(configs->DebugString(), HasSubstr("AES-128-GCM"));
EXPECT_THAT(configs->DebugString(), HasSubstr("31e1f05a7401"));
}
TEST(ObliviousHttpKeyConfigs, DuplicateKeyId) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config.
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010002" // HKDF_SHA256, AES_256_GCM
// Second key config.
"4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010001", // HKDF_SHA256, AES_128_GCM
&key));
EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
}
TEST(ObliviousHttpHeaderKeyConfigs, TestCreateWithSingleKeyConfig) {
auto instance = ObliviousHttpHeaderKeyConfig::Create(
123, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305);
QUICHE_ASSERT_OK(instance);
EXPECT_THAT(instance->DebugString(), HasSubstr("CHACHA20-POLY1305"));
std::string test_public_key(
EVP_HPKE_KEM_public_key_len(instance->GetHpkeKem()), 'a');
auto configs = ObliviousHttpKeyConfigs::Create(*instance, test_public_key);
QUICHE_ASSERT_OK(configs);
auto serialized_key = configs->GenerateConcatenatedKeys();
QUICHE_ASSERT_OK(serialized_key);
auto ohttp_configs =
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(*serialized_key);
QUICHE_ASSERT_OK(ohttp_configs);
EXPECT_EQ(ohttp_configs->PreferredConfig().GetKeyId(), 123);
auto parsed_public_key = ohttp_configs->GetPublicKeyForId(123);
EXPECT_THAT(parsed_public_key, IsOkAndHolds(test_public_key));
}
TEST(ObliviousHttpHeaderKeyConfigs, TestCreateWithWithMultipleKeys) {
std::string expected_preferred_public_key(32, 'a');
ObliviousHttpKeyConfigs::OhttpKeyConfig config1 = {
100,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
expected_preferred_public_key,
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM}}};
EXPECT_THAT(config1.DebugString(), HasSubstr("AES-256-GCM"));
ObliviousHttpKeyConfigs::OhttpKeyConfig config2 = {
200,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'b'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_CHACHA20_POLY1305}}};
EXPECT_THAT(config2.DebugString(), HasSubstr("CHACHA20-POLY1305"));
auto configs = ObliviousHttpKeyConfigs::Create({config1, config2});
QUICHE_ASSERT_OK(configs);
EXPECT_THAT(configs->DebugString(), HasSubstr("CHACHA20-POLY1305"));
auto serialized_key = configs->GenerateConcatenatedKeys();
ASSERT_THAT(serialized_key,
IsOkAndHolds(absl::StrCat(GetSerializedKeyConfig(config1),
GetSerializedKeyConfig(config2))));
auto ohttp_configs =
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(*serialized_key);
QUICHE_ASSERT_OK(ohttp_configs);
EXPECT_THAT(ohttp_configs->DebugString(), HasSubstr("CHACHA20-POLY1305"));
ASSERT_EQ(ohttp_configs->NumKeys(), 2);
EXPECT_THAT(
configs->PreferredConfig(),
AllOf(HasKeyId(100), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM)));
EXPECT_THAT(ohttp_configs->GetPublicKeyForId(
ohttp_configs->PreferredConfig().GetKeyId()),
IsOkAndHolds(expected_preferred_public_key));
}
TEST(ObliviousHttpHeaderKeyConfigs,
RoundTripSingleKeyConfigWithoutLengthPrefix) {
auto initial_config = ObliviousHttpHeaderKeyConfig::Create(
123, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305);
QUICHE_ASSERT_OK(initial_config);
std::string test_public_key(
EVP_HPKE_KEM_public_key_len(initial_config->GetHpkeKem()), 'a');
auto configs =
ObliviousHttpKeyConfigs::Create(*initial_config, test_public_key);
QUICHE_ASSERT_OK(configs);
auto serialized_key =
configs->GenerateConcatenatedKeys(/*with_length_prefix=*/false);
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(initial_config->GetKeyId()),
HasKemId(initial_config->GetHpkeKemId()),
HasKdfId(initial_config->GetHpkeKdfId()),
HasAeadId(initial_config->GetHpkeAeadId())));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(initial_config->GetKeyId()),
IsOkAndHolds(test_public_key));
EXPECT_THAT(
parsed_configs->GenerateConcatenatedKeys(/*with_length_prefix=*/false),
IsOkAndHolds(*serialized_key));
}
TEST(ObliviousHttpHeaderKeyConfigs, RoundTripSingleKeyConfigWithLengthPrefix) {
auto initial_config = ObliviousHttpHeaderKeyConfig::Create(
123, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305);
QUICHE_ASSERT_OK(initial_config);
std::string test_public_key(
EVP_HPKE_KEM_public_key_len(initial_config->GetHpkeKem()), 'a');
auto configs =
ObliviousHttpKeyConfigs::Create(*initial_config, test_public_key);
QUICHE_ASSERT_OK(configs);
auto serialized_key =
configs->GenerateConcatenatedKeys(/*with_length_prefix=*/true);
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(initial_config->GetKeyId()),
HasKemId(initial_config->GetHpkeKemId()),
HasKdfId(initial_config->GetHpkeKdfId()),
HasAeadId(initial_config->GetHpkeAeadId())));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(initial_config->GetKeyId()),
IsOkAndHolds(test_public_key));
EXPECT_THAT(
parsed_configs->GenerateConcatenatedKeys(/*with_length_prefix=*/true),
IsOkAndHolds(*serialized_key));
}
TEST(ObliviousHttpHeaderKeyConfigs,
RoundTripMultipleKeyConfigsWithoutLengthPrefix) {
ObliviousHttpKeyConfigs::OhttpKeyConfig config1 = {
100,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'a'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM}}};
ObliviousHttpKeyConfigs::OhttpKeyConfig config2 = {
200,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'b'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_CHACHA20_POLY1305},
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}}};
auto configs = ObliviousHttpKeyConfigs::Create({config1, config2});
QUICHE_ASSERT_OK(configs);
auto serialized_key =
configs->GenerateConcatenatedKeys(/*with_length_prefix=*/false);
QUICHE_ASSERT_OK(serialized_key);
auto parsed_configs =
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(*serialized_key);
QUICHE_ASSERT_OK(parsed_configs);
EXPECT_EQ(parsed_configs->NumKeys(), 2);
EXPECT_THAT(parsed_configs->PreferredConfig(),
AllOf(HasKeyId(100), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256)));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(100),
IsOkAndHolds(std::string(32, 'a')));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(200),
IsOkAndHolds(std::string(32, 'b')));
EXPECT_THAT(
parsed_configs->GenerateConcatenatedKeys(/*with_length_prefix=*/false),
IsOkAndHolds(*serialized_key));
}
TEST(ObliviousHttpHeaderKeyConfigs,
RoundTripMultipleKeyConfigsWithLengthPrefix) {
ObliviousHttpKeyConfigs::OhttpKeyConfig config1 = {
100,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'a'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM}}};
ObliviousHttpKeyConfigs::OhttpKeyConfig config2 = {
200,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'b'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_CHACHA20_POLY1305},
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}}};
auto configs = ObliviousHttpKeyConfigs::Create({config1, config2});
QUICHE_ASSERT_OK(configs);
auto serialized_key =
configs->GenerateConcatenatedKeys(/*with_length_prefix=*/true);
QUICHE_ASSERT_OK(serialized_key);
auto parsed_configs =
ObliviousHttpKeyConfigs::ParseConcatenatedKeys(*serialized_key);
QUICHE_ASSERT_OK(parsed_configs);
EXPECT_EQ(parsed_configs->NumKeys(), 2);
EXPECT_THAT(parsed_configs->PreferredConfig(),
AllOf(HasKeyId(100), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256)));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(100),
IsOkAndHolds(std::string(32, 'a')));
EXPECT_THAT(parsed_configs->GetPublicKeyForId(200),
IsOkAndHolds(std::string(32, 'b')));
EXPECT_THAT(
parsed_configs->GenerateConcatenatedKeys(/*with_length_prefix=*/true),
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(ObliviousHttpKeyConfigs,
ReverseRoundTripMultipleSymmetricAlgorithmsWithoutLengthPrefix) {
std::string key;
ASSERT_TRUE(
absl::HexStringToBytes("4b" // key_id
"0020" // kem_id
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0008" // len(symmetric_algorithms)
"00010001" // HKDF_SHA256, AES_128_GCM
"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_128_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));
EXPECT_THAT(configs->GenerateConcatenatedKeys(/*with_length_prefix=*/false),
IsOkAndHolds(key));
}
TEST(ObliviousHttpKeyConfigs,
ReverseRoundTripMultipleSymmetricAlgorithmsWithLengthPrefix) {
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)
"00010001" // HKDF_SHA256, AES_128_GCM
"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_128_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));
EXPECT_THAT(configs->GenerateConcatenatedKeys(/*with_length_prefix=*/true),
IsOkAndHolds(key));
}
TEST(ObliviousHttpKeyConfigs, ParseOhttpPayloadHeaderAgainstConfigs) {
auto config1 = ObliviousHttpHeaderKeyConfig::Create(
5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM);
QUICHE_ASSERT_OK(config1);
auto config2 = ObliviousHttpHeaderKeyConfig::Create(
5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
QUICHE_ASSERT_OK(config2);
std::string payload_bytes_1 =
BuildHeader(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM) +
"some payload data";
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseOhttpPayloadHeaderAgainstConfigs(
payload_bytes_1, {*config1, *config2}),
IsOkAndHolds(AllOf(
HasKeyId(5), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_128_GCM))));
std::string payload_bytes_2 =
BuildHeader(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM) +
"some payload data";
EXPECT_THAT(
ObliviousHttpKeyConfigs::ParseOhttpPayloadHeaderAgainstConfigs(
payload_bytes_2, {*config1, *config2}),
IsOkAndHolds(AllOf(
HasKeyId(5), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM))));
std::string mismatch_payload =
BuildHeader(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305) +
"some payload data";
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseOhttpPayloadHeaderAgainstConfigs(
mismatch_payload, {*config1, *config2}),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Payload did not match any key configs")));
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseOhttpPayloadHeaderAgainstConfigs(
payload_bytes_1, {}),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Payload did not match any key configs")));
}
TEST(ObliviousHttpKeyConfigs, GetConfigForPayload) {
std::string key;
ASSERT_TRUE(absl::HexStringToBytes(
// First key config (key_id = 0x4b).
"4b" // key_id
"0020" // kem_id: X25519-HKDF-SHA256
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0008" // len(symmetric_algorithms)
"00010001" // HKDF_SHA256, AES_128_GCM
"00010002" // HKDF_SHA256, AES_256_GCM
// Second key config (key_id = 0x4f).
"4f" // key_id
"0020" // kem_id: X25519-HKDF-SHA256
"606162636465666768696a6b6c6d6e6f" // public_key
"707172737475767778797a7b7c7d7e7f" // public_key
"0004" // len(symmetric_algorithms)
"00010003", // HKDF_SHA256, CHACHA20_POLY1305
&key));
auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key);
QUICHE_ASSERT_OK(configs);
std::string payload_1 =
BuildHeader(0x4b, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM) +
"payload";
EXPECT_THAT(
configs->GetConfigForPayload(payload_1),
IsOkAndHolds(AllOf(
HasKeyId(0x4b), HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256), HasAeadId(EVP_HPKE_AES_256_GCM))));
std::string payload_2 =
BuildHeader(0x4f, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305) +
"payload";
EXPECT_THAT(configs->GetConfigForPayload(payload_2),
IsOkAndHolds(AllOf(HasKeyId(0x4f),
HasKemId(EVP_HPKE_DHKEM_X25519_HKDF_SHA256),
HasKdfId(EVP_HPKE_HKDF_SHA256),
HasAeadId(EVP_HPKE_CHACHA20_POLY1305))));
std::string unknown_key_id_payload =
BuildHeader(0x99, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM) +
"payload";
EXPECT_THAT(configs->GetConfigForPayload(unknown_key_id_payload),
StatusIs(absl::StatusCode::kNotFound,
HasSubstr("No config found for key_id ")));
std::string mismatch_alg_payload =
BuildHeader(0x4b, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_CHACHA20_POLY1305) +
"payload";
EXPECT_THAT(configs->GetConfigForPayload(mismatch_alg_payload),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Payload did not match any key configs")));
EXPECT_THAT(configs->GetConfigForPayload(""),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(ObliviousHttpHeaderKeyConfigs, TestCreateWithInvalidConfigs) {
EXPECT_FALSE(ObliviousHttpKeyConfigs::Create({}).ok());
EXPECT_FALSE(ObliviousHttpKeyConfigs::Create(
{{100, 2, std::string(32, 'a'), {{2, 3}, {4, 5}}},
{200, 6, std::string(32, 'b'), {{7, 8}, {9, 10}}}})
.ok());
EXPECT_FALSE(ObliviousHttpKeyConfigs::Create(
{{123,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
// The expected length for this KEM is 32.
"invalid key length",
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}}}})
.ok());
}
TEST(ObliviousHttpHeaderKeyConfigs,
TestCreateSingleKeyConfigWithInvalidConfig) {
const auto sample_ohttp_hdr_config = ObliviousHttpHeaderKeyConfig::Create(
123, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM);
QUICHE_ASSERT_OK(sample_ohttp_hdr_config);
EXPECT_THAT(sample_ohttp_hdr_config->DebugString(), HasSubstr("AES-128-GCM"));
EXPECT_FALSE(ObliviousHttpKeyConfigs::Create(*sample_ohttp_hdr_config,
/*public_key=*/"")
.ok());
EXPECT_FALSE(
ObliviousHttpKeyConfigs::Create(*sample_ohttp_hdr_config,
// The expected length for this KEM is 32.
"invalid key length")
.ok());
}
TEST(ObliviousHttpHeaderKeyConfigs, TestHashImplWithObliviousStruct) {
// Insert different symmetric algorithms 50 times.
absl::flat_hash_set<ObliviousHttpKeyConfigs::SymmetricAlgorithmsConfig>
symmetric_algs_set;
for (int i = 0; i < 50; ++i) {
symmetric_algs_set.insert({EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM});
symmetric_algs_set.insert({EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM});
symmetric_algs_set.insert(
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_CHACHA20_POLY1305});
}
ASSERT_EQ(symmetric_algs_set.size(), 3);
EXPECT_THAT(symmetric_algs_set,
UnorderedElementsAreArray<
ObliviousHttpKeyConfigs::SymmetricAlgorithmsConfig>({
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM},
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM},
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_CHACHA20_POLY1305},
}));
// Insert different Key configs 50 times.
absl::flat_hash_set<ObliviousHttpKeyConfigs::OhttpKeyConfig>
ohttp_key_configs_set;
ObliviousHttpKeyConfigs::OhttpKeyConfig expected_key_config{
100,
EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
std::string(32, 'c'),
{{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM},
{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM}}};
for (int i = 0; i < 50; ++i) {
ohttp_key_configs_set.insert(expected_key_config);
}
ASSERT_EQ(ohttp_key_configs_set.size(), 1);
EXPECT_THAT(ohttp_key_configs_set, UnorderedElementsAre(expected_key_config));
}
} // namespace
} // namespace quiche