1 #include "quiche/oblivious_http/oblivious_http_gateway.h"
2
3 #include <stdint.h>
4
5 #include <string>
6 #include <utility>
7
8 #include "absl/status/status.h"
9 #include "absl/status/statusor.h"
10 #include "absl/strings/escaping.h"
11 #include "absl/strings/string_view.h"
12 #include "quiche/common/platform/api/quiche_test.h"
13 #include "quiche/common/platform/api/quiche_thread.h"
14 #include "quiche/common/quiche_random.h"
15 #include "quiche/oblivious_http/buffers/oblivious_http_request.h"
16
17 namespace quiche {
18 namespace {
19
GetHpkePrivateKey()20 std::string GetHpkePrivateKey() {
21 // Dev/Test private key generated using Keystore.
22 absl::string_view hpke_key_hex =
23 "b77431ecfa8f4cfc30d6e467aafa06944dffe28cb9dd1409e33a3045f5adc8a1";
24 std::string hpke_key_bytes;
25 EXPECT_TRUE(absl::HexStringToBytes(hpke_key_hex, &hpke_key_bytes));
26 return hpke_key_bytes;
27 }
28
GetHpkePublicKey()29 std::string GetHpkePublicKey() {
30 // Dev/Test public key generated using Keystore.
31 absl::string_view public_key =
32 "6d21cfe09fbea5122f9ebc2eb2a69fcc4f06408cd54aac934f012e76fcdcef62";
33 std::string public_key_bytes;
34 EXPECT_TRUE(absl::HexStringToBytes(public_key, &public_key_bytes));
35 return public_key_bytes;
36 }
37
GetOhttpKeyConfig(uint8_t key_id,uint16_t kem_id,uint16_t kdf_id,uint16_t aead_id)38 const ObliviousHttpHeaderKeyConfig GetOhttpKeyConfig(uint8_t key_id,
39 uint16_t kem_id,
40 uint16_t kdf_id,
41 uint16_t aead_id) {
42 auto ohttp_key_config =
43 ObliviousHttpHeaderKeyConfig::Create(key_id, kem_id, kdf_id, aead_id);
44 EXPECT_TRUE(ohttp_key_config.ok());
45 return std::move(ohttp_key_config.value());
46 }
47
TEST(ObliviousHttpGateway,TestProvisioningKeyAndDecapsulate)48 TEST(ObliviousHttpGateway, TestProvisioningKeyAndDecapsulate) {
49 // X25519 Secret key (priv key).
50 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#appendix-A-2
51 constexpr absl::string_view kX25519SecretKey =
52 "3c168975674b2fa8e465970b79c8dcf09f1c741626480bd4c6162fc5b6a98e1a";
53 std::string x25519_secret_key_bytes;
54 ASSERT_TRUE(
55 absl::HexStringToBytes(kX25519SecretKey, &x25519_secret_key_bytes));
56
57 auto instance = ObliviousHttpGateway::Create(
58 /*hpke_private_key*/ x25519_secret_key_bytes,
59 /*ohttp_key_config*/ GetOhttpKeyConfig(
60 /*key_id=*/1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
61 EVP_HPKE_AES_128_GCM));
62
63 // Encapsulated request.
64 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#appendix-A-14
65 constexpr absl::string_view kEncapsulatedRequest =
66 "010020000100014b28f881333e7c164ffc499ad9796f877f4e1051ee6d31bad19dec96c2"
67 "08b4726374e469135906992e1268c594d2a10c695d858c40a026e7965e7d86b83dd440b2"
68 "c0185204b4d63525";
69 std::string encapsulated_request_bytes;
70 ASSERT_TRUE(absl::HexStringToBytes(kEncapsulatedRequest,
71 &encapsulated_request_bytes));
72
73 auto decrypted_req =
74 instance->DecryptObliviousHttpRequest(encapsulated_request_bytes);
75 ASSERT_TRUE(decrypted_req.ok());
76 ASSERT_FALSE(decrypted_req->GetPlaintextData().empty());
77 }
78
TEST(ObliviousHttpGateway,TestDecryptingMultipleRequestsWithSingleInstance)79 TEST(ObliviousHttpGateway, TestDecryptingMultipleRequestsWithSingleInstance) {
80 auto instance = ObliviousHttpGateway::Create(
81 GetHpkePrivateKey(),
82 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
83 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM));
84 // plaintext: "test request 1"
85 absl::string_view encrypted_req_1 =
86 "010020000100025f20b60306b61ad9ecad389acd752ca75c4e2969469809fe3d84aae137"
87 "f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c8ea6c10592594845f";
88 std::string encrypted_req_1_bytes;
89 ASSERT_TRUE(absl::HexStringToBytes(encrypted_req_1, &encrypted_req_1_bytes));
90 auto decapsulated_req_1 =
91 instance->DecryptObliviousHttpRequest(encrypted_req_1_bytes);
92 ASSERT_TRUE(decapsulated_req_1.ok());
93 ASSERT_FALSE(decapsulated_req_1->GetPlaintextData().empty());
94
95 // plaintext: "test request 2"
96 absl::string_view encrypted_req_2 =
97 "01002000010002285ebc2fcad72cc91b378050cac29a62feea9cd97829335ee9fc87e672"
98 "4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5d922918865a0a447a";
99 std::string encrypted_req_2_bytes;
100 ASSERT_TRUE(absl::HexStringToBytes(encrypted_req_2, &encrypted_req_2_bytes));
101 auto decapsulated_req_2 =
102 instance->DecryptObliviousHttpRequest(encrypted_req_2_bytes);
103 ASSERT_TRUE(decapsulated_req_2.ok());
104 ASSERT_FALSE(decapsulated_req_2->GetPlaintextData().empty());
105 }
106
TEST(ObliviousHttpGateway,TestInvalidHPKEKey)107 TEST(ObliviousHttpGateway, TestInvalidHPKEKey) {
108 // Invalid private key.
109 EXPECT_EQ(ObliviousHttpGateway::Create(
110 "Invalid HPKE key",
111 GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
112 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
113 .status()
114 .code(),
115 absl::StatusCode::kInternal);
116 // Empty private key.
117 EXPECT_EQ(ObliviousHttpGateway::Create(
118 /*hpke_private_key*/ "",
119 GetOhttpKeyConfig(70, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
120 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM))
121 .status()
122 .code(),
123 absl::StatusCode::kInvalidArgument);
124 }
125
TEST(ObliviousHttpGateway,TestObliviousResponseHandling)126 TEST(ObliviousHttpGateway, TestObliviousResponseHandling) {
127 auto ohttp_key_config =
128 GetOhttpKeyConfig(3, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
129 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
130 auto instance =
131 ObliviousHttpGateway::Create(GetHpkePrivateKey(), ohttp_key_config);
132 ASSERT_TRUE(instance.ok());
133 auto encapsualte_request_on_client =
134 ObliviousHttpRequest::CreateClientObliviousRequest(
135 "test", GetHpkePublicKey(), ohttp_key_config);
136 ASSERT_TRUE(encapsualte_request_on_client.ok());
137 // Setup Recipient to allow setting up the HPKE context, and subsequently use
138 // it to encrypt the response.
139 auto decapsulated_req_on_server = instance->DecryptObliviousHttpRequest(
140 encapsualte_request_on_client->EncapsulateAndSerialize());
141 ASSERT_TRUE(decapsulated_req_on_server.ok());
142 auto server_request_context =
143 std::move(decapsulated_req_on_server.value()).ReleaseContext();
144 auto encapsulate_resp_on_gateway = instance->CreateObliviousHttpResponse(
145 "some response", server_request_context);
146 ASSERT_TRUE(encapsulate_resp_on_gateway.ok());
147 ASSERT_FALSE(encapsulate_resp_on_gateway->EncapsulateAndSerialize().empty());
148 }
149
TEST(ObliviousHttpGateway,TestHandlingMultipleResponsesForMultipleRequestsWithSingleInstance)150 TEST(ObliviousHttpGateway,
151 TestHandlingMultipleResponsesForMultipleRequestsWithSingleInstance) {
152 auto instance = ObliviousHttpGateway::Create(
153 GetHpkePrivateKey(),
154 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
155 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
156 QuicheRandom::GetInstance());
157 // Setup contexts first.
158 std::string encrypted_request_1_bytes;
159 ASSERT_TRUE(
160 absl::HexStringToBytes("010020000100025f20b60306b61ad9ecad389acd752ca75c4"
161 "e2969469809fe3d84aae137"
162 "f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c"
163 "8ea6c10592594845f",
164 &encrypted_request_1_bytes));
165 auto decrypted_request_1 =
166 instance->DecryptObliviousHttpRequest(encrypted_request_1_bytes);
167 ASSERT_TRUE(decrypted_request_1.ok());
168 std::string encrypted_request_2_bytes;
169 ASSERT_TRUE(
170 absl::HexStringToBytes("01002000010002285ebc2fcad72cc91b378050cac29a62fee"
171 "a9cd97829335ee9fc87e672"
172 "4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5"
173 "d922918865a0a447a",
174 &encrypted_request_2_bytes));
175 auto decrypted_request_2 =
176 instance->DecryptObliviousHttpRequest(encrypted_request_2_bytes);
177 ASSERT_TRUE(decrypted_request_2.ok());
178
179 // Extract contexts and handle the response for each corresponding request.
180 auto oblivious_request_context_1 =
181 std::move(decrypted_request_1.value()).ReleaseContext();
182 auto encrypted_response_1 = instance->CreateObliviousHttpResponse(
183 "test response 1", oblivious_request_context_1);
184 ASSERT_TRUE(encrypted_response_1.ok());
185 ASSERT_FALSE(encrypted_response_1->EncapsulateAndSerialize().empty());
186 auto oblivious_request_context_2 =
187 std::move(decrypted_request_2.value()).ReleaseContext();
188 auto encrypted_response_2 = instance->CreateObliviousHttpResponse(
189 "test response 2", oblivious_request_context_2);
190 ASSERT_TRUE(encrypted_response_2.ok());
191 ASSERT_FALSE(encrypted_response_2->EncapsulateAndSerialize().empty());
192 }
193
TEST(ObliviousHttpGateway,TestWithMultipleThreads)194 TEST(ObliviousHttpGateway, TestWithMultipleThreads) {
195 class TestQuicheThread : public QuicheThread {
196 public:
197 TestQuicheThread(const ObliviousHttpGateway& gateway_receiver,
198 std::string request_payload, std::string response_payload)
199 : QuicheThread("gateway_thread"),
200 gateway_receiver_(gateway_receiver),
201 request_payload_(request_payload),
202 response_payload_(response_payload) {}
203
204 protected:
205 void Run() override {
206 auto decrypted_request =
207 gateway_receiver_.DecryptObliviousHttpRequest(request_payload_);
208 ASSERT_TRUE(decrypted_request.ok());
209 ASSERT_FALSE(decrypted_request->GetPlaintextData().empty());
210 auto gateway_request_context =
211 std::move(decrypted_request.value()).ReleaseContext();
212 auto encrypted_response = gateway_receiver_.CreateObliviousHttpResponse(
213 response_payload_, gateway_request_context);
214 ASSERT_TRUE(encrypted_response.ok());
215 ASSERT_FALSE(encrypted_response->EncapsulateAndSerialize().empty());
216 }
217
218 private:
219 const ObliviousHttpGateway& gateway_receiver_;
220 std::string request_payload_, response_payload_;
221 };
222
223 auto gateway_receiver = ObliviousHttpGateway::Create(
224 GetHpkePrivateKey(),
225 GetOhttpKeyConfig(1, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
226 EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM),
227 QuicheRandom::GetInstance());
228
229 std::string request_payload_1;
230 ASSERT_TRUE(
231 absl::HexStringToBytes("010020000100025f20b60306b61ad9ecad389acd752ca75c4"
232 "e2969469809fe3d84aae137"
233 "f73e4ccfe9ba71f12831fdce6c8202fbd38a84c5d8a73ac4c"
234 "8ea6c10592594845f",
235 &request_payload_1));
236 TestQuicheThread t1(*gateway_receiver, request_payload_1, "test response 1");
237 std::string request_payload_2;
238 ASSERT_TRUE(
239 absl::HexStringToBytes("01002000010002285ebc2fcad72cc91b378050cac29a62fee"
240 "a9cd97829335ee9fc87e672"
241 "4fa13ff2efdff620423d54225d3099088e7b32a5165f805a5"
242 "d922918865a0a447a",
243 &request_payload_2));
244 TestQuicheThread t2(*gateway_receiver, request_payload_2, "test response 2");
245 t1.Start();
246 t2.Start();
247 t1.Join();
248 t2.Join();
249 }
250 } // namespace
251 } // namespace quiche
252