| #ifndef QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_ |
| #define QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_ |
| |
| #include <stdint.h> |
| |
| #include "absl/container/btree_map.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/string_view.h" |
| #include "openssl/base.h" |
| #include "quiche/common/platform/api/quiche_export.h" |
| #include "quiche/common/quiche_data_reader.h" |
| |
| namespace quiche { |
| |
| class QUICHE_EXPORT ObliviousHttpHeaderKeyConfig { |
| public: |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-4.2 |
| static constexpr absl::string_view kOhttpRequestLabel = |
| "message/bhttp request"; |
| static constexpr absl::string_view kOhttpResponseLabel = |
| "message/bhttp response"; |
| // Length of the Oblivious HTTP header. |
| static constexpr uint32_t kHeaderLength = |
| sizeof(uint8_t) + (3 * sizeof(uint16_t)); |
| static constexpr absl::string_view kKeyHkdfInfo = "key"; |
| static constexpr absl::string_view kNonceHkdfInfo = "nonce"; |
| |
| static absl::StatusOr<ObliviousHttpHeaderKeyConfig> Create(uint8_t key_id, |
| uint16_t kem_id, |
| uint16_t kdf_id, |
| uint16_t aead_id); |
| |
| // Copyable to support stack allocated pass-by-value for trivial data members. |
| ObliviousHttpHeaderKeyConfig(const ObliviousHttpHeaderKeyConfig& other) = |
| default; |
| ObliviousHttpHeaderKeyConfig& operator=( |
| const ObliviousHttpHeaderKeyConfig& other) = default; |
| |
| // Movable. |
| ObliviousHttpHeaderKeyConfig(ObliviousHttpHeaderKeyConfig&& other) = default; |
| ObliviousHttpHeaderKeyConfig& operator=( |
| ObliviousHttpHeaderKeyConfig&& other) = default; |
| |
| ~ObliviousHttpHeaderKeyConfig() = default; |
| |
| const EVP_HPKE_KEM* GetHpkeKem() const; |
| const EVP_HPKE_KDF* GetHpkeKdf() const; |
| const EVP_HPKE_AEAD* GetHpkeAead() const; |
| |
| uint8_t GetKeyId() const { return key_id_; } |
| uint16_t GetHpkeKemId() const { return kem_id_; } |
| uint16_t GetHpkeKdfId() const { return kdf_id_; } |
| uint16_t GetHpkeAeadId() const { return aead_id_; } |
| |
| // Build HPKE context info [request_label, 0x00, keyID(1 byte), |
| // kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)] in network byte order and |
| // return a sequence of bytes(bytestring). |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-10 |
| std::string SerializeRecipientContextInfo( |
| absl::string_view request_label = |
| ObliviousHttpHeaderKeyConfig::kOhttpRequestLabel) const; |
| |
| // Parses the below Header |
| // [keyID(1 byte), kemID(2 bytes), kdfID(2 bytes), aeadID(2 bytes)] |
| // from the payload received in Ohttp Request, and verifies that these values |
| // match with the info stored in `this` namely [key_id_, kem_id_, kdf_id_, |
| // aead_id_] |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#section-4.1-7 |
| absl::Status ParseOhttpPayloadHeader(absl::string_view payload_bytes) const; |
| |
| // Parses the Oblivious HTTP header [keyID(1 byte), kemID(2 bytes), kdfID(2 |
| // bytes), aeadID(2 bytes)] from the buffer initialized within |
| // `QuicheDataReader`, and verifies these values against instantiated class |
| // data namely [key_id_, kem_id_, kdf_id_, aead_id_] for a match. On |
| // success(i.e., if matched successfully), leaves `reader` pointing at the |
| // first byte after the header. |
| absl::Status ParseOhttpPayloadHeader(QuicheDataReader& reader) const; |
| |
| // Extracts Key ID from the OHTTP Request payload. |
| static absl::StatusOr<uint8_t> ParseKeyIdFromObliviousHttpRequestPayload( |
| absl::string_view payload_bytes); |
| |
| // Build Request header according to network byte order and return string. |
| std::string SerializeOhttpPayloadHeader() const; |
| |
| private: |
| // Constructor |
| explicit ObliviousHttpHeaderKeyConfig(uint8_t key_id, uint16_t kem_id, |
| uint16_t kdf_id, uint16_t aead_id); |
| |
| // Helps validate Key configuration for supported schemes. |
| absl::Status ValidateKeyConfig() const; |
| |
| // Public Key configuration hosted by Gateway to facilitate Oblivious HTTP |
| // HPKE encryption. |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#name-key-configuration-encoding |
| uint8_t key_id_; |
| uint16_t kem_id_; |
| uint16_t kdf_id_; |
| uint16_t aead_id_; |
| }; |
| |
| // Contains multiple ObliviousHttpHeaderKeyConfig objects and associated private |
| // keys. An ObliviousHttpHeaderKeyConfigs object can be constructed from the |
| // "Key Configuration" defined in the Oblivious HTTP spec. Multiple key |
| // configurations maybe be supported by the server. |
| // |
| // See https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3 |
| // for details of the "Key Configuration" spec. |
| // |
| // ObliviousHttpKeyConfigs objects are immutable after construction. |
| class QUICHE_EXPORT ObliviousHttpKeyConfigs { |
| public: |
| // Below two structures follow the Single key configuration spec in OHTTP RFC. |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-06.html#name-a-single-key-configuration |
| struct SymmetricAlgorithmsConfig { |
| uint16_t kdf_id; |
| uint16_t aead_id; |
| |
| bool operator==(const SymmetricAlgorithmsConfig& other) const { |
| return kdf_id == other.kdf_id && aead_id == other.aead_id; |
| } |
| |
| template <typename H> |
| friend H AbslHashValue(H h, const SymmetricAlgorithmsConfig& sym_alg_cfg) { |
| return H::combine(std::move(h), sym_alg_cfg.kdf_id, sym_alg_cfg.aead_id); |
| } |
| }; |
| |
| struct OhttpKeyConfig { |
| uint8_t key_id; |
| uint16_t kem_id; |
| std::string public_key; // Raw byte string. |
| absl::flat_hash_set<SymmetricAlgorithmsConfig> symmetric_algorithms; |
| |
| bool operator==(const OhttpKeyConfig& other) const { |
| return key_id == other.key_id && kem_id == other.kem_id && |
| public_key == other.public_key && |
| symmetric_algorithms == other.symmetric_algorithms; |
| } |
| |
| template <typename H> |
| friend H AbslHashValue(H h, const OhttpKeyConfig& ohttp_key_cfg) { |
| return H::combine(std::move(h), ohttp_key_cfg.key_id, |
| ohttp_key_cfg.kem_id, ohttp_key_cfg.public_key, |
| ohttp_key_cfg.symmetric_algorithms); |
| } |
| }; |
| |
| // Parses the "application/ohttp-keys" media type, which is a byte string |
| // formatted according to the spec: |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3 |
| static absl::StatusOr<ObliviousHttpKeyConfigs> ParseConcatenatedKeys( |
| absl::string_view key_configs); |
| |
| // Builds `ObliviousHttpKeyConfigs` with multiple key configurations, each |
| // made up of Single Key Configuration([{key_id, kem_id, public key}, |
| // Set<SymmetricAlgos>]) encoding specified in section 3. |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-03.html#name-key-configuration-encoding |
| // @params: Set<{key_id, kem_id, public key, Set<HPKE Symmetric Algorithms>>. |
| // @return: When given all valid configs supported by BoringSSL, builds and |
| // returns `ObliviousHttpKeyConfigs`. If any one of the input configs are |
| // invalid or unsupported by BSSL, returns an error. |
| // @note: Subsequently, To get concatenated keys[contiguous byte string of |
| // keys], use `GenerateConcatenatedKeys()`. This output can inturn be parsed |
| // by `ObliviousHttpKeyConfigs::ParseConcatenatedKeys` on client side. |
| static absl::StatusOr<ObliviousHttpKeyConfigs> Create( |
| absl::flat_hash_set<OhttpKeyConfig> ohttp_key_configs); |
| |
| // Builds `ObliviousHttpKeyConfigs` with given public_key and Single key |
| // configuration specified in `ObliviousHttpHeaderKeyConfig` object. After |
| // successful `Create`, clients can call `GenerateConcatenatedKeys()` to build |
| // the Single key config. |
| static absl::StatusOr<ObliviousHttpKeyConfigs> Create( |
| const ObliviousHttpHeaderKeyConfig& single_key_config, |
| absl::string_view public_key); |
| |
| // Generates byte string corresponding to "application/ohttp-keys" media type. |
| // https://www.ietf.org/archive/id/draft-ietf-ohai-ohttp-04.html#section-3 |
| absl::StatusOr<std::string> GenerateConcatenatedKeys() const; |
| |
| int NumKeys() const { return public_keys_.size(); } |
| |
| // Returns a preferred config to use. The preferred key is the key with |
| // the highest key_id. If more than one configuration exists for the |
| // preferred key any configuration may be returned. |
| // |
| // These methods are useful in the (common) case where only one key |
| // configuration is supported by the server. |
| ObliviousHttpHeaderKeyConfig PreferredConfig() const; |
| |
| absl::StatusOr<absl::string_view> GetPublicKeyForId(uint8_t key_id) const; |
| |
| // TODO(kmg): Add methods to somehow access other non-preferred key |
| // configurations. |
| |
| private: |
| using PublicKeyMap = absl::flat_hash_map<uint8_t, std::string>; |
| using ConfigMap = |
| absl::btree_map<uint8_t, std::vector<ObliviousHttpHeaderKeyConfig>, |
| std::greater<uint8_t>>; |
| |
| ObliviousHttpKeyConfigs(ConfigMap cm, PublicKeyMap km) |
| : configs_(std::move(cm)), public_keys_(std::move(km)) {} |
| |
| static absl::Status ReadSingleKeyConfig(QuicheDataReader& reader, |
| ConfigMap& configs, |
| PublicKeyMap& keys); |
| |
| // A mapping from key_id to ObliviousHttpHeaderKeyConfig objects for that key. |
| const ConfigMap configs_; |
| |
| // A mapping from key_id to the public key for that key_id. |
| const PublicKeyMap public_keys_; |
| }; |
| |
| } // namespace quiche |
| |
| #endif // QUICHE_OBLIVIOUS_HTTP_COMMON_OBLIVIOUS_HTTP_HEADER_KEY_CONFIG_H_ |