blob: 37599a01d21483bd4facd0eed19eb86739aee3ea [file] [log] [blame] [edit]
#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_