| #include "quiche/oblivious_http/buffers/oblivious_http_response.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #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/oblivious_http/buffers/oblivious_http_request.h" |
| #include "quiche/oblivious_http/common/oblivious_http_header_key_config.h" |
| |
| namespace quiche { |
| |
| namespace { |
| std::string GetHpkePrivateKey() { |
| absl::string_view hpke_key_hex = |
| "b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1"; |
| return absl::HexStringToBytes(hpke_key_hex); |
| } |
| |
| std::string GetHpkePublicKey() { |
| absl::string_view public_key = |
| "6d21cfe09fbea5122f9ebc2eb2a69fcc4f06408cd54aac934f012e76fcdcef62"; |
| return absl::HexStringToBytes(public_key); |
| } |
| |
| std::string GetSeed() { |
| absl::string_view seed = |
| "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"; |
| return absl::HexStringToBytes(seed); |
| } |
| |
| std::string GetSeededEncapsulatedKey() { |
| absl::string_view encapsulated_key = |
| "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"; |
| return absl::HexStringToBytes(encapsulated_key); |
| } |
| |
| 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 ohttp_key_config.value(); |
| } |
| |
| bssl::UniquePtr<EVP_HPKE_CTX> GetSeededClientContext(uint8_t key_id, |
| uint16_t kem_id, |
| uint16_t kdf_id, |
| uint16_t aead_id) { |
| bssl::UniquePtr<EVP_HPKE_CTX> client_ctx(EVP_HPKE_CTX_new()); |
| std::string encapsulated_key(EVP_HPKE_MAX_ENC_LENGTH, '\0'); |
| size_t enc_len; |
| std::string info = GetOhttpKeyConfig(key_id, kem_id, kdf_id, aead_id) |
| .SerializeRecipientContextInfo(); |
| |
| EXPECT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
| 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()), |
| GetSeed().size())); |
| encapsulated_key.resize(enc_len); |
| EXPECT_EQ(encapsulated_key, GetSeededEncapsulatedKey()); |
| return client_ctx; |
| } |
| |
| bssl::UniquePtr<EVP_HPKE_KEY> ConstructHpkeKey( |
| absl::string_view hpke_key, |
| 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())); |
| return bssl_hpke_key; |
| } |
| |
| ObliviousHttpRequest SetUpObliviousHttpContext(uint8_t key_id, uint16_t kem_id, |
| uint16_t kdf_id, |
| uint16_t aead_id, |
| std::string plaintext) { |
| auto ohttp_key_config = GetOhttpKeyConfig(key_id, kem_id, kdf_id, aead_id); |
| auto client_request_encapsulate = |
| ObliviousHttpRequest::CreateClientWithSeedForTesting( |
| std::move(plaintext), GetHpkePublicKey(), ohttp_key_config, |
| GetSeed()); |
| EXPECT_TRUE(client_request_encapsulate.ok()); |
| auto oblivious_request = |
| client_request_encapsulate->EncapsulateAndSerialize(); |
| auto server_request_decapsulate = |
| ObliviousHttpRequest::CreateServerObliviousRequest( |
| oblivious_request, |
| *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config)), |
| ohttp_key_config); |
| EXPECT_TRUE(server_request_decapsulate.ok()); |
| return std::move(server_request_decapsulate.value()); |
| } |
| |
| // QuicheRandom implementation. |
| // Just fills the buffer with repeated chars that's initialized in seed. |
| class TestQuicheRandom : public QuicheRandom { |
| public: |
| TestQuicheRandom(char seed) : seed_(seed) {} |
| ~TestQuicheRandom() override {} |
| |
| void RandBytes(void *data, size_t len) override { memset(data, seed_, len); } |
| |
| uint64_t RandUint64() override { |
| uint64_t random_int; |
| memset(&random_int, seed_, sizeof(random_int)); |
| return random_int; |
| } |
| |
| void InsecureRandBytes(void *data, size_t len) override { |
| return RandBytes(data, len); |
| } |
| uint64_t InsecureRandUint64() override { return RandUint64(); } |
| |
| private: |
| char seed_; |
| }; |
| |
| size_t GetResponseNonceLength(const EVP_HPKE_CTX &hpke_context) { |
| EXPECT_NE(&hpke_context, nullptr); |
| 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. |
| const size_t aead_key_len = EVP_AEAD_key_length(evp_hpke_aead); |
| // Nn = [AEAD nonce len], is determined by BSSL. |
| const size_t aead_nonce_len = EVP_AEAD_nonce_length(evp_hpke_aead); |
| const size_t secret_len = std::max(aead_key_len, aead_nonce_len); |
| return secret_len; |
| } |
| |
| TEST(ObliviousHttpResponse, TestDecapsulateReceivedResponse) { |
| // Construct encrypted payload with plaintext: "test response" |
| absl::string_view encrypted_response = |
| "39d5b03c02c97e216df444e4681007105974d4df1585aae05e7b53f3ccdb55d51f711d48" |
| "eeefbc1a555d6d928e35df33fd23c23846fa7b083e30692f7b"; |
| auto oblivious_context = |
| SetUpObliviousHttpContext(4, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, |
| EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM, |
| "test") |
| .ReleaseContext(); |
| auto decapsulated = ObliviousHttpResponse::CreateClientObliviousResponse( |
| absl::HexStringToBytes(encrypted_response), oblivious_context); |
| EXPECT_TRUE(decapsulated.ok()); |
| auto decrypted = decapsulated->GetPlaintextData(); |
| EXPECT_EQ(decrypted, "test response"); |
| } |
| } // namespace |
| |
| TEST(ObliviousHttpResponse, EndToEndTestForResponse) { |
| auto oblivious_ctx = ObliviousHttpRequest::Context( |
| GetSeededClientContext(5, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, |
| EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM), |
| GetSeededEncapsulatedKey()); |
| auto server_response_encapsulate = |
| ObliviousHttpResponse::CreateServerObliviousResponse("test response", |
| oblivious_ctx); |
| EXPECT_TRUE(server_response_encapsulate.ok()); |
| auto oblivious_response = |
| server_response_encapsulate->EncapsulateAndSerialize(); |
| auto client_response_encapsulate = |
| ObliviousHttpResponse::CreateClientObliviousResponse(oblivious_response, |
| oblivious_ctx); |
| auto decrypted = client_response_encapsulate->GetPlaintextData(); |
| EXPECT_EQ(decrypted, "test response"); |
| } |
| |
| TEST(ObliviousHttpResponse, TestEncapsulateWithQuicheRandom) { |
| auto random = TestQuicheRandom('z'); |
| auto server_seeded_request = SetUpObliviousHttpContext( |
| 6, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256, |
| EVP_HPKE_AES_256_GCM, "test"); |
| auto server_request_context = |
| std::move(server_seeded_request).ReleaseContext(); |
| auto server_response_encapsulate = |
| ObliviousHttpResponse::CreateServerObliviousResponse( |
| "test response", server_request_context, |
| ObliviousHttpHeaderKeyConfig::kOhttpResponseLabel, &random); |
| EXPECT_TRUE(server_response_encapsulate.ok()); |
| std::string response_nonce = |
| server_response_encapsulate->EncapsulateAndSerialize().substr( |
| 0, GetResponseNonceLength(*(server_request_context.hpke_context_))); |
| EXPECT_EQ(response_nonce, |
| std::string( |
| GetResponseNonceLength(*(server_request_context.hpke_context_)), |
| 'z')); |
| absl::string_view expected_encrypted_response = |
| "2a3271ac4e6a501f51d0264d3dd7d0bc8a06973b58e89c26d6dac06144"; |
| EXPECT_EQ( |
| server_response_encapsulate->EncapsulateAndSerialize().substr( |
| GetResponseNonceLength(*(server_request_context.hpke_context_))), |
| absl::HexStringToBytes(expected_encrypted_response)); |
| } |
| |
| } // namespace quiche |