Refactor OHTTP buffers for chunked client implementation.
It refactors `ObliviousHttpRequest` to expose `CreateHpkeSenderContext`, `EncryptChunk`, and `GetEncapsulatedKey`. Also exposes `DecryptChunk` in `ObliviousHttpResponse`. These will be used for client chunks implementation.
PiperOrigin-RevId: 834968917
diff --git a/quiche/oblivious_http/buffers/oblivious_http_request.cc b/quiche/oblivious_http/buffers/oblivious_http_request.cc
index 562833f..f6d3803 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_request.cc
+++ b/quiche/oblivious_http/buffers/oblivious_http_request.cc
@@ -101,10 +101,31 @@
if (plaintext_payload.empty() || hpke_public_key.empty()) {
return absl::InvalidArgumentError("Invalid input.");
}
- // Initialize HPKE key and context.
- bssl::UniquePtr<EVP_HPKE_KEY> client_key(EVP_HPKE_KEY_new());
- if (client_key == nullptr) {
- return SslErrorAsStatus("Failed to initialize HPKE Client Key.");
+ absl::StatusOr<Context> context = CreateHpkeSenderContext(
+ hpke_public_key, ohttp_key_config, seed, request_label);
+ if (!context.ok()) {
+ return context.status();
+ }
+ std::string encapsulated_key = context->encapsulated_key_;
+ // EncryptChunk with `is_final_chunk` set to false is the same implementation
+ // as encrypting the full request.
+ absl::StatusOr<std::string> ciphertext =
+ EncryptChunk(plaintext_payload, *context, /*is_final_chunk=*/false);
+ if (!ciphertext.ok()) {
+ return ciphertext.status();
+ }
+ return ObliviousHttpRequest(
+ std::move(context->hpke_context_), std::move(encapsulated_key),
+ ohttp_key_config, std::move(*ciphertext), std::move(plaintext_payload));
+}
+
+absl::StatusOr<ObliviousHttpRequest::Context>
+ObliviousHttpRequest::CreateHpkeSenderContext(
+ absl::string_view hpke_public_key,
+ const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
+ absl::string_view seed, absl::string_view request_label) {
+ if (hpke_public_key.empty()) {
+ return absl::InvalidArgumentError("HPKE public key is empty.");
}
bssl::UniquePtr<EVP_HPKE_CTX> client_ctx(EVP_HPKE_CTX_new());
if (client_ctx == nullptr) {
@@ -144,30 +165,45 @@
}
}
encapsulated_key.resize(enc_len);
+
+ return Context(std::move(client_ctx), std::move(encapsulated_key));
+}
+
+absl::StatusOr<std::string> ObliviousHttpRequest::EncryptChunk(
+ absl::string_view plaintext_payload, const Context& context,
+ bool is_final_chunk) {
+ if (plaintext_payload.empty() && !is_final_chunk) {
+ return absl::InvalidArgumentError("Invalid input.");
+ }
+
+ uint8_t* ad = nullptr;
+ size_t ad_len = 0;
+ if (is_final_chunk) {
+ ad = const_cast<uint8_t*>(kFinalAdBytes);
+ ad_len = sizeof(kFinalAdBytes);
+ }
+
std::string ciphertext(
- plaintext_payload.size() + EVP_HPKE_CTX_max_overhead(client_ctx.get()),
+ plaintext_payload.size() +
+ EVP_HPKE_CTX_max_overhead(context.hpke_context_.get()),
'\0');
size_t ciphertext_len;
if (!EVP_HPKE_CTX_seal(
- client_ctx.get(), reinterpret_cast<uint8_t*>(ciphertext.data()),
- &ciphertext_len, ciphertext.size(),
+ context.hpke_context_.get(),
+ reinterpret_cast<uint8_t*>(ciphertext.data()), &ciphertext_len,
+ ciphertext.size(),
reinterpret_cast<const uint8_t*>(plaintext_payload.data()),
- plaintext_payload.size(), nullptr, 0)) {
- return SslErrorAsStatus(
- "Failed to encrypt plaintext_payload with given public key param "
- "hpke_public_key.");
+ plaintext_payload.size(), ad, ad_len)) {
+ return SslErrorAsStatus("Failed to encrypt plaintext_payload.");
}
ciphertext.resize(ciphertext_len);
- if (encapsulated_key.empty() || ciphertext.empty()) {
+ if (context.encapsulated_key_.empty() || ciphertext.empty()) {
return absl::InternalError(absl::StrCat(
- "Failed to generate required data: ",
- (encapsulated_key.empty() ? "encapsulated key is empty" : ""),
- (ciphertext.empty() ? "encrypted data is empty" : ""), "."));
+ "Failed to generate required data:",
+ (context.encapsulated_key_.empty() ? " encapsulated key is empty" : ""),
+ (ciphertext.empty() ? " encrypted data is empty" : ""), "."));
}
-
- return ObliviousHttpRequest(
- std::move(client_ctx), std::move(encapsulated_key), ohttp_key_config,
- std::move(ciphertext), std::move(plaintext_payload));
+ return ciphertext;
}
// Request Serialize.
diff --git a/quiche/oblivious_http/buffers/oblivious_http_request.h b/quiche/oblivious_http/buffers/oblivious_http_request.h
index 3293e4a..cdaa69b 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_request.h
+++ b/quiche/oblivious_http/buffers/oblivious_http_request.h
@@ -31,6 +31,8 @@
Context(Context&& other) = default;
Context& operator=(Context&& other) = default;
+ std::string GetEncapsulatedKey() const { return encapsulated_key_; }
+
private:
explicit Context(bssl::UniquePtr<EVP_HPKE_CTX> hpke_context,
std::string encapsulated_key);
@@ -112,7 +114,20 @@
const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
absl::string_view request_label);
- // Decrypts an encrypted chunk.
+ // Creates the client's HPKE sender context.
+ static absl::StatusOr<Context> CreateHpkeSenderContext(
+ absl::string_view hpke_public_key,
+ const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
+ absl::string_view seed, absl::string_view request_label);
+
+ // Encrypts a chunk of plaintext. If `is_final_chunk` is true, the chunk will
+ // be encrypted with a final AAD.
+ static absl::StatusOr<std::string> EncryptChunk(
+ absl::string_view plaintext_payload, const Context& context,
+ bool is_final_chunk);
+
+ // Decrypts an encrypted chunk. If `is_final_chunk` is true, the chunk will
+ // be decrypted with a final AAD.
static absl::StatusOr<std::string> DecryptChunk(
Context& context, absl::string_view encrypted_chunk, bool is_final_chunk);
diff --git a/quiche/oblivious_http/buffers/oblivious_http_request_test.cc b/quiche/oblivious_http/buffers/oblivious_http_request_test.cc
index 8e5eb69..3b97655 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_request_test.cc
+++ b/quiche/oblivious_http/buffers/oblivious_http_request_test.cc
@@ -66,12 +66,12 @@
bssl::UniquePtr<EVP_HPKE_KEY> ConstructHpkeKey(
absl::string_view hpke_key,
- const ObliviousHttpHeaderKeyConfig &ohttp_key_config) {
+ const ObliviousHttpHeaderKeyConfig& ohttp_key_config) {
bssl::UniquePtr<EVP_HPKE_KEY> bssl_hpke_key(EVP_HPKE_KEY_new());
EXPECT_NE(bssl_hpke_key, nullptr);
EXPECT_TRUE(EVP_HPKE_KEY_init(
bssl_hpke_key.get(), ohttp_key_config.GetHpkeKem(),
- reinterpret_cast<const uint8_t *>(hpke_key.data()), hpke_key.size()));
+ reinterpret_cast<const uint8_t*>(hpke_key.data()), hpke_key.size()));
return bssl_hpke_key;
}
@@ -474,4 +474,60 @@
ohttp_key_config);
EXPECT_EQ(decapsulate.status().code(), absl::StatusCode::kInvalidArgument);
}
+
+TEST(ObliviousHttpRequest, CreateHpkeSenderContext) {
+ auto ohttp_key_config =
+ GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
+ EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM);
+ absl::StatusOr<ObliviousHttpRequest::Context> context =
+ ObliviousHttpRequest::CreateHpkeSenderContext(
+ GetHpkePublicKey(), ohttp_key_config, GetSeed(),
+ ObliviousHttpHeaderKeyConfig::kChunkedOhttpRequestLabel);
+ QUICHE_EXPECT_OK(context);
+}
+
+TEST(ObliviousHttpRequest, EncryptChunkAndDecryptChunkSuccess) {
+ ObliviousHttpHeaderKeyConfig ohttp_key_config =
+ GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
+ EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM);
+ absl::StatusOr<ObliviousHttpRequest::Context> sender_ctx =
+ ObliviousHttpRequest::CreateHpkeSenderContext(
+ GetHpkePublicKey(), ohttp_key_config, GetSeed(),
+ ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel);
+ QUICHE_EXPECT_OK(sender_ctx);
+ if (!sender_ctx.ok()) {
+ return;
+ }
+
+ std::string plaintext_chunk = "test_chunk";
+ absl::StatusOr<std::string> encrypted_chunk =
+ ObliviousHttpRequest::EncryptChunk(plaintext_chunk, *sender_ctx,
+ /*is_final_chunk=*/false);
+ QUICHE_EXPECT_OK(encrypted_chunk);
+ if (!encrypted_chunk.ok()) {
+ return;
+ }
+
+ bssl::UniquePtr<EVP_HPKE_KEY> hpke_key =
+ ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config);
+ std::string request_header = ohttp_key_config.SerializeOhttpPayloadHeader();
+ std::string oblivious_http_request_header =
+ absl::StrCat(request_header, sender_ctx->GetEncapsulatedKey());
+ QuicheDataReader reader(oblivious_http_request_header);
+ auto receiver_ctx = ObliviousHttpRequest::DecodeEncapsulatedRequestHeader(
+ reader, *hpke_key, ohttp_key_config,
+ ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel);
+ QUICHE_EXPECT_OK(receiver_ctx);
+ if (!receiver_ctx.ok()) {
+ return;
+ }
+
+ auto decrypted_chunk = ObliviousHttpRequest::DecryptChunk(
+ *receiver_ctx, *encrypted_chunk, /*is_final_chunk=*/false);
+ QUICHE_EXPECT_OK(decrypted_chunk);
+ if (!decrypted_chunk.ok()) {
+ return;
+ }
+ EXPECT_EQ(*decrypted_chunk, plaintext_chunk);
+}
} // namespace quiche
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response.cc b/quiche/oblivious_http/buffers/oblivious_http_response.cc
index 321415b..ed668c3 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_response.cc
+++ b/quiche/oblivious_http/buffers/oblivious_http_response.cc
@@ -78,37 +78,26 @@
absl::string_view encrypted_response =
absl::string_view(encrypted_data).substr(secret_len);
- // Steps (1, 3 to 5) + AEAD context SetUp before 6th step is performed in
- // CommonOperations.
- auto common_ops_st = CommonOperationsToEncapDecap(
- response_nonce, oblivious_http_request_context, resp_label,
- aead_params_st.value().aead_key_len,
- aead_params_st.value().aead_nonce_len, aead_params_st.value().secret_len);
- if (!common_ops_st.ok()) {
- return common_ops_st.status();
+ absl::StatusOr<AeadContextData> aead_context_data =
+ GetAeadContextData(oblivious_http_request_context, *aead_params_st,
+ resp_label, response_nonce);
+ if (!aead_context_data.ok()) {
+ return aead_context_data.status();
}
- std::string decrypted(encrypted_response.size(), '\0');
- size_t decrypted_len;
-
// Decrypt with initialized AEAD context.
// response, error = Open(aead_key, aead_nonce, "", ct)
// https://www.rfc-editor.org/rfc/rfc9458.html#section-4.4-6
- if (!EVP_AEAD_CTX_open(
- common_ops_st.value().aead_ctx.get(),
- reinterpret_cast<uint8_t*>(decrypted.data()), &decrypted_len,
- decrypted.size(),
- reinterpret_cast<const uint8_t*>(
- common_ops_st.value().aead_nonce.data()),
- aead_params_st.value().aead_nonce_len,
- reinterpret_cast<const uint8_t*>(encrypted_response.data()),
- encrypted_response.size(), nullptr, 0)) {
- return SslErrorAsStatus(
- "Failed to decrypt the response with derived AEAD key and nonce.");
+ // DecryptChunk with `is_final_chunk` as false is the same implementation as
+ // decrypting the full encrypted response.
+ absl::StatusOr<std::string> decrypted =
+ DecryptChunk(encrypted_response, *aead_context_data,
+ aead_context_data->aead_nonce, /*is_final_chunk=*/false);
+ if (!decrypted.ok()) {
+ return decrypted.status();
}
- decrypted.resize(decrypted_len);
ObliviousHttpResponse oblivious_response(std::move(encrypted_data),
- std::move(decrypted));
+ std::move(*decrypted));
return oblivious_response;
}
@@ -407,6 +396,33 @@
return encrypted_data;
}
+absl::StatusOr<std::string> ObliviousHttpResponse::DecryptChunk(
+ absl::string_view encrypted_chunk, const AeadContextData& aead_context_data,
+ absl::string_view chunk_nonce, bool is_final_chunk) {
+ uint8_t* ad = nullptr;
+ size_t ad_len = 0;
+ if (is_final_chunk) {
+ ad = const_cast<uint8_t*>(kFinalAdBytes);
+ ad_len = sizeof(kFinalAdBytes);
+ }
+
+ std::string decrypted(encrypted_chunk.size(), '\0');
+ size_t decrypted_len;
+ if (!EVP_AEAD_CTX_open(
+ aead_context_data.aead_ctx.get(),
+ reinterpret_cast<uint8_t*>(decrypted.data()), &decrypted_len,
+ decrypted.size(),
+ reinterpret_cast<const uint8_t*>(chunk_nonce.data()),
+ aead_context_data.aead_nonce.size(),
+ reinterpret_cast<const uint8_t*>(encrypted_chunk.data()),
+ encrypted_chunk.size(), ad, ad_len)) {
+ return SslErrorAsStatus(
+ "Failed to decrypt the response with derived AEAD key.");
+ }
+ decrypted.resize(decrypted_len);
+ return decrypted;
+}
+
absl::StatusOr<ObliviousHttpResponse::ChunkCounter>
ObliviousHttpResponse::ChunkCounter::Create(std::string nonce) {
if (nonce.empty()) {
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response.h b/quiche/oblivious_http/buffers/oblivious_http_response.h
index 29b6763..508f2c0 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_response.h
+++ b/quiche/oblivious_http/buffers/oblivious_http_response.h
@@ -106,6 +106,11 @@
absl::string_view plaintext_payload, absl::string_view chunk_nonce,
bool is_final_chunk);
+ static absl::StatusOr<std::string> DecryptChunk(
+ absl::string_view encrypted_chunk,
+ const AeadContextData& aead_context_data, absl::string_view chunk_nonce,
+ bool is_final_chunk);
+
// Generic Usecase : server-side calls this method in the context of Response
// to serialize OHTTP response that will be returned to client-side.
// Returns serialized OHTTP response bytestring.
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response_test.cc b/quiche/oblivious_http/buffers/oblivious_http_response_test.cc
index 8c0060b..5e13054 100644
--- a/quiche/oblivious_http/buffers/oblivious_http_response_test.cc
+++ b/quiche/oblivious_http/buffers/oblivious_http_response_test.cc
@@ -24,6 +24,19 @@
namespace quiche {
namespace {
+
+// Example from
+// https://www.ietf.org/archive/id/draft-ietf-ohai-chunked-ohttp-06.html#name-example
+constexpr absl::string_view kChunkNonce1Hex = "fead854635d2d5527d64f546";
+constexpr absl::string_view kEncryptedChunk1Hex =
+ "79bf1cc87fa0e2c02de4546945aa3d1e48";
+constexpr absl::string_view kChunkNonce2Hex = "fead854635d2d5527d64f547";
+constexpr absl::string_view kEncryptedChunk2Hex =
+ "b348b5bd4c594c16b6170b07b475845d1f32";
+constexpr absl::string_view kChunkNonce3Hex = "fead854635d2d5527d64f544";
+constexpr absl::string_view kEncryptedChunk3Hex =
+ "ed9d8a796617a5b27265f4d73247f639";
+
std::string GetHpkePrivateKey() {
absl::string_view hpke_key_hex =
"b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1";
@@ -77,12 +90,12 @@
.SerializeRecipientContextInfo();
EXPECT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
- client_ctx.get(), reinterpret_cast<uint8_t *>(encapsulated_key.data()),
+ client_ctx.get(), reinterpret_cast<uint8_t*>(encapsulated_key.data()),
&enc_len, encapsulated_key.size(), EVP_hpke_x25519_hkdf_sha256(),
EVP_hpke_hkdf_sha256(), EVP_hpke_aes_256_gcm(),
- reinterpret_cast<const uint8_t *>(GetHpkePublicKey().data()),
- GetHpkePublicKey().size(), reinterpret_cast<const uint8_t *>(info.data()),
- info.size(), reinterpret_cast<const uint8_t *>(GetSeed().data()),
+ reinterpret_cast<const uint8_t*>(GetHpkePublicKey().data()),
+ GetHpkePublicKey().size(), reinterpret_cast<const uint8_t*>(info.data()),
+ info.size(), reinterpret_cast<const uint8_t*>(GetSeed().data()),
GetSeed().size()));
encapsulated_key.resize(enc_len);
EXPECT_EQ(encapsulated_key, GetSeededEncapsulatedKey());
@@ -91,12 +104,12 @@
bssl::UniquePtr<EVP_HPKE_KEY> ConstructHpkeKey(
absl::string_view hpke_key,
- const ObliviousHttpHeaderKeyConfig &ohttp_key_config) {
+ const ObliviousHttpHeaderKeyConfig& ohttp_key_config) {
bssl::UniquePtr<EVP_HPKE_KEY> bssl_hpke_key(EVP_HPKE_KEY_new());
EXPECT_NE(bssl_hpke_key, nullptr);
EXPECT_TRUE(EVP_HPKE_KEY_init(
bssl_hpke_key.get(), ohttp_key_config.GetHpkeKem(),
- reinterpret_cast<const uint8_t *>(hpke_key.data()), hpke_key.size()));
+ reinterpret_cast<const uint8_t*>(hpke_key.data()), hpke_key.size()));
return bssl_hpke_key;
}
@@ -128,7 +141,7 @@
TestQuicheRandom(char seed) : seed_(seed) {}
~TestQuicheRandom() override {}
- void RandBytes(void *data, size_t len) override { memset(data, seed_, len); }
+ void RandBytes(void* data, size_t len) override { memset(data, seed_, len); }
uint64_t RandUint64() override {
uint64_t random_int;
@@ -136,7 +149,7 @@
return random_int;
}
- void InsecureRandBytes(void *data, size_t len) override {
+ void InsecureRandBytes(void* data, size_t len) override {
return RandBytes(data, len);
}
uint64_t InsecureRandUint64() override { return RandUint64(); }
@@ -145,9 +158,9 @@
char seed_;
};
-size_t GetResponseNonceLength(const EVP_HPKE_CTX &hpke_context) {
+size_t GetResponseNonceLength(const EVP_HPKE_CTX& hpke_context) {
EXPECT_NE(&hpke_context, nullptr);
- const EVP_AEAD *evp_hpke_aead =
+ const EVP_AEAD* evp_hpke_aead =
EVP_HPKE_AEAD_aead(EVP_HPKE_CTX_aead(&hpke_context));
EXPECT_NE(evp_hpke_aead, nullptr);
// Nk = [AEAD key len], is determined by BSSL.
@@ -288,39 +301,98 @@
std::string plaintext_payload_bytes;
EXPECT_TRUE(
absl::HexStringToBytes(plaintext_payload, &plaintext_payload_bytes));
- std::string chunk_nonce = "fead854635d2d5527d64f546";
std::string chunk_nonce_bytes;
- EXPECT_TRUE(absl::HexStringToBytes(chunk_nonce, &chunk_nonce_bytes));
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce1Hex, &chunk_nonce_bytes));
auto encrypted_chunk = ObliviousHttpResponse::EncryptChunk(
context, aead_context_data, plaintext_payload_bytes, chunk_nonce_bytes,
/*is_final_chunk=*/false);
QUICHE_EXPECT_OK(encrypted_chunk);
std::string encrypted_chunk_hex = absl::BytesToHexString(*encrypted_chunk);
- EXPECT_EQ(encrypted_chunk_hex, "79bf1cc87fa0e2c02de4546945aa3d1e48");
+ EXPECT_EQ(encrypted_chunk_hex, kEncryptedChunk1Hex);
plaintext_payload = "40c8";
EXPECT_TRUE(
absl::HexStringToBytes(plaintext_payload, &plaintext_payload_bytes));
- chunk_nonce = "fead854635d2d5527d64f547";
- EXPECT_TRUE(absl::HexStringToBytes(chunk_nonce, &chunk_nonce_bytes));
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce2Hex, &chunk_nonce_bytes));
encrypted_chunk = ObliviousHttpResponse::EncryptChunk(
context, aead_context_data, plaintext_payload_bytes, chunk_nonce_bytes,
/*is_final_chunk=*/false);
QUICHE_EXPECT_OK(encrypted_chunk);
encrypted_chunk_hex = absl::BytesToHexString(*encrypted_chunk);
- EXPECT_EQ(encrypted_chunk_hex, "b348b5bd4c594c16b6170b07b475845d1f32");
+ EXPECT_EQ(encrypted_chunk_hex, kEncryptedChunk2Hex);
- chunk_nonce = "fead854635d2d5527d64f544";
- EXPECT_TRUE(absl::HexStringToBytes(chunk_nonce, &chunk_nonce_bytes));
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce3Hex, &chunk_nonce_bytes));
encrypted_chunk = ObliviousHttpResponse::EncryptChunk(
context, aead_context_data, /*plaintext_payload=*/"", chunk_nonce_bytes,
/*is_final_chunk=*/true);
QUICHE_EXPECT_OK(encrypted_chunk);
encrypted_chunk_hex = absl::BytesToHexString(*encrypted_chunk);
- EXPECT_EQ(encrypted_chunk_hex, "ed9d8a796617a5b27265f4d73247f639");
+ EXPECT_EQ(encrypted_chunk_hex, kEncryptedChunk3Hex);
+}
+
+TEST(ObliviousHttpResponse, TestDecryptChunks) {
+ absl::StatusOr<EncryptChunkTestParams> test_params = SetUpEncryptChunkTest();
+ QUICHE_EXPECT_OK(test_params);
+ if (!test_params.ok()) {
+ return;
+ }
+ auto& [context, aead_params, aead_context_data] = *test_params;
+
+ // Chunk 1 decryption
+ std::string encrypted_chunk1_bytes;
+ EXPECT_TRUE(
+ absl::HexStringToBytes(kEncryptedChunk1Hex, &encrypted_chunk1_bytes));
+ std::string chunk_nonce1_bytes;
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce1Hex, &chunk_nonce1_bytes));
+ auto decrypted_chunk1 = ObliviousHttpResponse::DecryptChunk(
+ encrypted_chunk1_bytes, aead_context_data, chunk_nonce1_bytes,
+ /*is_final_chunk=*/false);
+ QUICHE_EXPECT_OK(decrypted_chunk1);
+ if (!decrypted_chunk1.ok()) {
+ return;
+ }
+ std::string expected_plaintext1_hex = "01";
+ std::string expected_plaintext1_bytes;
+ EXPECT_TRUE(absl::HexStringToBytes(expected_plaintext1_hex,
+ &expected_plaintext1_bytes));
+ EXPECT_EQ(*decrypted_chunk1, expected_plaintext1_bytes);
+
+ // Chunk 2 decryption
+ std::string encrypted_chunk2_bytes;
+ EXPECT_TRUE(
+ absl::HexStringToBytes(kEncryptedChunk2Hex, &encrypted_chunk2_bytes));
+ std::string chunk_nonce2_bytes;
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce2Hex, &chunk_nonce2_bytes));
+ auto decrypted_chunk2 = ObliviousHttpResponse::DecryptChunk(
+ encrypted_chunk2_bytes, aead_context_data, chunk_nonce2_bytes,
+ /*is_final_chunk=*/false);
+ QUICHE_EXPECT_OK(decrypted_chunk2);
+ if (!decrypted_chunk2.ok()) {
+ return;
+ }
+ std::string expected_plaintext2_hex = "40c8";
+ std::string expected_plaintext2_bytes;
+ EXPECT_TRUE(absl::HexStringToBytes(expected_plaintext2_hex,
+ &expected_plaintext2_bytes));
+ EXPECT_EQ(*decrypted_chunk2, expected_plaintext2_bytes);
+
+ // Chunk 3 decryption
+ std::string encrypted_chunk3_bytes;
+ EXPECT_TRUE(
+ absl::HexStringToBytes(kEncryptedChunk3Hex, &encrypted_chunk3_bytes));
+ std::string chunk_nonce3_bytes;
+ EXPECT_TRUE(absl::HexStringToBytes(kChunkNonce3Hex, &chunk_nonce3_bytes));
+ auto decrypted_chunk3 = ObliviousHttpResponse::DecryptChunk(
+ encrypted_chunk3_bytes, aead_context_data, chunk_nonce3_bytes,
+ /*is_final_chunk=*/true);
+ QUICHE_EXPECT_OK(decrypted_chunk3);
+ if (!decrypted_chunk3.ok()) {
+ return;
+ }
+ EXPECT_EQ(*decrypted_chunk3, "");
}
TEST(OblviousHttpResponse, EncryptNonFinalChunkWithEmptyPayloadError) {