1 #ifndef QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_
2 #define QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_
3 
4 #include <stdint.h>
5 
6 #include "absl/container/btree_map.h"
7 #include "absl/container/flat_hash_map.h"
8 #include "absl/container/flat_hash_set.h"
9 #include "absl/status/status.h"
10 #include "absl/status/statusor.h"
11 #include "absl/strings/string_view.h"
12 #include "openssl/base.h"
13 #include "quiche/common/platform/api/quiche_export.h"
14 #include "quiche/common/quiche_data_reader.h"
15 
16 namespace quiche {
17 
18 class QUICHE_EXPORT ObliviousHttpHeaderKeyConfig {
19  public:
20   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-4.2
21   static constexpr absl::string_view kOhttpRequestLabel =
22       "message/bhttp request";
23   static constexpr absl::string_view kOhttpResponseLabel =
24       "message/bhttp response";
25   // Length of the Oblivious HTTP header.
26   static constexpr uint32_t kHeaderLength =
27       sizeof(uint8_t) + (3 * sizeof(uint16_t));
28   static constexpr absl::string_view kKeyHkdfInfo = "key";
29   static constexpr absl::string_view kNonceHkdfInfo = "nonce";
30 
31   static absl::StatusOr<ObliviousHttpHeaderKeyConfig> Create(uint8_t key_id,
32                                                              uint16_t kem_id,
33                                                              uint16_t kdf_id,
34                                                              uint16_t aead_id);
35 
36   // Copyable to support stack allocated pass-by-value for trivial data members.
37   ObliviousHttpHeaderKeyConfig(const ObliviousHttpHeaderKeyConfig& other) =
38       default;
39   ObliviousHttpHeaderKeyConfig& operator=(
40       const ObliviousHttpHeaderKeyConfig& other) = default;
41 
42   // Movable.
43   ObliviousHttpHeaderKeyConfig(ObliviousHttpHeaderKeyConfig&& other) = default;
44   ObliviousHttpHeaderKeyConfig& operator=(
45       ObliviousHttpHeaderKeyConfig&& other) = default;
46 
47   ~ObliviousHttpHeaderKeyConfig() = default;
48 
49   const EVP_HPKE_KEM* GetHpkeKem() const;
50   const EVP_HPKE_KDF* GetHpkeKdf() const;
51   const EVP_HPKE_AEAD* GetHpkeAead() const;
52 
GetKeyId()53   uint8_t GetKeyId() const { return key_id_; }
GetHpkeKemId()54   uint16_t GetHpkeKemId() const { return kem_id_; }
GetHpkeKdfId()55   uint16_t GetHpkeKdfId() const { return kdf_id_; }
GetHpkeAeadId()56   uint16_t GetHpkeAeadId() const { return aead_id_; }
57 
58   // Build HPKE context info [request_label, 0x00, keyID(1 byte),
59   // kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)] in network byte order and
60   // return a sequence of bytes(bytestring).
61   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-10
62   std::string SerializeRecipientContextInfo(
63       absl::string_view request_label =
64           ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel) const;
65 
66   // Parses the below Header
67   // [keyID(1 byte), kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)]
68   // from the payload received in Ohttp Request, and verifies that these values
69   // match with the info stored in `this` namely [key_id_, kem_id_, kdf_id_,
70   // aead_id_]
71   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-7
72   absl::Status ParseOhttpPayloadHeader(absl::string_view payload_bytes) const;
73 
74   // Parses the Oblivious HTTP header [keyID(1 byte), kemID(2 bytes), kdfID(2
75   // bytes), aeadID(2 bytes)] from the buffer initialized within
76   // `QuicheDataReader`, and  verifies these values against instantiated class
77   // data namely [key_id_, kem_id_, kdf_id_, aead_id_] for a match. On
78   // success(i.e., if matched successfully), leaves `reader` pointing at the
79   // first byte after the header.
80   absl::Status ParseOhttpPayloadHeader(QuicheDataReader& reader) const;
81 
82   // Extracts Key ID from the OHTTP Request payload.
83   static absl::StatusOr<uint8_t> ParseKeyIdFromObliviousHttpRequestPayload(
84       absl::string_view payload_bytes);
85 
86   // Build Request header according to network byte order and return string.
87   std::string SerializeOhttpPayloadHeader() const;
88 
89  private:
90   // Constructor
91   explicit ObliviousHttpHeaderKeyConfig(uint8_t key_id, uint16_t kem_id,
92                                         uint16_t kdf_id, uint16_t aead_id);
93 
94   // Helps validate Key configuration for supported schemes.
95   absl::Status ValidateKeyConfig() const;
96 
97   // Public Key configuration hosted by Gateway to facilitate Oblivious HTTP
98   // HPKE encryption.
99   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#name-key-configuration-encoding
100   uint8_t key_id_;
101   uint16_t kem_id_;
102   uint16_t kdf_id_;
103   uint16_t aead_id_;
104 };
105 
106 // Contains multiple ObliviousHttpHeaderKeyConfig objects and associated private
107 // keys.  An ObliviousHttpHeaderKeyConfigs object can be constructed from the
108 // "Key Configuration" defined in the Oblivious HTTP spec.  Multiple key
109 // configurations maybe be supported by the server.
110 //
111 // See https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3
112 // for details of the "Key Configuration" spec.
113 //
114 // ObliviousHttpKeyConfigs objects are immutable after construction.
115 class QUICHE_EXPORT ObliviousHttpKeyConfigs {
116  public:
117   // Below two structures follow the Single key configuration spec in OHTTP RFC.
118   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-06.html#name-a-single-key-configuration
119   struct SymmetricAlgorithmsConfig {
120     uint16_t kdf_id;
121     uint16_t aead_id;
122 
123     bool operator==(const SymmetricAlgorithmsConfig& other) const {
124       return kdf_id == other.kdf_id && aead_id == other.aead_id;
125     }
126 
127     template <typename H>
AbslHashValueSymmetricAlgorithmsConfig128     friend H AbslHashValue(H h, const SymmetricAlgorithmsConfig& sym_alg_cfg) {
129       return H::combine(std::move(h), sym_alg_cfg.kdf_id, sym_alg_cfg.aead_id);
130     }
131   };
132 
133   struct OhttpKeyConfig {
134     uint8_t key_id;
135     uint16_t kem_id;
136     std::string public_key;  // Raw byte string.
137     absl::flat_hash_set<SymmetricAlgorithmsConfig> symmetric_algorithms;
138 
139     bool operator==(const OhttpKeyConfig& other) const {
140       return key_id == other.key_id && kem_id == other.kem_id &&
141              public_key == other.public_key &&
142              symmetric_algorithms == other.symmetric_algorithms;
143     }
144 
145     template <typename H>
AbslHashValueOhttpKeyConfig146     friend H AbslHashValue(H h, const OhttpKeyConfig& ohttp_key_cfg) {
147       return H::combine(std::move(h), ohttp_key_cfg.key_id,
148                         ohttp_key_cfg.kem_id, ohttp_key_cfg.public_key,
149                         ohttp_key_cfg.symmetric_algorithms);
150     }
151   };
152 
153   // Parses the "application/ohttp-keys" media type, which is a byte string
154   // formatted according to the spec:
155   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3
156   static absl::StatusOr<ObliviousHttpKeyConfigs> ParseConcatenatedKeys(
157       absl::string_view key_configs);
158 
159   // Builds `ObliviousHttpKeyConfigs` with multiple key configurations, each
160   // made up of Single Key Configuration([{key_id, kem_id, public key},
161   // Set<SymmetricAlgos>]) encoding specified in section 3.
162   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#name-key-configuration-encoding
163   // @params: Set<{key_id, kem_id, public key, Set<HPKE Symmetric Algorithms>>.
164   // @return: When given all valid configs supported by BoringSSL, builds and
165   // returns `ObliviousHttpKeyConfigs`. If any one of the input configs are
166   // invalid or unsupported by BSSL, returns an error.
167   // @note: Subsequently, To get concatenated keys[contiguous byte string of
168   // keys], use `GenerateConcatenatedKeys()`. This output can inturn be parsed
169   // by `ObliviousHttpKeyConfigs::ParseConcatenatedKeys` on client side.
170   static absl::StatusOr<ObliviousHttpKeyConfigs> Create(
171       absl::flat_hash_set<OhttpKeyConfig> ohttp_key_configs);
172 
173   // Builds `ObliviousHttpKeyConfigs` with given public_key and Single key
174   // configuration specified in `ObliviousHttpHeaderKeyConfig` object. After
175   // successful `Create`, clients can call `GenerateConcatenatedKeys()` to build
176   // the Single key config.
177   static absl::StatusOr<ObliviousHttpKeyConfigs> Create(
178       const ObliviousHttpHeaderKeyConfig& single_key_config,
179       absl::string_view public_key);
180 
181   // Generates byte string corresponding to "application/ohttp-keys" media type.
182   // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3
183   absl::StatusOr<std::string> GenerateConcatenatedKeys() const;
184 
NumKeys()185   int NumKeys() const { return public_keys_.size(); }
186 
187   // Returns a preferred config to use.  The preferred key is the key with
188   // the highest key_id.  If more than one configuration exists for the
189   // preferred key any configuration may be returned.
190   //
191   // These methods are useful in the (common) case where only one key
192   // configuration is supported by the server.
193   ObliviousHttpHeaderKeyConfig PreferredConfig() const;
194 
195   absl::StatusOr<absl::string_view> GetPublicKeyForId(uint8_t key_id) const;
196 
197   // TODO(kmg): Add methods to somehow access other non-preferred key
198   // configurations.
199 
200  private:
201   using PublicKeyMap = absl::flat_hash_map<uint8_t, std::string>;
202   using ConfigMap =
203       absl::btree_map<uint8_t, std::vector<ObliviousHttpHeaderKeyConfig>,
204                       std::greater<uint8_t>>;
205 
ObliviousHttpKeyConfigs(ConfigMap cm,PublicKeyMap km)206   ObliviousHttpKeyConfigs(ConfigMap cm, PublicKeyMap km)
207       : configs_(std::move(cm)), public_keys_(std::move(km)) {}
208 
209   static absl::Status ReadSingleKeyConfig(QuicheDataReader& reader,
210                                           ConfigMap& configs,
211                                           PublicKeyMap& keys);
212 
213   // A mapping from key_id to ObliviousHttpHeaderKeyConfig objects for that key.
214   const ConfigMap configs_;
215 
216   // A mapping from key_id to the public key for that key_id.
217   const PublicKeyMap public_keys_;
218 };
219 
220 }  // namespace quiche
221 
222 #endif  // QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_
223