1 #include "quiche/oblivious_http/oblivious_http_client.h"
2
3 #include <stdint.h>
4
5 #include <memory>
6 #include <string>
7 #include <utility>
8
9 #include "absl/status/status.h"
10 #include "absl/status/statusor.h"
11 #include "absl/strings/escaping.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/common/platform/api/quiche_test.h"
14 #include "quiche/common/platform/api/quiche_thread.h"
15
16 namespace quiche {
17
GetHpkePrivateKey()18 std::string GetHpkePrivateKey() {
19 // Dev/Test private key generated using Keystore.
20 absl::string_view hpke_key_hex =
21 "b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1";
22 std::string hpke_key_bytes;
23 EXPECT_TRUE(absl::HexStringToBytes(hpke_key_hex, &hpke_key_bytes));
24 return hpke_key_bytes;
25 }
26
GetHpkePublicKey()27 std::string GetHpkePublicKey() {
28 // Dev/Test public key generated using Keystore.
29 absl::string_view public_key =
30 "6d21cfe09fbea5122f9ebc2eb2a69fcc4f06408cd54aac934f012e76fcdcef62";
31 std::string public_key_bytes;
32 EXPECT_TRUE(absl::HexStringToBytes(public_key, &public_key_bytes));
33 return public_key_bytes;
34 }
35
GetOhttpKeyConfig(uint8_t key_id,uint16_t kem_id,uint16_t kdf_id,uint16_t aead_id)36 const ObliviousHttpHeaderKeyConfig GetOhttpKeyConfig(uint8_t key_id,
37 uint16_t kem_id,
38 uint16_t kdf_id,
39 uint16_t aead_id) {
40 auto ohttp_key_config =
41 ObliviousHttpHeaderKeyConfig::Create(key_id, kem_id, kdf_id, aead_id);
42 EXPECT_TRUE(ohttp_key_config.ok());
43 return ohttp_key_config.value();
44 }
45
ConstructHpkeKey(absl::string_view hpke_key,const ObliviousHttpHeaderKeyConfig & ohttp_key_config)46 bssl::UniquePtr<EVP_HPKE_KEY> ConstructHpkeKey(
47 absl::string_view hpke_key,
48 const ObliviousHttpHeaderKeyConfig& ohttp_key_config) {
49 bssl::UniquePtr<EVP_HPKE_KEY> bssl_hpke_key(EVP_HPKE_KEY_new());
50 EXPECT_NE(bssl_hpke_key, nullptr);
51 EXPECT_TRUE(EVP_HPKE_KEY_init(
52 bssl_hpke_key.get(), ohttp_key_config.GetHpkeKem(),
53 reinterpret_cast<const uint8_t*>(hpke_key.data()), hpke_key.size()));
54 return bssl_hpke_key;
55 }
56
TEST(ObliviousHttpClient,TestEncapsulate)57 TEST(ObliviousHttpClient, TestEncapsulate) {
58 auto client = ObliviousHttpClient::Create(
59 GetHpkePublicKey(),
60 GetOhttpKeyConfig(8, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
61 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
62 ASSERT_TRUE(client.ok());
63 auto encrypted_req = client->CreateObliviousHttpRequest("test string 1");
64 ASSERT_TRUE(encrypted_req.ok());
65 auto serialized_encrypted_req = encrypted_req->EncapsulateAndSerialize();
66 ASSERT_FALSE(serialized_encrypted_req.empty());
67 }
68
TEST(ObliviousHttpClient,TestEncryptingMultipleRequestsWithSingleInstance)69 TEST(ObliviousHttpClient, TestEncryptingMultipleRequestsWithSingleInstance) {
70 auto client = ObliviousHttpClient::Create(
71 GetHpkePublicKey(),
72 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
73 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
74 ASSERT_TRUE(client.ok());
75 auto ohttp_req_1 = client->CreateObliviousHttpRequest("test string 1");
76 ASSERT_TRUE(ohttp_req_1.ok());
77 auto serialized_ohttp_req_1 = ohttp_req_1->EncapsulateAndSerialize();
78 ASSERT_FALSE(serialized_ohttp_req_1.empty());
79 auto ohttp_req_2 = client->CreateObliviousHttpRequest("test string 2");
80 ASSERT_TRUE(ohttp_req_2.ok());
81 auto serialized_ohttp_req_2 = ohttp_req_2->EncapsulateAndSerialize();
82 ASSERT_FALSE(serialized_ohttp_req_2.empty());
83 EXPECT_NE(serialized_ohttp_req_1, serialized_ohttp_req_2);
84 }
85
TEST(ObliviousHttpClient,TestInvalidHPKEKey)86 TEST(ObliviousHttpClient, TestInvalidHPKEKey) {
87 // Invalid public key.
88 EXPECT_EQ(ObliviousHttpClient::Create(
89 "Invalid HPKE key",
90 GetOhttpKeyConfig(50, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
91 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
92 .status()
93 .code(),
94 absl::StatusCode::kInvalidArgument);
95 // Empty public key.
96 EXPECT_EQ(ObliviousHttpClient::Create(
97 /*hpke_public_key*/ "",
98 GetOhttpKeyConfig(50, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
99 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
100 .status()
101 .code(),
102 absl::StatusCode::kInvalidArgument);
103 }
104
TEST(ObliviousHttpClient,TestTwoSamePlaintextsWillGenerateDifferentEncryptedPayloads)105 TEST(ObliviousHttpClient,
106 TestTwoSamePlaintextsWillGenerateDifferentEncryptedPayloads) {
107 // Due to the nature of the encapsulated_key generated in HPKE being unique
108 // for every request, expect different encrypted payloads when encrypting same
109 // plaintexts.
110 auto client = ObliviousHttpClient::Create(
111 GetHpkePublicKey(),
112 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
113 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
114 ASSERT_TRUE(client.ok());
115 auto encrypted_request_1 =
116 client->CreateObliviousHttpRequest("same plaintext");
117 ASSERT_TRUE(encrypted_request_1.ok());
118 auto serialized_encrypted_request_1 =
119 encrypted_request_1->EncapsulateAndSerialize();
120 ASSERT_FALSE(serialized_encrypted_request_1.empty());
121 auto encrypted_request_2 =
122 client->CreateObliviousHttpRequest("same plaintext");
123 ASSERT_TRUE(encrypted_request_2.ok());
124 auto serialized_encrypted_request_2 =
125 encrypted_request_2->EncapsulateAndSerialize();
126 ASSERT_FALSE(serialized_encrypted_request_2.empty());
127 EXPECT_NE(serialized_encrypted_request_1, serialized_encrypted_request_2);
128 }
129
TEST(ObliviousHttpClient,TestObliviousResponseHandling)130 TEST(ObliviousHttpClient, TestObliviousResponseHandling) {
131 auto ohttp_key_config =
132 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
133 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
134 auto encapsulate_req_on_client =
135 ObliviousHttpRequest::CreateClientObliviousRequest(
136 "test", GetHpkePublicKey(), ohttp_key_config);
137 ASSERT_TRUE(encapsulate_req_on_client.ok());
138 auto decapsulate_req_on_gateway =
139 ObliviousHttpRequest::CreateServerObliviousRequest(
140 encapsulate_req_on_client->EncapsulateAndSerialize(),
141 *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config)),
142 ohttp_key_config);
143 ASSERT_TRUE(decapsulate_req_on_gateway.ok());
144 auto gateway_request_context =
145 std::move(decapsulate_req_on_gateway.value()).ReleaseContext();
146 auto encapsulate_resp_on_gateway =
147 ObliviousHttpResponse::CreateServerObliviousResponse(
148 "test response", gateway_request_context);
149 ASSERT_TRUE(encapsulate_resp_on_gateway.ok());
150
151 auto client =
152 ObliviousHttpClient::Create(GetHpkePublicKey(), ohttp_key_config);
153 ASSERT_TRUE(client.ok());
154 auto client_request_context =
155 std::move(encapsulate_req_on_client.value()).ReleaseContext();
156 auto decapsulate_resp_on_client = client->DecryptObliviousHttpResponse(
157 encapsulate_resp_on_gateway->EncapsulateAndSerialize(),
158 client_request_context);
159 ASSERT_TRUE(decapsulate_resp_on_client.ok());
160 EXPECT_EQ(decapsulate_resp_on_client->GetPlaintextData(), "test response");
161 }
162
TEST(ObliviousHttpClient,DecryptResponseReceivedByTheClientUsingServersObliviousContext)163 TEST(ObliviousHttpClient,
164 DecryptResponseReceivedByTheClientUsingServersObliviousContext) {
165 auto ohttp_key_config =
166 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
167 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
168 auto encapsulate_req_on_client =
169 ObliviousHttpRequest::CreateClientObliviousRequest(
170 "test", GetHpkePublicKey(), ohttp_key_config);
171 ASSERT_TRUE(encapsulate_req_on_client.ok());
172 auto decapsulate_req_on_gateway =
173 ObliviousHttpRequest::CreateServerObliviousRequest(
174 encapsulate_req_on_client->EncapsulateAndSerialize(),
175 *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config)),
176 ohttp_key_config);
177 ASSERT_TRUE(decapsulate_req_on_gateway.ok());
178 auto gateway_request_context =
179 std::move(decapsulate_req_on_gateway.value()).ReleaseContext();
180 auto encapsulate_resp_on_gateway =
181 ObliviousHttpResponse::CreateServerObliviousResponse(
182 "test response", gateway_request_context);
183 ASSERT_TRUE(encapsulate_resp_on_gateway.ok());
184
185 auto client =
186 ObliviousHttpClient::Create(GetHpkePublicKey(), ohttp_key_config);
187 ASSERT_TRUE(client.ok());
188 auto decapsulate_resp_on_client = client->DecryptObliviousHttpResponse(
189 encapsulate_resp_on_gateway->EncapsulateAndSerialize(),
190 gateway_request_context);
191 ASSERT_TRUE(decapsulate_resp_on_client.ok());
192 EXPECT_EQ(decapsulate_resp_on_client->GetPlaintextData(), "test response");
193 }
194
TEST(ObliviousHttpClient,TestWithMultipleThreads)195 TEST(ObliviousHttpClient, TestWithMultipleThreads) {
196 class TestQuicheThread : public QuicheThread {
197 public:
198 TestQuicheThread(const ObliviousHttpClient& client,
199 std::string request_payload,
200 ObliviousHttpHeaderKeyConfig ohttp_key_config)
201 : QuicheThread("client_thread"),
202 client_(client),
203 request_payload_(request_payload),
204 ohttp_key_config_(ohttp_key_config) {}
205
206 protected:
207 void Run() override {
208 auto encrypted_request =
209 client_.CreateObliviousHttpRequest(request_payload_);
210 ASSERT_TRUE(encrypted_request.ok());
211 ASSERT_FALSE(encrypted_request->EncapsulateAndSerialize().empty());
212 // Setup recipient and get encrypted response payload.
213 auto decapsulate_req_on_gateway =
214 ObliviousHttpRequest::CreateServerObliviousRequest(
215 encrypted_request->EncapsulateAndSerialize(),
216 *(ConstructHpkeKey(GetHpkePrivateKey(), ohttp_key_config_)),
217 ohttp_key_config_);
218 ASSERT_TRUE(decapsulate_req_on_gateway.ok());
219 auto gateway_request_context =
220 std::move(decapsulate_req_on_gateway.value()).ReleaseContext();
221 auto encapsulate_resp_on_gateway =
222 ObliviousHttpResponse::CreateServerObliviousResponse(
223 "test response", gateway_request_context);
224 ASSERT_TRUE(encapsulate_resp_on_gateway.ok());
225 ASSERT_FALSE(
226 encapsulate_resp_on_gateway->EncapsulateAndSerialize().empty());
227 auto client_request_context =
228 std::move(encrypted_request.value()).ReleaseContext();
229 auto decrypted_response = client_.DecryptObliviousHttpResponse(
230 encapsulate_resp_on_gateway->EncapsulateAndSerialize(),
231 client_request_context);
232 ASSERT_TRUE(decrypted_response.ok());
233 ASSERT_FALSE(decrypted_response->GetPlaintextData().empty());
234 }
235
236 private:
237 const ObliviousHttpClient& client_;
238 std::string request_payload_;
239 ObliviousHttpHeaderKeyConfig ohttp_key_config_;
240 };
241
242 auto ohttp_key_config =
243 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
244 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
245 auto client =
246 ObliviousHttpClient::Create(GetHpkePublicKey(), ohttp_key_config);
247
248 TestQuicheThread t1(*client, "test request 1", ohttp_key_config);
249 TestQuicheThread t2(*client, "test request 2", ohttp_key_config);
250 t1.Start();
251 t2.Start();
252 t1.Join();
253 t2.Join();
254 }
255
256 } // namespace quiche
257