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