Add OHTTP support for custom request and response labels for encryption key generation. This will be required for protocols that reuse the OHTTP encapsulation format. PiperOrigin-RevId: 552943211
diff --git a/quiche/oblivious_http/buffers/oblivious_http_integration_test.cc b/quiche/oblivious_http/buffers/oblivious_http_integration_test.cc index 5d00efc..b9e17f6 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_integration_test.cc +++ b/quiche/oblivious_http/buffers/oblivious_http_integration_test.cc
@@ -105,4 +105,55 @@ [](const testing::TestParamInfo<ObliviousHttpParameterizedTest::ParamType> &info) { return info.param.test_case_name; }); +TEST(ObliviousHttpIntegrationTest, TestWithCustomRequestResponseLabels) { + const std::string kRequestLabel = "test_request_label"; + const std::string kResponseLabel = "test_response_label"; + + ObliviousHttpResponseTestStrings test = {"", 4, "test_request_plaintext", + "test_response_plaintext"}; + + auto ohttp_key_config = + GetOhttpKeyConfig(test.key_id, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, + EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM); + // Round-trip request flow. + auto client_req_encap = ObliviousHttpRequest::CreateClientObliviousRequest( + test.request_plaintext, GetHpkePublicKey(), ohttp_key_config, + kRequestLabel); + EXPECT_TRUE(client_req_encap.ok()); + ASSERT_FALSE(client_req_encap->EncapsulateAndSerialize().empty()); + auto server_req_decap = ObliviousHttpRequest::CreateServerObliviousRequest( + client_req_encap->EncapsulateAndSerialize(), + *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config)), + ohttp_key_config, kRequestLabel); + EXPECT_TRUE(server_req_decap.ok()); + EXPECT_EQ(server_req_decap->GetPlaintextData(), test.request_plaintext); + + auto failed_server_req_decap = + ObliviousHttpRequest::CreateServerObliviousRequest( + client_req_encap->EncapsulateAndSerialize(), + *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config)), + ohttp_key_config); + EXPECT_FALSE(failed_server_req_decap.ok()); + + // Round-trip response flow. + auto server_request_context = + std::move(server_req_decap.value()).ReleaseContext(); + auto server_resp_encap = ObliviousHttpResponse::CreateServerObliviousResponse( + test.response_plaintext, server_request_context, kResponseLabel); + EXPECT_TRUE(server_resp_encap.ok()); + ASSERT_FALSE(server_resp_encap->EncapsulateAndSerialize().empty()); + auto client_request_context = + std::move(client_req_encap.value()).ReleaseContext(); + auto client_resp_decap = ObliviousHttpResponse::CreateClientObliviousResponse( + server_resp_encap->EncapsulateAndSerialize(), client_request_context, + kResponseLabel); + EXPECT_TRUE(client_resp_decap.ok()); + EXPECT_EQ(client_resp_decap->GetPlaintextData(), test.response_plaintext); + + auto failed_client_resp_decap = + ObliviousHttpResponse::CreateClientObliviousResponse( + server_resp_encap->EncapsulateAndSerialize(), client_request_context); + EXPECT_FALSE(failed_client_resp_decap.ok()); +} + } // namespace quiche
diff --git a/quiche/oblivious_http/buffers/oblivious_http_request.cc b/quiche/oblivious_http/buffers/oblivious_http_request.cc index 7c0b2ea..fec5c8f 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_request.cc +++ b/quiche/oblivious_http/buffers/oblivious_http_request.cc
@@ -40,7 +40,8 @@ absl::StatusOr<ObliviousHttpRequest> ObliviousHttpRequest::CreateServerObliviousRequest( absl::string_view encrypted_data, const EVP_HPKE_KEY& gateway_key, - const ObliviousHttpHeaderKeyConfig& ohttp_key_config) { + const ObliviousHttpHeaderKeyConfig& ohttp_key_config, + absl::string_view request_label) { if (EVP_HPKE_KEY_kem(&gateway_key) == nullptr) { return absl::InvalidArgumentError( "Invalid input param. Failed to import gateway_key."); @@ -65,7 +66,8 @@ "Failed to extract encapsulation key of expected len=", enc_key_len, "from payload.")); } - std::string info = ohttp_key_config.SerializeRecipientContextInfo(); + std::string info = + ohttp_key_config.SerializeRecipientContextInfo(request_label); if (!EVP_HPKE_CTX_setup_recipient( gateway_ctx.get(), &gateway_key, ohttp_key_config.GetHpkeKdf(), ohttp_key_config.GetHpkeAead(), @@ -97,24 +99,26 @@ absl::StatusOr<ObliviousHttpRequest> ObliviousHttpRequest::CreateClientObliviousRequest( std::string plaintext_payload, absl::string_view hpke_public_key, - const ObliviousHttpHeaderKeyConfig& ohttp_key_config) { + const ObliviousHttpHeaderKeyConfig& ohttp_key_config, + absl::string_view request_label) { return EncapsulateWithSeed(std::move(plaintext_payload), hpke_public_key, - ohttp_key_config, ""); + ohttp_key_config, /*seed=*/"", request_label); } absl::StatusOr<ObliviousHttpRequest> ObliviousHttpRequest::CreateClientWithSeedForTesting( std::string plaintext_payload, absl::string_view hpke_public_key, const ObliviousHttpHeaderKeyConfig& ohttp_key_config, - absl::string_view seed) { + absl::string_view seed, absl::string_view request_label) { return ObliviousHttpRequest::EncapsulateWithSeed( - std::move(plaintext_payload), hpke_public_key, ohttp_key_config, seed); + std::move(plaintext_payload), hpke_public_key, ohttp_key_config, seed, + request_label); } absl::StatusOr<ObliviousHttpRequest> ObliviousHttpRequest::EncapsulateWithSeed( std::string plaintext_payload, absl::string_view hpke_public_key, const ObliviousHttpHeaderKeyConfig& ohttp_key_config, - absl::string_view seed) { + absl::string_view seed, absl::string_view request_label) { if (plaintext_payload.empty() || hpke_public_key.empty()) { return absl::InvalidArgumentError("Invalid input."); } @@ -130,7 +134,8 @@ // Setup the sender (client) std::string encapsulated_key(EVP_HPKE_MAX_ENC_LENGTH, '\0'); size_t enc_len; - std::string info = ohttp_key_config.SerializeRecipientContextInfo(); + std::string info = + ohttp_key_config.SerializeRecipientContextInfo(request_label); if (seed.empty()) { if (!EVP_HPKE_CTX_setup_sender( client_ctx.get(),
diff --git a/quiche/oblivious_http/buffers/oblivious_http_request.h b/quiche/oblivious_http/buffers/oblivious_http_request.h index 58a555b..0678ff5 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_request.h +++ b/quiche/oblivious_http/buffers/oblivious_http_request.h
@@ -54,20 +54,26 @@ // Generic Usecase : server-side calls this method in the context of Request. static absl::StatusOr<ObliviousHttpRequest> CreateServerObliviousRequest( absl::string_view encrypted_data, const EVP_HPKE_KEY& gateway_key, - const ObliviousHttpHeaderKeyConfig& ohttp_key_config); + const ObliviousHttpHeaderKeyConfig& ohttp_key_config, + absl::string_view request_label = + ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel); // Constructs an OHTTP request for the given `plaintext_payload`. // On success, returns obj that callers will use to `EncapsulateAndSerialize` // OHttp request. static absl::StatusOr<ObliviousHttpRequest> CreateClientObliviousRequest( std::string plaintext_payload, absl::string_view hpke_public_key, - const ObliviousHttpHeaderKeyConfig& ohttp_key_config); + const ObliviousHttpHeaderKeyConfig& ohttp_key_config, + absl::string_view request_label = + ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel); // Same as above but accepts a random number seed for testing. static absl::StatusOr<ObliviousHttpRequest> CreateClientWithSeedForTesting( std::string plaintext_payload, absl::string_view hpke_public_key, const ObliviousHttpHeaderKeyConfig& ohttp_key_config, - absl::string_view seed); + absl::string_view seed, + absl::string_view request_label = + ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel); // Movable. ObliviousHttpRequest(ObliviousHttpRequest&& other) = default; @@ -106,7 +112,7 @@ static absl::StatusOr<ObliviousHttpRequest> EncapsulateWithSeed( std::string plaintext_payload, absl::string_view hpke_public_key, const ObliviousHttpHeaderKeyConfig& ohttp_key_config, - absl::string_view seed); + absl::string_view seed, absl::string_view request_label); // This field will be empty after calling `ReleaseContext()`. absl::optional<Context> oblivious_http_request_context_;
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response.cc b/quiche/oblivious_http/buffers/oblivious_http_response.cc index ea1936d..3496be9 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_response.cc +++ b/quiche/oblivious_http/buffers/oblivious_http_response.cc
@@ -47,7 +47,8 @@ absl::StatusOr<ObliviousHttpResponse> ObliviousHttpResponse::CreateClientObliviousResponse( std::string encrypted_data, - ObliviousHttpRequest::Context& oblivious_http_request_context) { + ObliviousHttpRequest::Context& oblivious_http_request_context, + absl::string_view resp_label) { if (oblivious_http_request_context.hpke_context_ == nullptr) { return absl::FailedPreconditionError( "HPKE context wasn't initialized before proceeding with this Response " @@ -91,7 +92,7 @@ // 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, + 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()) { @@ -131,7 +132,7 @@ ObliviousHttpResponse::CreateServerObliviousResponse( std::string plaintext_payload, ObliviousHttpRequest::Context& oblivious_http_request_context, - QuicheRandom* quiche_random) { + absl::string_view response_label, QuicheRandom* quiche_random) { if (oblivious_http_request_context.hpke_context_ == nullptr) { return absl::FailedPreconditionError( "HPKE context wasn't initialized before proceeding with this Response " @@ -168,7 +169,7 @@ // 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, + response_nonce, oblivious_http_request_context, response_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()) { @@ -246,8 +247,8 @@ ObliviousHttpResponse::CommonOperationsToEncapDecap( absl::string_view response_nonce, ObliviousHttpRequest::Context& oblivious_http_request_context, - const size_t aead_key_len, const size_t aead_nonce_len, - const size_t secret_len) { + absl::string_view resp_label, const size_t aead_key_len, + const size_t aead_nonce_len, const size_t secret_len) { if (response_nonce.empty()) { return absl::InvalidArgumentError("Invalid input params."); } @@ -256,8 +257,6 @@ // key and nonce associated with context. // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.2-2.1 std::string secret(secret_len, '\0'); - absl::string_view resp_label = - ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel; if (!EVP_HPKE_CTX_export(oblivious_http_request_context.hpke_context_.get(), reinterpret_cast<uint8_t*>(secret.data()), secret.size(),
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response.h b/quiche/oblivious_http/buffers/oblivious_http_response.h index 82b388d..2bc7caf 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_response.h +++ b/quiche/oblivious_http/buffers/oblivious_http_response.h
@@ -9,6 +9,7 @@ #include "absl/strings/string_view.h" #include "quiche/common/quiche_random.h" #include "quiche/oblivious_http/buffers/oblivious_http_request.h" +#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h" namespace quiche { @@ -21,7 +22,9 @@ // alive only for the lifetime of this factory method call. static absl::StatusOr<ObliviousHttpResponse> CreateClientObliviousResponse( std::string encrypted_data, - ObliviousHttpRequest::Context& oblivious_http_request_context); + ObliviousHttpRequest::Context& oblivious_http_request_context, + absl::string_view resp_label = + ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel); // Encrypt the input param `plaintext_payload` and create OHttp response using // ObliviousHttpContext context obj that was returned from @@ -39,6 +42,8 @@ static absl::StatusOr<ObliviousHttpResponse> CreateServerObliviousResponse( std::string plaintext_payload, ObliviousHttpRequest::Context& oblivious_http_request_context, + absl::string_view resp_label = + ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel, QuicheRandom* quiche_random = nullptr); // Copyable. @@ -87,8 +92,8 @@ static absl::StatusOr<CommonOperationsResult> CommonOperationsToEncapDecap( absl::string_view response_nonce, ObliviousHttpRequest::Context& oblivious_http_request_context, - const size_t aead_key_len, const size_t aead_nonce_len, - const size_t secret_len); + absl::string_view resp_label, const size_t aead_key_len, + const size_t aead_nonce_len, const size_t secret_len); std::string encrypted_data_; std::string response_plaintext_; };
diff --git a/quiche/oblivious_http/buffers/oblivious_http_response_test.cc b/quiche/oblivious_http/buffers/oblivious_http_response_test.cc index b2147d7..4178c73 100644 --- a/quiche/oblivious_http/buffers/oblivious_http_response_test.cc +++ b/quiche/oblivious_http/buffers/oblivious_http_response_test.cc
@@ -15,6 +15,7 @@ #include "openssl/hpke.h" #include "quiche/common/platform/api/quiche_test.h" #include "quiche/oblivious_http/buffers/oblivious_http_request.h" +#include "quiche/oblivious_http/common/oblivious_http_header_key_config.h" namespace quiche { @@ -190,7 +191,8 @@ std::move(server_seeded_request).ReleaseContext(); auto server_response_encapsulate = ObliviousHttpResponse::CreateServerObliviousResponse( - "test response", server_request_context, &random); + "test response", server_request_context, + ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel, &random); EXPECT_TRUE(server_response_encapsulate.ok()); std::string response_nonce = server_response_encapsulate->EncapsulateAndSerialize().substr(
diff --git a/quiche/oblivious_http/common/oblivious_http_header_key_config.cc b/quiche/oblivious_http/common/oblivious_http_header_key_config.cc index b0103c2..3693268 100644 --- a/quiche/oblivious_http/common/oblivious_http_header_key_config.cc +++ b/quiche/oblivious_http/common/oblivious_http_header_key_config.cc
@@ -115,13 +115,13 @@ return aead.value(); } -std::string ObliviousHttpHeaderKeyConfig::SerializeRecipientContextInfo() - const { +std::string ObliviousHttpHeaderKeyConfig::SerializeRecipientContextInfo( + absl::string_view request_label) const { uint8_t zero_byte = 0x00; - int buf_len = kOhttpRequestLabel.size() + kHeaderLength + sizeof(zero_byte); + int buf_len = request_label.size() + kHeaderLength + sizeof(zero_byte); std::string info(buf_len, '\0'); QuicheDataWriter writer(info.size(), info.data()); - QUICHE_CHECK(writer.WriteStringPiece(kOhttpRequestLabel)); + QUICHE_CHECK(writer.WriteStringPiece(request_label)); QUICHE_CHECK(writer.WriteUInt8(zero_byte)); // Zero byte. QUICHE_CHECK(writer.WriteUInt8(key_id_)); QUICHE_CHECK(writer.WriteUInt16(kem_id_));
diff --git a/quiche/oblivious_http/common/oblivious_http_header_key_config.h b/quiche/oblivious_http/common/oblivious_http_header_key_config.h index 488561b..37599a0 100644 --- a/quiche/oblivious_http/common/oblivious_http_header_key_config.h +++ b/quiche/oblivious_http/common/oblivious_http_header_key_config.h
@@ -55,11 +55,13 @@ uint16_t GetHpkeKdfId() const { return kdf_id_; } uint16_t GetHpkeAeadId() const { return aead_id_; } - // Build HPKE context info ["message/bhttp request", 0x00, keyID(1 byte), + // Build HPKE context info [request_label, 0x00, keyID(1 byte), // kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)] in network byte order and // return a sequence of bytes(bytestring). // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-10 - std::string SerializeRecipientContextInfo() const; + std::string SerializeRecipientContextInfo( + absl::string_view request_label = + ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel) const; // Parses the below Header // [keyID(1 byte), kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)]
diff --git a/quiche/oblivious_http/oblivious_http_gateway.cc b/quiche/oblivious_http/oblivious_http_gateway.cc index b2d2e88..e788369 100644 --- a/quiche/oblivious_http/oblivious_http_gateway.cc +++ b/quiche/oblivious_http/oblivious_http_gateway.cc
@@ -51,17 +51,18 @@ absl::StatusOr<ObliviousHttpRequest> ObliviousHttpGateway::DecryptObliviousHttpRequest( - absl::string_view encrypted_data) const { + absl::string_view encrypted_data, absl::string_view request_label) const { return ObliviousHttpRequest::CreateServerObliviousRequest( - encrypted_data, *(server_hpke_key_), ohttp_key_config_); + encrypted_data, *(server_hpke_key_), ohttp_key_config_, request_label); } absl::StatusOr<ObliviousHttpResponse> ObliviousHttpGateway::CreateObliviousHttpResponse( std::string plaintext_data, - ObliviousHttpRequest::Context& oblivious_http_request_context) const { + ObliviousHttpRequest::Context& oblivious_http_request_context, + absl::string_view response_label) const { return ObliviousHttpResponse::CreateServerObliviousResponse( - std::move(plaintext_data), oblivious_http_request_context, + std::move(plaintext_data), oblivious_http_request_context, response_label, quiche_random_); }
diff --git a/quiche/oblivious_http/oblivious_http_gateway.h b/quiche/oblivious_http/oblivious_http_gateway.h index ae6c746..cd02841 100644 --- a/quiche/oblivious_http/oblivious_http_gateway.h +++ b/quiche/oblivious_http/oblivious_http_gateway.h
@@ -55,7 +55,9 @@ // ohttp_server_object.DecryptObliviousHttpRequest(<encrypted binary http // 2>); absl::StatusOr<ObliviousHttpRequest> DecryptObliviousHttpRequest( - absl::string_view encrypted_data) const; + absl::string_view encrypted_data, + absl::string_view request_label = + ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel) const; // After `DecryptObliviousHttpRequest` operation, callers on server-side will // extract `oblivious_http_request_context` from the returned object @@ -63,7 +65,9 @@ // response flow back to the client. absl::StatusOr<ObliviousHttpResponse> CreateObliviousHttpResponse( std::string plaintext_data, - ObliviousHttpRequest::Context& oblivious_http_request_context) const; + ObliviousHttpRequest::Context& oblivious_http_request_context, + absl::string_view response_label = + ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel) const; private: explicit ObliviousHttpGateway(