1 #include "quiche/oblivious_http/buffers/oblivious_http_request.h"
2 
3 #include <stddef.h>
4 #include <stdint.h>
5 
6 #include <memory>
7 #include <optional>
8 #include <string>
9 #include <utility>
10 
11 #include "absl/memory/memory.h"
12 #include "absl/status/status.h"
13 #include "absl/status/statusor.h"
14 #include "absl/strings/str_cat.h"
15 #include "absl/strings/string_view.h"
16 #include "openssl/hpke.h"
17 #include "quiche/common/platform/api/quiche_bug_tracker.h"
18 #include "quiche/common/platform/api/quiche_logging.h"
19 #include "quiche/common/quiche_crypto_logging.h"
20 
21 namespace quiche {
22 // Ctor.
Context(bssl::UniquePtr<EVP_HPKE_CTX> hpke_context,std::string encapsulated_key)23 ObliviousHttpRequest::Context::Context(
24     bssl::UniquePtr<EVP_HPKE_CTX> hpke_context, std::string encapsulated_key)
25     : hpke_context_(std::move(hpke_context)),
26       encapsulated_key_(std::move(encapsulated_key)) {}
27 
28 // Ctor.
ObliviousHttpRequest(bssl::UniquePtr<EVP_HPKE_CTX> hpke_context,std::string encapsulated_key,const ObliviousHttpHeaderKeyConfig & ohttp_key_config,std::string req_ciphertext,std::string req_plaintext)29 ObliviousHttpRequest::ObliviousHttpRequest(
30     bssl::UniquePtr<EVP_HPKE_CTX> hpke_context, std::string encapsulated_key,
31     const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
32     std::string req_ciphertext, std::string req_plaintext)
33     : oblivious_http_request_context_(absl::make_optional(
34           Context(std::move(hpke_context), std::move(encapsulated_key)))),
35       key_config_(ohttp_key_config),
36       request_ciphertext_(std::move(req_ciphertext)),
37       request_plaintext_(std::move(req_plaintext)) {}
38 
39 // Request Decapsulation.
40 absl::StatusOr<ObliviousHttpRequest>
CreateServerObliviousRequest(absl::string_view encrypted_data,const EVP_HPKE_KEY & gateway_key,const ObliviousHttpHeaderKeyConfig & ohttp_key_config,absl::string_view request_label)41 ObliviousHttpRequest::CreateServerObliviousRequest(
42     absl::string_view encrypted_data, const EVP_HPKE_KEY& gateway_key,
43     const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
44     absl::string_view request_label) {
45   if (EVP_HPKE_KEY_kem(&gateway_key) == nullptr) {
46     return absl::InvalidArgumentError(
47         "Invalid input param. Failed to import gateway_key.");
48   }
49   bssl::UniquePtr<EVP_HPKE_CTX> gateway_ctx(EVP_HPKE_CTX_new());
50   if (gateway_ctx == nullptr) {
51     return SslErrorAsStatus("Failed to initialize Gateway/Server's Context.");
52   }
53 
54   QuicheDataReader reader(encrypted_data);
55 
56   auto is_hdr_ok = ohttp_key_config.ParseOhttpPayloadHeader(reader);
57   if (!is_hdr_ok.ok()) {
58     return is_hdr_ok;
59   }
60 
61   size_t enc_key_len = EVP_HPKE_KEM_enc_len(EVP_HPKE_KEY_kem(&gateway_key));
62 
63   absl::string_view enc_key_received;
64   if (!reader.ReadStringPiece(&enc_key_received, enc_key_len)) {
65     return absl::FailedPreconditionError(absl::StrCat(
66         "Failed to extract encapsulation key of expected len=", enc_key_len,
67         "from payload."));
68   }
69   std::string info =
70       ohttp_key_config.SerializeRecipientContextInfo(request_label);
71   if (!EVP_HPKE_CTX_setup_recipient(
72           gateway_ctx.get(), &gateway_key, ohttp_key_config.GetHpkeKdf(),
73           ohttp_key_config.GetHpkeAead(),
74           reinterpret_cast<const uint8_t*>(enc_key_received.data()),
75           enc_key_received.size(),
76           reinterpret_cast<const uint8_t*>(info.data()), info.size())) {
77     return SslErrorAsStatus("Failed to setup recipient context");
78   }
79 
80   absl::string_view ciphertext_received = reader.ReadRemainingPayload();
81   // Decrypt the message.
82   std::string decrypted(ciphertext_received.size(), '\0');
83   size_t decrypted_len;
84   if (!EVP_HPKE_CTX_open(
85           gateway_ctx.get(), reinterpret_cast<uint8_t*>(decrypted.data()),
86           &decrypted_len, decrypted.size(),
87           reinterpret_cast<const uint8_t*>(ciphertext_received.data()),
88           ciphertext_received.size(), nullptr, 0)) {
89     return SslErrorAsStatus("Failed to decrypt.",
90                             absl::StatusCode::kInvalidArgument);
91   }
92   decrypted.resize(decrypted_len);
93   return ObliviousHttpRequest(
94       std::move(gateway_ctx), std::string(enc_key_received), ohttp_key_config,
95       std::string(ciphertext_received), std::move(decrypted));
96 }
97 
98 // Request Encapsulation.
99 absl::StatusOr<ObliviousHttpRequest>
CreateClientObliviousRequest(std::string plaintext_payload,absl::string_view hpke_public_key,const ObliviousHttpHeaderKeyConfig & ohttp_key_config,absl::string_view request_label)100 ObliviousHttpRequest::CreateClientObliviousRequest(
101     std::string plaintext_payload, absl::string_view hpke_public_key,
102     const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
103     absl::string_view request_label) {
104   return EncapsulateWithSeed(std::move(plaintext_payload), hpke_public_key,
105                              ohttp_key_config, /*seed=*/"", request_label);
106 }
107 
108 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 request_label)109 ObliviousHttpRequest::CreateClientWithSeedForTesting(
110     std::string plaintext_payload, absl::string_view hpke_public_key,
111     const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
112     absl::string_view seed, absl::string_view request_label) {
113   return ObliviousHttpRequest::EncapsulateWithSeed(
114       std::move(plaintext_payload), hpke_public_key, ohttp_key_config, seed,
115       request_label);
116 }
117 
EncapsulateWithSeed(std::string plaintext_payload,absl::string_view hpke_public_key,const ObliviousHttpHeaderKeyConfig & ohttp_key_config,absl::string_view seed,absl::string_view request_label)118 absl::StatusOr<ObliviousHttpRequest> ObliviousHttpRequest::EncapsulateWithSeed(
119     std::string plaintext_payload, absl::string_view hpke_public_key,
120     const ObliviousHttpHeaderKeyConfig& ohttp_key_config,
121     absl::string_view seed, absl::string_view request_label) {
122   if (plaintext_payload.empty() || hpke_public_key.empty()) {
123     return absl::InvalidArgumentError("Invalid input.");
124   }
125   // Initialize HPKE key and context.
126   bssl::UniquePtr<EVP_HPKE_KEY> client_key(EVP_HPKE_KEY_new());
127   if (client_key == nullptr) {
128     return SslErrorAsStatus("Failed to initialize HPKE Client Key.");
129   }
130   bssl::UniquePtr<EVP_HPKE_CTX> client_ctx(EVP_HPKE_CTX_new());
131   if (client_ctx == nullptr) {
132     return SslErrorAsStatus("Failed to initialize HPKE Client Context.");
133   }
134   // Setup the sender (client)
135   std::string encapsulated_key(EVP_HPKE_MAX_ENC_LENGTH, '\0');
136   size_t enc_len;
137   std::string info =
138       ohttp_key_config.SerializeRecipientContextInfo(request_label);
139   if (seed.empty()) {
140     if (!EVP_HPKE_CTX_setup_sender(
141             client_ctx.get(),
142             reinterpret_cast<uint8_t*>(encapsulated_key.data()), &enc_len,
143             encapsulated_key.size(), ohttp_key_config.GetHpkeKem(),
144             ohttp_key_config.GetHpkeKdf(), ohttp_key_config.GetHpkeAead(),
145             reinterpret_cast<const uint8_t*>(hpke_public_key.data()),
146             hpke_public_key.size(),
147             reinterpret_cast<const uint8_t*>(info.data()), info.size())) {
148       return SslErrorAsStatus(
149           "Failed to setup HPKE context with given public key param "
150           "hpke_public_key.");
151     }
152   } else {
153     if (!EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
154             client_ctx.get(),
155             reinterpret_cast<uint8_t*>(encapsulated_key.data()), &enc_len,
156             encapsulated_key.size(), ohttp_key_config.GetHpkeKem(),
157             ohttp_key_config.GetHpkeKdf(), ohttp_key_config.GetHpkeAead(),
158             reinterpret_cast<const uint8_t*>(hpke_public_key.data()),
159             hpke_public_key.size(),
160             reinterpret_cast<const uint8_t*>(info.data()), info.size(),
161             reinterpret_cast<const uint8_t*>(seed.data()), seed.size())) {
162       return SslErrorAsStatus(
163           "Failed to setup HPKE context with given public key param "
164           "hpke_public_key and seed.");
165     }
166   }
167   encapsulated_key.resize(enc_len);
168   std::string ciphertext(
169       plaintext_payload.size() + EVP_HPKE_CTX_max_overhead(client_ctx.get()),
170       '\0');
171   size_t ciphertext_len;
172   if (!EVP_HPKE_CTX_seal(
173           client_ctx.get(), reinterpret_cast<uint8_t*>(ciphertext.data()),
174           &ciphertext_len, ciphertext.size(),
175           reinterpret_cast<const uint8_t*>(plaintext_payload.data()),
176           plaintext_payload.size(), nullptr, 0)) {
177     return SslErrorAsStatus(
178         "Failed to encrypt plaintext_payload with given public key param "
179         "hpke_public_key.");
180   }
181   ciphertext.resize(ciphertext_len);
182   if (encapsulated_key.empty() || ciphertext.empty()) {
183     return absl::InternalError(absl::StrCat(
184         "Failed to generate required data: ",
185         (encapsulated_key.empty() ? "encapsulated key is empty" : ""),
186         (ciphertext.empty() ? "encrypted data is empty" : ""), "."));
187   }
188 
189   return ObliviousHttpRequest(
190       std::move(client_ctx), std::move(encapsulated_key), ohttp_key_config,
191       std::move(ciphertext), std::move(plaintext_payload));
192 }
193 
194 // Request Serialize.
195 // Builds request=[hdr, enc, ct].
196 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-4.5
EncapsulateAndSerialize() const197 std::string ObliviousHttpRequest::EncapsulateAndSerialize() const {
198   if (!oblivious_http_request_context_.has_value()) {
199     QUICHE_BUG(ohttp_encapsulate_after_context_extract)
200         << "EncapsulateAndSerialize cannot be called after ReleaseContext()";
201     return "";
202   }
203   return absl::StrCat(key_config_.SerializeOhttpPayloadHeader(),
204                       oblivious_http_request_context_->encapsulated_key_,
205                       request_ciphertext_);
206 }
207 
208 // Returns Decrypted blob in the case of server, and returns plaintext used by
209 // the client while `CreateClientObliviousRequest`.
GetPlaintextData() const210 absl::string_view ObliviousHttpRequest::GetPlaintextData() const {
211   return request_plaintext_;
212 }
213 
214 }  // namespace quiche
215