blob: 0227ee429ae15d991caa6c786414f2890786b074 [file] [log] [blame]
#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
#include <cstdint>
#include "absl/strings/escaping.h"
#include "openssl/hpke.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/quiche_data_writer.h"
namespace quiche {
namespace {
using ::testing::AllOf;
using ::testing::Property;
using ::testing::StrEq;
/**
* Build Request header.
*/
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)); // kemID
EXPECT_TRUE(writer.WriteUInt16(kdf_id)); // kdfID
EXPECT_TRUE(writer.WriteUInt16(aead_id)); // aeadID
return hdr;
}
TEST(ObliviousHttpHeaderKeyConfig, TestSerializeRecipientContextInfo) {
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;
absl::string_view ohttp_req_label = "message/bhttp request";
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);
ASSERT_TRUE(instance.ok());
EXPECT_EQ(instance.value().SerializeRecipientContextInfo(), expected);
}
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);
ASSERT_TRUE(valid_key_config.ok());
}
TEST(ObliviousHttpHeaderKeyConfig, TestInvalidKeyConfig) {
auto invalid_kem = ObliviousHttpHeaderKeyConfig::Create(
3, 0, EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
EXPECT_EQ(invalid_kem.status().code(), absl::StatusCode::kInvalidArgument);
auto invalid_kdf = ObliviousHttpHeaderKeyConfig::Create(
3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, 0, EVP_HPKE_AES_256_GCM);
EXPECT_EQ(invalid_kdf.status().code(), absl::StatusCode::kInvalidArgument);
auto invalid_aead = ObliviousHttpHeaderKeyConfig::Create(
3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256, 0);
EXPECT_EQ(invalid_kdf.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST(ObliviousHttpHeaderKeyConfig, TestParsingValidHeader) {
auto instance = ObliviousHttpHeaderKeyConfig::Create(
5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
ASSERT_TRUE(instance.ok());
std::string good_hdr(BuildHeader(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
ASSERT_TRUE(instance.value().ParseOhttpPayloadHeader(good_hdr).ok());
}
TEST(ObliviousHttpHeaderKeyConfig, TestParsingInvalidHeader) {
auto instance = ObliviousHttpHeaderKeyConfig::Create(
8, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
ASSERT_TRUE(instance.ok());
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.value().ParseOhttpPayloadHeader(keyid_mismatch_hdr).code(),
absl::StatusCode::kInvalidArgument);
std::string invalid_hpke_hdr(BuildHeader(8, 0, 0, 0));
EXPECT_EQ(instance.value().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);
ASSERT_TRUE(parsed_key_id.ok());
EXPECT_EQ(parsed_key_id.value(), 99);
}
TEST(ObliviousHttpHeaderKeyConfig, TestCopyable) {
auto obj1 = ObliviousHttpHeaderKeyConfig::Create(
4, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_256_GCM);
ASSERT_TRUE(obj1.ok());
auto copy_obj1_to_obj2 = obj1.value();
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);
ASSERT_TRUE(instance.ok());
EXPECT_EQ(instance->SerializeOhttpPayloadHeader(),
BuildHeader(7, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM));
}
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 = absl::HexStringToBytes(
"4b0020f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fa27a049bc746a6e97a1e0244b00"
"0400010002");
auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).value();
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)));
EXPECT_THAT(
configs.GetPublicKeyForId(configs.PreferredConfig().GetKeyId()).value(),
StrEq(absl::HexStringToBytes(
"f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fa27a049bc746a6e97a1e0244b")));
}
TEST(ObliviousHttpKeyConfigs, TwoSimilarKeyConfigs) {
std::string key = absl::HexStringToBytes(
"4b0020f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fa27a049bc746a6e97a1e0244b00"
"0400010002" // Intentional concatenation
"4f0020f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fa27a049bc746a6e97a1e0244b00"
"0400010001");
EXPECT_THAT(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).value(),
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, RFCExample) {
std::string key = absl::HexStringToBytes(
"01002031e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e79815500"
"080001000100010003");
auto configs = ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).value();
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)));
EXPECT_THAT(
configs.GetPublicKeyForId(configs.PreferredConfig().GetKeyId()).value(),
StrEq(absl::HexStringToBytes(
"31e1f05a740102115220e9af918f738674aec95f54db6e04eb705aae8e798155")));
}
TEST(ObliviousHttpKeyConfigs, DuplicateKeyId) {
std::string key = absl::HexStringToBytes(
"4b0020f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fa27a049bc746a6e97a1e0244b00"
"0400010002" // Intentional concatenation
"4b0020f83e0a17cbdb18d2684dd2a9b087a43e5f3fa3fb27a049bc746a6e97a1e0244b00"
"0400010001");
EXPECT_FALSE(ObliviousHttpKeyConfigs::ParseConcatenatedKeys(key).ok());
}
} // namespace
} // namespace quiche