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