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