blob: 04360f0a96230a0f884deff79ec697a7532ee9ec [file] [log] [blame]
#include "quiche/oblivious_http/oblivious_http_gateway.h"
#include <stdint.h>
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.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/platform/api/quiche_thread.h"
#include "quiche/common/quiche_random.h"
#include "quiche/common/test_tools/quiche_test_utils.h"
#include "quiche/oblivious_http/buffers/oblivious_http_request.h"
#include "quiche/oblivious_http/common/oblivious_http_chunk_handler.h"
#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
namespace quiche {
namespace {
constexpr absl::string_view kEncapsulatedChunkedRequest =
"01002000010001"
"8811eb457e100811c40a0aa71340a1b81d804bb986f736f2f566a7199761a032"
"1c2ad24942d4d692563012f2980c8fef437a336b9b2fc938ef77a5834f"
"1d2e33d8fd25577afe31bd1c79d094f76b6250ae6549b473ecd950501311"
"001c6c1395d0ef7c1022297966307b8a7f";
class TestChunkHandler : public ObliviousHttpChunkHandler {
public:
TestChunkHandler() = default;
~TestChunkHandler() override = default;
absl::Status OnDecryptedChunk(absl::string_view decrypted_chunk) override {
EXPECT_FALSE(on_chunks_done_called_);
chunk_count_++;
absl::StrAppend(&concatenated_decrypted_chunks_, decrypted_chunk);
return absl::OkStatus();
}
absl::Status OnChunksDone() override {
EXPECT_FALSE(on_chunks_done_called_);
on_chunks_done_called_ = true;
std::string expected_request;
EXPECT_TRUE(absl::HexStringToBytes(
"00034745540568747470730b6578616d706c652e636f6d012f",
&expected_request));
EXPECT_EQ(concatenated_decrypted_chunks_, expected_request);
return absl::OkStatus();
}
uint64_t GetChunkCount() const { return chunk_count_; }
bool GetOnChunksDoneCalled() const { return on_chunks_done_called_; }
private:
uint64_t chunk_count_ = 0;
bool on_chunks_done_called_ = false;
std::string concatenated_decrypted_chunks_;
};
std::string GetHpkePrivateKey() {
// Dev/Test private key generated using Keystore.
absl::string_view hpke_key_hex =
"b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1";
std::string hpke_key_bytes;
EXPECT_TRUE(absl::HexStringToBytes(hpke_key_hex, &hpke_key_bytes));
return hpke_key_bytes;
}
std::string GetHpkePublicKey() {
// Dev/Test public key generated using Keystore.
absl::string_view public_key =
"6d21cfe09fbea5122f9ebc2eb2a69fcc4f06408cd54aac934f012e76fcdcef62";
std::string public_key_bytes;
EXPECT_TRUE(absl::HexStringToBytes(public_key, &public_key_bytes));
return public_key_bytes;
}
const ObliviousHttpHeaderKeyConfig GetOhttpKeyConfig(uint8_t key_id,
uint16_t kem_id,
uint16_t kdf_id,
uint16_t aead_id) {
auto ohttp_key_config =
ObliviousHttpHeaderKeyConfig::Create(key_id, kem_id, kdf_id, aead_id);
EXPECT_TRUE(ohttp_key_config.ok());
return std::move(ohttp_key_config.value());
}
TEST(ObliviousHttpGateway, TestProvisioningKeyAndDecapsulate) {
// X25519 Secret key (priv key).
// https://www.rfc-editor.org/rfc/rfc9458.html#appendix-A-2
constexpr absl::string_view kX25519SecretKey =
"3c168975674b2fa8e465970b79c8dcf09f1c741626480bd4c6162fc5b6a98e1a";
std::string x25519_secret_key_bytes;
ASSERT_TRUE(
absl::HexStringToBytes(kX25519SecretKey, &x25519_secret_key_bytes));
auto instance = ObliviousHttpGateway::Create(
/*hpke_private_key*/ x25519_secret_key_bytes,
/*ohttp_key_config*/ GetOhttpKeyConfig(
/*key_id=*/1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM));
// Encapsulated request.
// https://www.rfc-editor.org/rfc/rfc9458.html#appendix-A-14
constexpr absl::string_view kEncapsulatedRequest =
"010020000100014b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad19dec96c2"
"08b4726374e469135906992e1268c594d2a10c695d858c40a026e7965e7d86b83dd440b2"
"c0185204b4d63525";
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedRequest,
&encapsulated_request_bytes));
auto decrypted_req =
instance->DecryptObliviousHttpRequest(encapsulated_request_bytes);
ASSERT_TRUE(decrypted_req.ok());
ASSERT_FALSE(decrypted_req->GetPlaintextData().empty());
}
absl::StatusOr<ChunkedObliviousHttpGateway> CreateChunkedObliviousHttpGateway(
ObliviousHttpChunkHandler& chunk_handler,
QuicheRandom* quiche_random = nullptr) {
constexpr absl::string_view kX25519SecretKey =
"1c190d72acdbe4dbc69e680503bb781a932c70a12c8f3754434c67d8640d8698";
std::string x25519_secret_key_bytes;
EXPECT_TRUE(
absl::HexStringToBytes(kX25519SecretKey, &x25519_secret_key_bytes));
return ChunkedObliviousHttpGateway::Create(
x25519_secret_key_bytes,
GetOhttpKeyConfig(
/*key_id=*/1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
EVP_HPKE_AES_128_GCM),
chunk_handler, quiche_random);
}
TEST(ChunkedObliviousHttpGateway, ProvisionKeyAndDecapsulateFullRequest) {
// Example from
// https://www.ietf.org/archive/id/draft-ietf-ohai-chunked-ohttp-05.html#appendix-A
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
QUICHE_EXPECT_OK(instance->DecryptRequest(encapsulated_request_bytes, true));
EXPECT_TRUE(chunk_handler.GetOnChunksDoneCalled());
EXPECT_EQ(chunk_handler.GetChunkCount(), 3);
}
TEST(ChunkedObliviousHttpGateway, ProvisionKeyAndDecapsulateBufferedRequest) {
// Example from
// https://www.ietf.org/archive/id/draft-ietf-ohai-chunked-ohttp-05.html#appendix-A
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
for (size_t i = 0; i < encapsulated_request_bytes.size(); i++) {
absl::string_view current_byte(&encapsulated_request_bytes[i], 1);
QUICHE_EXPECT_OK(instance->DecryptRequest(current_byte, false));
}
QUICHE_EXPECT_OK(instance->DecryptRequest("", true));
EXPECT_TRUE(chunk_handler.GetOnChunksDoneCalled());
EXPECT_EQ(chunk_handler.GetChunkCount(), 3);
}
TEST(ChunkedObliviousHttpGateway, DecryptingAfterDoneReturnsInvalidArgument) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
QUICHE_EXPECT_OK(instance->DecryptRequest(encapsulated_request_bytes, true));
auto second_decrypt =
instance->DecryptRequest(encapsulated_request_bytes, true);
EXPECT_EQ(second_decrypt.code(), absl::StatusCode::kInternal);
EXPECT_EQ(second_decrypt.message(), "Decrypting is marked as invalid.");
}
TEST(ChunkedObliviousHttpGateway, FinalChunkNotDoneReturnsInvalidArgument) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes("010020", &encapsulated_request_bytes));
EXPECT_EQ(instance->DecryptRequest(encapsulated_request_bytes, true).code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ChunkedObliviousHttpGateway, GettingDecryptErrorSetsGatewayToInvalid) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string invalid_key_request =
"020020000100014b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad19dec96c2"
"08b4726374e469135906992e";
std::string encapsulated_request_bytes;
ASSERT_TRUE(
absl::HexStringToBytes(invalid_key_request, &encapsulated_request_bytes));
EXPECT_EQ(instance->DecryptRequest(encapsulated_request_bytes, false).code(),
absl::StatusCode::kInvalidArgument);
auto second_decrypt =
instance->DecryptRequest(encapsulated_request_bytes, true);
EXPECT_EQ(second_decrypt.code(), absl::StatusCode::kInternal);
EXPECT_EQ(second_decrypt.message(), "Decrypting is marked as invalid.");
}
TEST(ChunkedObliviousHttpGateway, InvalidKeyConfigReturnsInvalidArgument) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(
absl::HexStringToBytes("990020000100018811eb457e100811c40a0aa71340a1b81d8"
"04bb986f736f2f566a7199761a032",
&encapsulated_request_bytes));
EXPECT_EQ(instance->DecryptRequest(encapsulated_request_bytes, false).code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ChunkedObliviousHttpGateway, ChunkHandlerOnChunkErrorPropagates) {
class FailingChunkHandler : public ObliviousHttpChunkHandler {
public:
FailingChunkHandler() = default;
~FailingChunkHandler() override = default;
absl::Status OnDecryptedChunk(
absl::string_view /*decrypted_chunk*/) override {
return absl::InvalidArgumentError("Invalid data");
}
absl::Status OnChunksDone() override {
return absl::InvalidArgumentError("Invalid data");
}
};
FailingChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
EXPECT_EQ(instance->DecryptRequest(encapsulated_request_bytes, true).code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ChunkedObliviousHttpGateway, ChunkHandlerOnChunksDoneErrorPropagates) {
class FailingChunkHandler : public ObliviousHttpChunkHandler {
public:
FailingChunkHandler() = default;
~FailingChunkHandler() override = default;
absl::Status OnDecryptedChunk(
absl::string_view /*decrypted_chunk*/) override {
return absl::OkStatus();
}
absl::Status OnChunksDone() override {
return absl::InvalidArgumentError("Invalid data");
}
};
FailingChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
EXPECT_EQ(instance->DecryptRequest(encapsulated_request_bytes, true).code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ObliviousHttpGateway, TestDecryptingMultipleRequestsWithSingleInstance) {
auto instance = ObliviousHttpGateway::Create(
GetHpkePrivateKey(),
GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
// plaintext: "test request 1"
absl::string_view encrypted_req_1 =
"010020000100025f20b60306b61ad9ecad389acd752ca75c4e2969469809fe3d84aae137"
"f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c8ea6c10592594845f";
std::string encrypted_req_1_bytes;
ASSERT_TRUE(absl::HexStringToBytes(encrypted_req_1, &encrypted_req_1_bytes));
auto decapsulated_req_1 =
instance->DecryptObliviousHttpRequest(encrypted_req_1_bytes);
ASSERT_TRUE(decapsulated_req_1.ok());
ASSERT_FALSE(decapsulated_req_1->GetPlaintextData().empty());
// plaintext: "test request 2"
absl::string_view encrypted_req_2 =
"01002000010002285ebc2fcad72cc91b378050cac29a62feea9cd97829335ee9fc87e672"
"4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5d922918865a0a447a";
std::string encrypted_req_2_bytes;
ASSERT_TRUE(absl::HexStringToBytes(encrypted_req_2, &encrypted_req_2_bytes));
auto decapsulated_req_2 =
instance->DecryptObliviousHttpRequest(encrypted_req_2_bytes);
ASSERT_TRUE(decapsulated_req_2.ok());
ASSERT_FALSE(decapsulated_req_2->GetPlaintextData().empty());
}
TEST(ObliviousHttpGateway, TestInvalidHPKEKey) {
// Invalid private key.
EXPECT_EQ(ObliviousHttpGateway::Create(
"Invalid HPKE key",
GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
.status()
.code(),
absl::StatusCode::kInternal);
// Empty private key.
EXPECT_EQ(ObliviousHttpGateway::Create(
/*hpke_private_key*/ "",
GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
.status()
.code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ChunkedObliviousHttpGateway, TestInvalidHPKEKey) {
TestChunkHandler chunk_handler;
// Invalid private key.
EXPECT_EQ(ChunkedObliviousHttpGateway::Create(
"Invalid HPKE key",
GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
chunk_handler)
.status()
.code(),
absl::StatusCode::kInternal);
// Empty private key.
EXPECT_EQ(ChunkedObliviousHttpGateway::Create(
/*hpke_private_key*/ "",
GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
chunk_handler)
.status()
.code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ObliviousHttpGateway, TestObliviousResponseHandling) {
auto ohttp_key_config =
GetOhttpKeyConfig(3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
auto instance =
ObliviousHttpGateway::Create(GetHpkePrivateKey(), ohttp_key_config);
ASSERT_TRUE(instance.ok());
auto encapsualte_request_on_client =
ObliviousHttpRequest::CreateClientObliviousRequest(
"test", GetHpkePublicKey(), ohttp_key_config);
ASSERT_TRUE(encapsualte_request_on_client.ok());
// Setup Recipient to allow setting up the HPKE context, and subsequently use
// it to encrypt the response.
auto decapsulated_req_on_server = instance->DecryptObliviousHttpRequest(
encapsualte_request_on_client->EncapsulateAndSerialize());
ASSERT_TRUE(decapsulated_req_on_server.ok());
auto server_request_context =
std::move(decapsulated_req_on_server.value()).ReleaseContext();
auto encapsulate_resp_on_gateway = instance->CreateObliviousHttpResponse(
"some response", server_request_context);
ASSERT_TRUE(encapsulate_resp_on_gateway.ok());
ASSERT_FALSE(encapsulate_resp_on_gateway->EncapsulateAndSerialize().empty());
}
class TestQuicheRandom : public QuicheRandom {
public:
TestQuicheRandom(std::string seed) : seed_(seed) {}
~TestQuicheRandom() override {}
void RandBytes(void* data, size_t len) override {
size_t copy_len = std::min(len, seed_.length());
memcpy(data, seed_.c_str(), copy_len);
}
uint64_t RandUint64() override { return 0; }
void InsecureRandBytes(void* /*data*/, size_t /*len*/) override {}
uint64_t InsecureRandUint64() override { return 0; }
private:
std::string seed_;
};
TEST(ChunkedObliviousHttpGateway, SingleChunkResponse) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
// Request decryption implicitly sets up the context for response encryption
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
QUICHE_EXPECT_OK(instance->DecryptRequest(encapsulated_request_bytes, true));
// 63 byte response to test final chunk indicator length.
std::string plaintext_response =
"111111111111111111111111111111111111111111111111111111111111111111111111"
"111111111111111111111111111111111111111111111111111111";
absl::StatusOr<std::string> encrypted_response =
instance->EncryptResponse(plaintext_response, true);
QUICHE_EXPECT_OK(encrypted_response);
EXPECT_FALSE(encrypted_response->empty());
EXPECT_NE(*encrypted_response, plaintext_response);
}
TEST(ChunkedObliviousHttpGateway, MultipleChunkResponse) {
// Example from
// https://www.ietf.org/archive/id/draft-ietf-ohai-chunked-ohttp-05.html#appendix-A
TestChunkHandler chunk_handler;
std::string response_nonce = "bcce7f4cb921309ba5d62edf1769ef09";
std::string response_nonce_bytes;
EXPECT_TRUE(absl::HexStringToBytes(response_nonce, &response_nonce_bytes));
TestQuicheRandom quiche_random(response_nonce_bytes);
auto instance =
CreateChunkedObliviousHttpGateway(chunk_handler, &quiche_random);
// Request decrypting implicitly sets up the context for response encryption
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
QUICHE_EXPECT_OK(instance->DecryptRequest(encapsulated_request_bytes, true));
std::string plaintext_response = "01";
std::string plaintext_response_bytes;
EXPECT_TRUE(
absl::HexStringToBytes(plaintext_response, &plaintext_response_bytes));
std::vector<std::string> encrypted_response_chunks;
absl::StatusOr<std::string> encrypted_response_chunk =
instance->EncryptResponse(plaintext_response_bytes, false);
QUICHE_EXPECT_OK(encrypted_response_chunk);
std::string encrypted_response_chunk_hex =
absl::BytesToHexString(*encrypted_response_chunk);
// The first chunk should contain the response nonce.
EXPECT_EQ(
encrypted_response_chunk_hex,
"bcce7f4cb921309ba5d62edf1769ef091179bf1cc87fa0e2c02de4546945aa3d1e48");
plaintext_response = "40c8";
EXPECT_TRUE(
absl::HexStringToBytes(plaintext_response, &plaintext_response_bytes));
encrypted_response_chunk =
instance->EncryptResponse(plaintext_response_bytes, false);
QUICHE_EXPECT_OK(encrypted_response_chunk);
encrypted_response_chunk_hex =
absl::BytesToHexString(*encrypted_response_chunk);
EXPECT_EQ(encrypted_response_chunk_hex,
"12b348b5bd4c594c16b6170b07b475845d1f32");
EXPECT_TRUE(
absl::HexStringToBytes(plaintext_response, &plaintext_response_bytes));
encrypted_response_chunk =
instance->EncryptResponse(/*plaintext_payload=*/"", true);
QUICHE_EXPECT_OK(encrypted_response_chunk);
encrypted_response_chunk_hex =
absl::BytesToHexString(*encrypted_response_chunk);
EXPECT_EQ(encrypted_response_chunk_hex, "00ed9d8a796617a5b27265f4d73247f639");
}
TEST(ChunkedObliviousHttpGateway, EncryptingAfterFinalChunkFails) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
// Request decryption implicitly sets up the context for response encryption
std::string encapsulated_request_bytes;
ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedChunkedRequest,
&encapsulated_request_bytes));
QUICHE_EXPECT_OK(instance->DecryptRequest(encapsulated_request_bytes, true));
std::string plaintext_response = "0140c8";
absl::StatusOr<std::string> encrypted_response =
instance->EncryptResponse(plaintext_response, true);
QUICHE_EXPECT_OK(encrypted_response);
EXPECT_EQ(
instance->EncryptResponse(plaintext_response, false).status().code(),
absl::StatusCode::kInvalidArgument);
}
TEST(ChunkedObliviousHttpGateway, EncryptingBeforeDecryptingFails) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string plaintext_response = "0140c8";
EXPECT_EQ(
instance->EncryptResponse(plaintext_response, false).status().code(),
absl::StatusCode::kInternal);
}
TEST(ChunkedObliviousHttpGateway, EncryptionErrorMarksGatewayInvalid) {
TestChunkHandler chunk_handler;
auto instance = CreateChunkedObliviousHttpGateway(chunk_handler);
std::string plaintext_response = "0140c8";
EXPECT_EQ(
instance->EncryptResponse(plaintext_response, false).status().code(),
absl::StatusCode::kInternal);
EXPECT_EQ(
instance->EncryptResponse(plaintext_response, false).status().message(),
"Encrypting is marked as invalid.");
}
TEST(ObliviousHttpGateway,
TestHandlingMultipleResponsesForMultipleRequestsWithSingleInstance) {
auto instance = ObliviousHttpGateway::Create(
GetHpkePrivateKey(),
GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
QuicheRandom::GetInstance());
// Setup contexts first.
std::string encrypted_request_1_bytes;
ASSERT_TRUE(
absl::HexStringToBytes("010020000100025f20b60306b61ad9ecad389acd752ca75c4"
"e2969469809fe3d84aae137"
"f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c"
"8ea6c10592594845f",
&encrypted_request_1_bytes));
auto decrypted_request_1 =
instance->DecryptObliviousHttpRequest(encrypted_request_1_bytes);
ASSERT_TRUE(decrypted_request_1.ok());
std::string encrypted_request_2_bytes;
ASSERT_TRUE(
absl::HexStringToBytes("01002000010002285ebc2fcad72cc91b378050cac29a62fee"
"a9cd97829335ee9fc87e672"
"4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5"
"d922918865a0a447a",
&encrypted_request_2_bytes));
auto decrypted_request_2 =
instance->DecryptObliviousHttpRequest(encrypted_request_2_bytes);
ASSERT_TRUE(decrypted_request_2.ok());
// Extract contexts and handle the response for each corresponding request.
auto oblivious_request_context_1 =
std::move(decrypted_request_1.value()).ReleaseContext();
auto encrypted_response_1 = instance->CreateObliviousHttpResponse(
"test response 1", oblivious_request_context_1);
ASSERT_TRUE(encrypted_response_1.ok());
ASSERT_FALSE(encrypted_response_1->EncapsulateAndSerialize().empty());
auto oblivious_request_context_2 =
std::move(decrypted_request_2.value()).ReleaseContext();
auto encrypted_response_2 = instance->CreateObliviousHttpResponse(
"test response 2", oblivious_request_context_2);
ASSERT_TRUE(encrypted_response_2.ok());
ASSERT_FALSE(encrypted_response_2->EncapsulateAndSerialize().empty());
}
TEST(ObliviousHttpGateway, TestWithMultipleThreads) {
class TestQuicheThread : public QuicheThread {
public:
TestQuicheThread(const ObliviousHttpGateway& gateway_receiver,
std::string request_payload, std::string response_payload)
: QuicheThread("gateway_thread"),
gateway_receiver_(gateway_receiver),
request_payload_(request_payload),
response_payload_(response_payload) {}
protected:
void Run() override {
auto decrypted_request =
gateway_receiver_.DecryptObliviousHttpRequest(request_payload_);
ASSERT_TRUE(decrypted_request.ok());
ASSERT_FALSE(decrypted_request->GetPlaintextData().empty());
auto gateway_request_context =
std::move(decrypted_request.value()).ReleaseContext();
auto encrypted_response = gateway_receiver_.CreateObliviousHttpResponse(
response_payload_, gateway_request_context);
ASSERT_TRUE(encrypted_response.ok());
ASSERT_FALSE(encrypted_response->EncapsulateAndSerialize().empty());
}
private:
const ObliviousHttpGateway& gateway_receiver_;
std::string request_payload_, response_payload_;
};
auto gateway_receiver = ObliviousHttpGateway::Create(
GetHpkePrivateKey(),
GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
QuicheRandom::GetInstance());
std::string request_payload_1;
ASSERT_TRUE(
absl::HexStringToBytes("010020000100025f20b60306b61ad9ecad389acd752ca75c4"
"e2969469809fe3d84aae137"
"f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c"
"8ea6c10592594845f",
&request_payload_1));
TestQuicheThread t1(*gateway_receiver, request_payload_1, "test response 1");
std::string request_payload_2;
ASSERT_TRUE(
absl::HexStringToBytes("01002000010002285ebc2fcad72cc91b378050cac29a62fee"
"a9cd97829335ee9fc87e672"
"4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5"
"d922918865a0a447a",
&request_payload_2));
TestQuicheThread t2(*gateway_receiver, request_payload_2, "test response 2");
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
} // namespace
} // namespace quiche