blob: 5981c000d5b551d2720d6f6f0dd032339458abf7 [file] [log] [blame]
#include <string>
#include <utility>
#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/test_tools/quiche_test_utils.h"
#include "quiche/oblivious_http/buffers/oblivious_http_request.h"
#include "quiche/oblivious_http/buffers/oblivious_http_response.h"
#include "quiche/oblivious_http/common/oblivious_http_chunk_handler.h"
#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
#include "quiche/oblivious_http/oblivious_http_client.h"
#include "quiche/oblivious_http/oblivious_http_gateway.h"
namespace quiche {
namespace {
constexpr absl::string_view kRequestTestPayload = "request test payload";
constexpr absl::string_view kResponseTestPayload = "response test payload";
constexpr absl::string_view kHpkePrivateKeyHex =
"b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1";
constexpr absl::string_view kHpkePublicKeyHex =
"6d21cfe09fbea5122f9ebc2eb2a69fcc4f06408cd54aac934f012e76fcdcef62";
absl::StatusOr<std::string> GetHpkeKey(absl::string_view hpke_key_hex) {
std::string hpke_key_bytes;
if (!absl::HexStringToBytes(hpke_key_hex, &hpke_key_bytes)) {
return absl::InvalidArgumentError("Invalid HPKE key.");
}
return hpke_key_bytes;
}
class TestChunkHandler : public ObliviousHttpChunkHandler {
public:
absl::Status OnDecryptedChunk(absl::string_view decrypted_chunk) override {
absl::StrAppend(&concatenated_decrypted_chunks_, decrypted_chunk);
return absl::OkStatus();
}
absl::Status OnChunksDone() override {
on_chunks_done_called_ = true;
return absl::OkStatus();
}
std::string GetConcatenatedDecryptedChunks() const {
return concatenated_decrypted_chunks_;
}
bool AreChunksDone() const { return on_chunks_done_called_; }
private:
bool on_chunks_done_called_ = false;
std::string concatenated_decrypted_chunks_;
};
TEST(ObliviousHttpIntegrationTest, ClientAndGatewayRequestResponse) {
absl::StatusOr<ObliviousHttpHeaderKeyConfig> key_config =
ObliviousHttpHeaderKeyConfig::Create(
/*key_id=*/1, /*kem_id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
/*kdf_id=*/EVP_HPKE_HKDF_SHA256, /*aead_id=*/
EVP_HPKE_AES_128_GCM);
QUICHE_ASSERT_OK(key_config);
absl::StatusOr<std::string> hpke_public_key = GetHpkeKey(kHpkePublicKeyHex);
QUICHE_ASSERT_OK(hpke_public_key);
absl::StatusOr<std::string> hpke_private_key = GetHpkeKey(kHpkePrivateKeyHex);
QUICHE_ASSERT_OK(hpke_private_key);
absl::StatusOr<ObliviousHttpClient> client =
ObliviousHttpClient::Create(hpke_public_key.value(), key_config.value());
QUICHE_ASSERT_OK(client);
absl::StatusOr<ObliviousHttpGateway> gateway = ObliviousHttpGateway::Create(
hpke_private_key.value(), key_config.value());
QUICHE_ASSERT_OK(gateway);
// Request encrypt/decrypt
absl::StatusOr<ObliviousHttpRequest> request =
client->CreateObliviousHttpRequest(std::string(kRequestTestPayload));
QUICHE_ASSERT_OK(request);
request =
gateway->DecryptObliviousHttpRequest(request->EncapsulateAndSerialize());
QUICHE_ASSERT_OK(request);
EXPECT_EQ(request->GetPlaintextData(), kRequestTestPayload);
// Response encrypt/decrypt
ObliviousHttpRequest::Context request_context =
std::move(*request).ReleaseContext();
absl::StatusOr<ObliviousHttpResponse> response =
gateway->CreateObliviousHttpResponse(std::string(kResponseTestPayload),
request_context);
QUICHE_ASSERT_OK(response);
response = client->DecryptObliviousHttpResponse(
response->EncapsulateAndSerialize(), request_context);
QUICHE_ASSERT_OK(response);
EXPECT_EQ(response->GetPlaintextData(), kResponseTestPayload);
}
TEST(ObliviousHttpIntegrationTest,
ChunkedClientAndChunkedGatewayRequestResponseChunks) {
absl::StatusOr<ObliviousHttpHeaderKeyConfig> key_config =
ObliviousHttpHeaderKeyConfig::Create(
/*key_id=*/1, /*kem_id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
/*kdf_id=*/EVP_HPKE_HKDF_SHA256, /*aead_id=*/
EVP_HPKE_AES_128_GCM);
QUICHE_ASSERT_OK(key_config);
absl::StatusOr<std::string> hpke_public_key = GetHpkeKey(kHpkePublicKeyHex);
QUICHE_ASSERT_OK(hpke_public_key);
absl::StatusOr<std::string> hpke_private_key = GetHpkeKey(kHpkePrivateKeyHex);
QUICHE_ASSERT_OK(hpke_private_key);
TestChunkHandler client_chunk_handler;
absl::StatusOr<ChunkedObliviousHttpClient> chunked_client =
ChunkedObliviousHttpClient::Create(
hpke_public_key.value(), key_config.value(), &client_chunk_handler);
QUICHE_ASSERT_OK(chunked_client);
TestChunkHandler gateway_chunk_handler;
absl::StatusOr<ChunkedObliviousHttpGateway> gateway =
ChunkedObliviousHttpGateway::Create(
hpke_private_key.value(), key_config.value(), &gateway_chunk_handler);
QUICHE_ASSERT_OK(gateway);
// Encrypt request in chunks
absl::string_view request_part1 =
kRequestTestPayload.substr(0, kRequestTestPayload.length() / 2);
absl::string_view request_part2 =
kRequestTestPayload.substr(kRequestTestPayload.length() / 2);
absl::StatusOr<std::string> req_chunk1_encrypted =
chunked_client->EncryptRequestChunk(request_part1,
/*is_final_chunk=*/false);
QUICHE_ASSERT_OK(req_chunk1_encrypted);
absl::StatusOr<std::string> req_chunk2_encrypted =
chunked_client->EncryptRequestChunk(request_part2,
/*is_final_chunk=*/false);
QUICHE_ASSERT_OK(req_chunk2_encrypted);
absl::StatusOr<std::string> req_chunk3_encrypted =
chunked_client->EncryptRequestChunk(/*plaintext_payload=*/"",
/*is_final_chunk=*/true);
QUICHE_ASSERT_OK(req_chunk3_encrypted);
// Decrypt request in chunks
QUICHE_ASSERT_OK(gateway->DecryptRequest(req_chunk1_encrypted.value(),
/*end_stream=*/false));
QUICHE_ASSERT_OK(gateway->DecryptRequest(req_chunk2_encrypted.value(),
/*end_stream=*/false));
QUICHE_ASSERT_OK(gateway->DecryptRequest(req_chunk3_encrypted.value(),
/*end_stream=*/true));
EXPECT_TRUE(gateway_chunk_handler.AreChunksDone());
EXPECT_EQ(gateway_chunk_handler.GetConcatenatedDecryptedChunks(),
kRequestTestPayload);
// Encrypt response in chunks
absl::string_view response_part1 =
kResponseTestPayload.substr(0, kResponseTestPayload.length() / 2);
absl::string_view response_part2 =
kResponseTestPayload.substr(kResponseTestPayload.length() / 2);
absl::StatusOr<std::string> resp_chunk1_encrypted =
gateway->EncryptResponse(response_part1, /*is_final_chunk=*/false);
QUICHE_ASSERT_OK(resp_chunk1_encrypted);
absl::StatusOr<std::string> resp_chunk2_encrypted =
gateway->EncryptResponse(response_part2, /*is_final_chunk=*/false);
QUICHE_ASSERT_OK(resp_chunk2_encrypted);
absl::StatusOr<std::string> resp_chunk3_encrypted = gateway->EncryptResponse(
/*plaintext_payload=*/"", /*is_final_chunk=*/true);
QUICHE_ASSERT_OK(resp_chunk3_encrypted);
// Decrypt response in chunks
QUICHE_ASSERT_OK(
chunked_client->DecryptResponse(resp_chunk1_encrypted.value(),
/*end_stream=*/false));
QUICHE_ASSERT_OK(
chunked_client->DecryptResponse(resp_chunk2_encrypted.value(),
/*end_stream=*/false));
QUICHE_ASSERT_OK(
chunked_client->DecryptResponse(resp_chunk3_encrypted.value(),
/*end_stream=*/true));
EXPECT_TRUE(client_chunk_handler.AreChunksDone());
EXPECT_EQ(client_chunk_handler.GetConcatenatedDecryptedChunks(),
kResponseTestPayload);
}
} // namespace
} // namespace quiche