1 #include "quiche/oblivious_http/common/oblivious_http_header_key_config.h"
2 
3 #include <algorithm>
4 #include <cstdint>
5 #include <string>
6 #include <vector>
7 
8 #include "absl/memory/memory.h"
9 #include "absl/status/status.h"
10 #include "absl/strings/escaping.h"
11 #include "absl/strings/str_cat.h"
12 #include "absl/strings/string_view.h"
13 #include "openssl/base.h"
14 #include "openssl/hpke.h"
15 #include "quiche/common/platform/api/quiche_bug_tracker.h"
16 #include "quiche/common/platform/api/quiche_logging.h"
17 #include "quiche/common/quiche_data_writer.h"
18 #include "quiche/common/quiche_endian.h"
19 
20 namespace quiche {
21 namespace {
22 
23 // Size of KEM ID is 2 bytes. Refer to OHTTP Key Config in the spec,
24 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-06.html#name-a-single-key-configuration
25 constexpr size_t kSizeOfHpkeKemId = 2;
26 
27 // Size of Symmetric algorithms is 2 bytes(16 bits) each.
28 // Refer to HPKE Symmetric Algorithms configuration in the spec,
29 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-06.html#name-a-single-key-configuration
30 constexpr size_t kSizeOfSymmetricAlgorithmHpkeKdfId = 2;
31 constexpr size_t kSizeOfSymmetricAlgorithmHpkeAeadId = 2;
32 
CheckKemId(uint16_t kem_id)33 absl::StatusOr<const EVP_HPKE_KEM*> CheckKemId(uint16_t kem_id) {
34   switch (kem_id) {
35     case EVP_HPKE_DHKEM_X25519_HKDF_SHA256:
36       return EVP_hpke_x25519_hkdf_sha256();
37     default:
38       return absl::UnimplementedError("No support for this KEM ID.");
39   }
40 }
41 
CheckKdfId(uint16_t kdf_id)42 absl::StatusOr<const EVP_HPKE_KDF*> CheckKdfId(uint16_t kdf_id) {
43   switch (kdf_id) {
44     case EVP_HPKE_HKDF_SHA256:
45       return EVP_hpke_hkdf_sha256();
46     default:
47       return absl::UnimplementedError("No support for this KDF ID.");
48   }
49 }
50 
CheckAeadId(uint16_t aead_id)51 absl::StatusOr<const EVP_HPKE_AEAD*> CheckAeadId(uint16_t aead_id) {
52   switch (aead_id) {
53     case EVP_HPKE_AES_128_GCM:
54       return EVP_hpke_aes_128_gcm();
55     case EVP_HPKE_AES_256_GCM:
56       return EVP_hpke_aes_256_gcm();
57     case EVP_HPKE_CHACHA20_POLY1305:
58       return EVP_hpke_chacha20_poly1305();
59     default:
60       return absl::UnimplementedError("No support for this AEAD ID.");
61   }
62 }
63 
64 }  // namespace
65 
ObliviousHttpHeaderKeyConfig(uint8_t key_id,uint16_t kem_id,uint16_t kdf_id,uint16_t aead_id)66 ObliviousHttpHeaderKeyConfig::ObliviousHttpHeaderKeyConfig(uint8_t key_id,
67                                                            uint16_t kem_id,
68                                                            uint16_t kdf_id,
69                                                            uint16_t aead_id)
70     : key_id_(key_id), kem_id_(kem_id), kdf_id_(kdf_id), aead_id_(aead_id) {}
71 
72 absl::StatusOr<ObliviousHttpHeaderKeyConfig>
Create(uint8_t key_id,uint16_t kem_id,uint16_t kdf_id,uint16_t aead_id)73 ObliviousHttpHeaderKeyConfig::Create(uint8_t key_id, uint16_t kem_id,
74                                      uint16_t kdf_id, uint16_t aead_id) {
75   ObliviousHttpHeaderKeyConfig instance(key_id, kem_id, kdf_id, aead_id);
76   auto is_config_ok = instance.ValidateKeyConfig();
77   if (!is_config_ok.ok()) {
78     return is_config_ok;
79   }
80   return instance;
81 }
82 
ValidateKeyConfig() const83 absl::Status ObliviousHttpHeaderKeyConfig::ValidateKeyConfig() const {
84   auto supported_kem = CheckKemId(kem_id_);
85   if (!supported_kem.ok()) {
86     return absl::InvalidArgumentError(
87         absl::StrCat("Unsupported KEM ID:", kem_id_));
88   }
89   auto supported_kdf = CheckKdfId(kdf_id_);
90   if (!supported_kdf.ok()) {
91     return absl::InvalidArgumentError(
92         absl::StrCat("Unsupported KDF ID:", kdf_id_));
93   }
94   auto supported_aead = CheckAeadId(aead_id_);
95   if (!supported_aead.ok()) {
96     return absl::InvalidArgumentError(
97         absl::StrCat("Unsupported AEAD ID:", aead_id_));
98   }
99   return absl::OkStatus();
100 }
101 
GetHpkeKem() const102 const EVP_HPKE_KEM* ObliviousHttpHeaderKeyConfig::GetHpkeKem() const {
103   auto kem = CheckKemId(kem_id_);
104   QUICHE_CHECK_OK(kem.status());
105   return kem.value();
106 }
GetHpkeKdf() const107 const EVP_HPKE_KDF* ObliviousHttpHeaderKeyConfig::GetHpkeKdf() const {
108   auto kdf = CheckKdfId(kdf_id_);
109   QUICHE_CHECK_OK(kdf.status());
110   return kdf.value();
111 }
GetHpkeAead() const112 const EVP_HPKE_AEAD* ObliviousHttpHeaderKeyConfig::GetHpkeAead() const {
113   auto aead = CheckAeadId(aead_id_);
114   QUICHE_CHECK_OK(aead.status());
115   return aead.value();
116 }
117 
SerializeRecipientContextInfo(absl::string_view request_label) const118 std::string ObliviousHttpHeaderKeyConfig::SerializeRecipientContextInfo(
119     absl::string_view request_label) const {
120   uint8_t zero_byte = 0x00;
121   int buf_len = request_label.size() + kHeaderLength + sizeof(zero_byte);
122   std::string info(buf_len, '\0');
123   QuicheDataWriter writer(info.size(), info.data());
124   QUICHE_CHECK(writer.WriteStringPiece(request_label));
125   QUICHE_CHECK(writer.WriteUInt8(zero_byte));  // Zero byte.
126   QUICHE_CHECK(writer.WriteUInt8(key_id_));
127   QUICHE_CHECK(writer.WriteUInt16(kem_id_));
128   QUICHE_CHECK(writer.WriteUInt16(kdf_id_));
129   QUICHE_CHECK(writer.WriteUInt16(aead_id_));
130   return info;
131 }
132 
133 /**
134  * Follows IETF Ohttp spec, section 4.1 (Encapsulation of Requests).
135  * https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-10
136  */
ParseOhttpPayloadHeader(absl::string_view payload_bytes) const137 absl::Status ObliviousHttpHeaderKeyConfig::ParseOhttpPayloadHeader(
138     absl::string_view payload_bytes) const {
139   if (payload_bytes.empty()) {
140     return absl::InvalidArgumentError("Empty request payload.");
141   }
142   QuicheDataReader reader(payload_bytes);
143   return ParseOhttpPayloadHeader(reader);
144 }
145 
ParseOhttpPayloadHeader(QuicheDataReader & reader) const146 absl::Status ObliviousHttpHeaderKeyConfig::ParseOhttpPayloadHeader(
147     QuicheDataReader& reader) const {
148   uint8_t key_id;
149   if (!reader.ReadUInt8(&key_id)) {
150     return absl::InvalidArgumentError("Failed to read key_id from header.");
151   }
152   if (key_id != key_id_) {
153     return absl::InvalidArgumentError(
154         absl::StrCat("KeyID in request:", static_cast<uint16_t>(key_id),
155                      " doesn't match with server's public key "
156                      "configuration KeyID:",
157                      static_cast<uint16_t>(key_id_)));
158   }
159   uint16_t kem_id;
160   if (!reader.ReadUInt16(&kem_id)) {
161     return absl::InvalidArgumentError("Failed to read kem_id from header.");
162   }
163   if (kem_id != kem_id_) {
164     return absl::InvalidArgumentError(
165         absl::StrCat("Received Invalid kemID:", kem_id, " Expected:", kem_id_));
166   }
167   uint16_t kdf_id;
168   if (!reader.ReadUInt16(&kdf_id)) {
169     return absl::InvalidArgumentError("Failed to read kdf_id from header.");
170   }
171   if (kdf_id != kdf_id_) {
172     return absl::InvalidArgumentError(
173         absl::StrCat("Received Invalid kdfID:", kdf_id, " Expected:", kdf_id_));
174   }
175   uint16_t aead_id;
176   if (!reader.ReadUInt16(&aead_id)) {
177     return absl::InvalidArgumentError("Failed to read aead_id from header.");
178   }
179   if (aead_id != aead_id_) {
180     return absl::InvalidArgumentError(absl::StrCat(
181         "Received Invalid aeadID:", aead_id, " Expected:", aead_id_));
182   }
183   return absl::OkStatus();
184 }
185 
186 absl::StatusOr<uint8_t>
ParseKeyIdFromObliviousHttpRequestPayload(absl::string_view payload_bytes)187 ObliviousHttpHeaderKeyConfig::ParseKeyIdFromObliviousHttpRequestPayload(
188     absl::string_view payload_bytes) {
189   if (payload_bytes.empty()) {
190     return absl::InvalidArgumentError("Empty request payload.");
191   }
192   QuicheDataReader reader(payload_bytes);
193   uint8_t key_id;
194   if (!reader.ReadUInt8(&key_id)) {
195     return absl::InvalidArgumentError("Failed to read key_id from payload.");
196   }
197   return key_id;
198 }
199 
SerializeOhttpPayloadHeader() const200 std::string ObliviousHttpHeaderKeyConfig::SerializeOhttpPayloadHeader() const {
201   int buf_len =
202       sizeof(key_id_) + sizeof(kem_id_) + sizeof(kdf_id_) + sizeof(aead_id_);
203   std::string hdr(buf_len, '\0');
204   QuicheDataWriter writer(hdr.size(), hdr.data());
205   QUICHE_CHECK(writer.WriteUInt8(key_id_));
206   QUICHE_CHECK(writer.WriteUInt16(kem_id_));   // kemID
207   QUICHE_CHECK(writer.WriteUInt16(kdf_id_));   // kdfID
208   QUICHE_CHECK(writer.WriteUInt16(aead_id_));  // aeadID
209   return hdr;
210 }
211 
212 namespace {
213 // https://www.rfc-editor.org/rfc/rfc9180#section-7.1
KeyLength(uint16_t kem_id)214 absl::StatusOr<uint16_t> KeyLength(uint16_t kem_id) {
215   auto supported_kem = CheckKemId(kem_id);
216   if (!supported_kem.ok()) {
217     return absl::InvalidArgumentError(absl::StrCat(
218         "Unsupported KEM ID:", kem_id, ". public key length is unknown."));
219   }
220   return EVP_HPKE_KEM_public_key_len(supported_kem.value());
221 }
222 
SerializeOhttpKeyWithPublicKey(uint8_t key_id,absl::string_view public_key,const std::vector<ObliviousHttpHeaderKeyConfig> & ohttp_configs)223 absl::StatusOr<std::string> SerializeOhttpKeyWithPublicKey(
224     uint8_t key_id, absl::string_view public_key,
225     const std::vector<ObliviousHttpHeaderKeyConfig>& ohttp_configs) {
226   auto ohttp_config = ohttp_configs[0];
227   // Check if `ohttp_config` match spec's encoding guidelines.
228   static_assert(sizeof(ohttp_config.GetHpkeKemId()) == kSizeOfHpkeKemId &&
229                     sizeof(ohttp_config.GetHpkeKdfId()) ==
230                         kSizeOfSymmetricAlgorithmHpkeKdfId &&
231                     sizeof(ohttp_config.GetHpkeAeadId()) ==
232                         kSizeOfSymmetricAlgorithmHpkeAeadId,
233                 "Size of HPKE IDs should match RFC specification.");
234 
235   uint16_t symmetric_algs_length =
236       ohttp_configs.size() * (kSizeOfSymmetricAlgorithmHpkeKdfId +
237                               kSizeOfSymmetricAlgorithmHpkeAeadId);
238   int buf_len = sizeof(key_id) + kSizeOfHpkeKemId + public_key.size() +
239                 sizeof(symmetric_algs_length) + symmetric_algs_length;
240   std::string ohttp_key_configuration(buf_len, '\0');
241   QuicheDataWriter writer(ohttp_key_configuration.size(),
242                           ohttp_key_configuration.data());
243   if (!writer.WriteUInt8(key_id)) {
244     return absl::InternalError("Failed to serialize OHTTP key.[key_id]");
245   }
246   if (!writer.WriteUInt16(ohttp_config.GetHpkeKemId())) {
247     return absl::InternalError(
248         "Failed to serialize OHTTP key.[kem_id]");  // kemID.
249   }
250   if (!writer.WriteStringPiece(public_key)) {
251     return absl::InternalError(
252         "Failed to serialize OHTTP key.[public_key]");  // Raw public key.
253   }
254   if (!writer.WriteUInt16(symmetric_algs_length)) {
255     return absl::InternalError(
256         "Failed to serialize OHTTP key.[symmetric_algs_length]");
257   }
258   for (const auto& item : ohttp_configs) {
259     // Check if KEM ID is the same for all the configs stored in `this` for
260     // given `key_id`.
261     if (item.GetHpkeKemId() != ohttp_config.GetHpkeKemId()) {
262       QUICHE_BUG(ohttp_key_configs_builder_parser)
263           << "ObliviousHttpKeyConfigs object cannot hold ConfigMap of "
264              "different KEM IDs:[ "
265           << item.GetHpkeKemId() << "," << ohttp_config.GetHpkeKemId()
266           << " ]for a given key_id:" << static_cast<uint16_t>(key_id);
267     }
268     if (!writer.WriteUInt16(item.GetHpkeKdfId())) {
269       return absl::InternalError(
270           "Failed to serialize OHTTP key.[kdf_id]");  // kdfID.
271     }
272     if (!writer.WriteUInt16(item.GetHpkeAeadId())) {
273       return absl::InternalError(
274           "Failed to serialize OHTTP key.[aead_id]");  // aeadID.
275     }
276   }
277   QUICHE_DCHECK_EQ(writer.remaining(), 0u);
278   return ohttp_key_configuration;
279 }
280 
GetDebugStringForFailedKeyConfig(const ObliviousHttpKeyConfigs::OhttpKeyConfig & failed_key_config)281 std::string GetDebugStringForFailedKeyConfig(
282     const ObliviousHttpKeyConfigs::OhttpKeyConfig& failed_key_config) {
283   std::string debug_string = "[ ";
284   absl::StrAppend(&debug_string,
285                   "key_id:", static_cast<uint16_t>(failed_key_config.key_id),
286                   " , kem_id:", failed_key_config.kem_id,
287                   ". Printing HEX formatted public_key:",
288                   absl::BytesToHexString(failed_key_config.public_key));
289   absl::StrAppend(&debug_string, ", symmetric_algorithms: { ");
290   for (const auto& symmetric_config : failed_key_config.symmetric_algorithms) {
291     absl::StrAppend(&debug_string, "{kdf_id: ", symmetric_config.kdf_id,
292                     ", aead_id:", symmetric_config.aead_id, " }");
293   }
294   absl::StrAppend(&debug_string, " } ]");
295   return debug_string;
296 }
297 
298 // Verifies if the `key_config` contains all valid combinations of [kem_id,
299 // kdf_id, aead_id] that comprises Single Key configuration encoding as
300 // specified in
301 // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#name-a-single-key-configuration.
StoreKeyConfigIfValid(ObliviousHttpKeyConfigs::OhttpKeyConfig key_config,absl::btree_map<uint8_t,std::vector<ObliviousHttpHeaderKeyConfig>,std::greater<uint8_t>> & configs,absl::flat_hash_map<uint8_t,std::string> & keys)302 absl::Status StoreKeyConfigIfValid(
303     ObliviousHttpKeyConfigs::OhttpKeyConfig key_config,
304     absl::btree_map<uint8_t, std::vector<ObliviousHttpHeaderKeyConfig>,
305                     std::greater<uint8_t>>& configs,
306     absl::flat_hash_map<uint8_t, std::string>& keys) {
307   if (!CheckKemId(key_config.kem_id).ok() ||
308       key_config.public_key.size() != KeyLength(key_config.kem_id).value()) {
309     QUICHE_LOG(ERROR) << "Failed to process: "
310                       << GetDebugStringForFailedKeyConfig(key_config);
311     return absl::InvalidArgumentError(
312         absl::StrCat("Invalid key_config! [KEM ID:", key_config.kem_id, "]"));
313   }
314   for (const auto& symmetric_config : key_config.symmetric_algorithms) {
315     if (!CheckKdfId(symmetric_config.kdf_id).ok() ||
316         !CheckAeadId(symmetric_config.aead_id).ok()) {
317       QUICHE_LOG(ERROR) << "Failed to process: "
318                         << GetDebugStringForFailedKeyConfig(key_config);
319       return absl::InvalidArgumentError(
320           absl::StrCat("Invalid key_config! [KDF ID:", symmetric_config.kdf_id,
321                        ", AEAD ID:", symmetric_config.aead_id, "]"));
322     }
323     auto ohttp_config = ObliviousHttpHeaderKeyConfig::Create(
324         key_config.key_id, key_config.kem_id, symmetric_config.kdf_id,
325         symmetric_config.aead_id);
326     if (ohttp_config.ok()) {
327       configs[key_config.key_id].emplace_back(std::move(ohttp_config.value()));
328     }
329   }
330   keys.emplace(key_config.key_id, std::move(key_config.public_key));
331   return absl::OkStatus();
332 }
333 
334 }  // namespace
335 
336 absl::StatusOr<ObliviousHttpKeyConfigs>
ParseConcatenatedKeys(absl::string_view key_config)337 ObliviousHttpKeyConfigs::ParseConcatenatedKeys(absl::string_view key_config) {
338   ConfigMap configs;
339   PublicKeyMap keys;
340   auto reader = QuicheDataReader(key_config);
341   while (!reader.IsDoneReading()) {
342     absl::Status status = ReadSingleKeyConfig(reader, configs, keys);
343     if (!status.ok()) return status;
344   }
345   return ObliviousHttpKeyConfigs(std::move(configs), std::move(keys));
346 }
347 
Create(absl::flat_hash_set<ObliviousHttpKeyConfigs::OhttpKeyConfig> ohttp_key_configs)348 absl::StatusOr<ObliviousHttpKeyConfigs> ObliviousHttpKeyConfigs::Create(
349     absl::flat_hash_set<ObliviousHttpKeyConfigs::OhttpKeyConfig>
350         ohttp_key_configs) {
351   if (ohttp_key_configs.empty()) {
352     return absl::InvalidArgumentError("Empty input.");
353   }
354   ConfigMap configs_map;
355   PublicKeyMap keys_map;
356   for (auto& ohttp_key_config : ohttp_key_configs) {
357     auto result = StoreKeyConfigIfValid(std::move(ohttp_key_config),
358                                         configs_map, keys_map);
359     if (!result.ok()) {
360       return result;
361     }
362   }
363   auto oblivious_configs =
364       ObliviousHttpKeyConfigs(std::move(configs_map), std::move(keys_map));
365   return oblivious_configs;
366 }
367 
Create(const ObliviousHttpHeaderKeyConfig & single_key_config,absl::string_view public_key)368 absl::StatusOr<ObliviousHttpKeyConfigs> ObliviousHttpKeyConfigs::Create(
369     const ObliviousHttpHeaderKeyConfig& single_key_config,
370     absl::string_view public_key) {
371   if (public_key.empty()) {
372     return absl::InvalidArgumentError("Empty input.");
373   }
374 
375   if (auto key_length = KeyLength(single_key_config.GetHpkeKemId());
376       public_key.size() != key_length.value()) {
377     return absl::InvalidArgumentError(absl::StrCat(
378         "Invalid key. Key size mismatch. Expected:", key_length.value(),
379         " Actual:", public_key.size()));
380   }
381 
382   ConfigMap configs;
383   PublicKeyMap keys;
384   uint8_t key_id = single_key_config.GetKeyId();
385   keys.emplace(key_id, public_key);
386   configs[key_id].emplace_back(std::move(single_key_config));
387   return ObliviousHttpKeyConfigs(std::move(configs), std::move(keys));
388 }
389 
GenerateConcatenatedKeys() const390 absl::StatusOr<std::string> ObliviousHttpKeyConfigs::GenerateConcatenatedKeys()
391     const {
392   std::string concatenated_keys;
393   for (const auto& [key_id, ohttp_configs] : configs_) {
394     auto key = public_keys_.find(key_id);
395     if (key == public_keys_.end()) {
396       return absl::InternalError(
397           "Failed to serialize. No public key found for key_id");
398     }
399     auto serialized =
400         SerializeOhttpKeyWithPublicKey(key_id, key->second, ohttp_configs);
401     if (!serialized.ok()) {
402       return absl::InternalError("Failed to serialize OHTTP key configs.");
403     }
404     absl::StrAppend(&concatenated_keys, serialized.value());
405   }
406   return concatenated_keys;
407 }
408 
PreferredConfig() const409 ObliviousHttpHeaderKeyConfig ObliviousHttpKeyConfigs::PreferredConfig() const {
410   // configs_ is forced to have at least one object during construction.
411   return configs_.begin()->second.front();
412 }
413 
GetPublicKeyForId(uint8_t key_id) const414 absl::StatusOr<absl::string_view> ObliviousHttpKeyConfigs::GetPublicKeyForId(
415     uint8_t key_id) const {
416   auto key = public_keys_.find(key_id);
417   if (key == public_keys_.end()) {
418     return absl::NotFoundError("No public key found for key_id");
419   }
420   return key->second;
421 }
422 
ReadSingleKeyConfig(QuicheDataReader & reader,ConfigMap & configs,PublicKeyMap & keys)423 absl::Status ObliviousHttpKeyConfigs::ReadSingleKeyConfig(
424     QuicheDataReader& reader, ConfigMap& configs, PublicKeyMap& keys) {
425   uint8_t key_id;
426   uint16_t kem_id;
427   // First byte: key_id; next two bytes: kem_id.
428   if (!reader.ReadUInt8(&key_id) || !reader.ReadUInt16(&kem_id)) {
429     return absl::InvalidArgumentError("Invalid key_config!");
430   }
431 
432   // Public key length depends on the kem_id.
433   auto maybe_key_length = KeyLength(kem_id);
434   if (!maybe_key_length.ok()) {
435     return maybe_key_length.status();
436   }
437   const int key_length = maybe_key_length.value();
438   std::string key_str(key_length, '\0');
439   if (!reader.ReadBytes(key_str.data(), key_length)) {
440     return absl::InvalidArgumentError("Invalid key_config!");
441   }
442   if (!keys.insert({key_id, std::move(key_str)}).second) {
443     return absl::InvalidArgumentError("Duplicate key_id's in key_config!");
444   }
445 
446   // Extract the algorithms for this public key.
447   absl::string_view alg_bytes;
448   // Read the 16-bit length, then read that many bytes into alg_bytes.
449   if (!reader.ReadStringPiece16(&alg_bytes)) {
450     return absl::InvalidArgumentError("Invalid key_config!");
451   }
452   QuicheDataReader sub_reader(alg_bytes);
453   while (!sub_reader.IsDoneReading()) {
454     uint16_t kdf_id;
455     uint16_t aead_id;
456     if (!sub_reader.ReadUInt16(&kdf_id) || !sub_reader.ReadUInt16(&aead_id)) {
457       return absl::InvalidArgumentError("Invalid key_config!");
458     }
459 
460     absl::StatusOr<ObliviousHttpHeaderKeyConfig> maybe_cfg =
461         ObliviousHttpHeaderKeyConfig::Create(key_id, kem_id, kdf_id, aead_id);
462     if (!maybe_cfg.ok()) {
463       // TODO(kmg): Add support to ignore key types in the server response that
464       // aren't supported by the client.
465       return maybe_cfg.status();
466     }
467     configs[key_id].emplace_back(std::move(maybe_cfg.value()));
468   }
469   return absl::OkStatus();
470 }
471 
472 }  // namespace quiche
473