11#include <botan/internal/hybrid_public_key.h>
13#include <botan/pk_algs.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/kex_to_kem_adapter.h>
17#include <botan/internal/pk_ops_impl.h>
18#include <botan/internal/stl_util.h>
24std::vector<std::pair<std::string, std::string>> algorithm_specs_for_group(Group_Params group) {
27 switch(group.code()) {
28 case Group_Params::HYBRID_X25519_KYBER_512_R3_OQS:
29 case Group_Params::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE:
30 return {{
"Curve25519",
"Curve25519"}, {
"Kyber",
"Kyber-512-r3"}};
31 case Group_Params::HYBRID_X25519_KYBER_768_R3_OQS:
32 return {{
"Curve25519",
"Curve25519"}, {
"Kyber",
"Kyber-768-r3"}};
33 case Group_Params::HYBRID_X448_KYBER_768_R3_OQS:
34 return {{
"X448",
"X448"}, {
"Kyber",
"Kyber-768-r3"}};
35 case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS:
36 return {{
"Curve25519",
"Curve25519"}, {
"FrodoKEM",
"eFrodoKEM-640-SHAKE"}};
37 case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS:
38 return {{
"Curve25519",
"Curve25519"}, {
"FrodoKEM",
"eFrodoKEM-640-AES"}};
39 case Group_Params::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS:
40 return {{
"X448",
"X448"}, {
"FrodoKEM",
"eFrodoKEM-976-SHAKE"}};
41 case Group_Params::HYBRID_X448_eFRODOKEM_976_AES_OQS:
42 return {{
"X448",
"X448"}, {
"FrodoKEM",
"eFrodoKEM-976-AES"}};
44 case Group_Params::HYBRID_SECP256R1_KYBER_512_R3_OQS:
45 return {{
"ECDH",
"secp256r1"}, {
"Kyber",
"Kyber-512-r3"}};
46 case Group_Params::HYBRID_SECP256R1_KYBER_768_R3_OQS:
47 return {{
"ECDH",
"secp256r1"}, {
"Kyber",
"Kyber-768-r3"}};
48 case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS:
49 return {{
"ECDH",
"secp256r1"}, {
"FrodoKEM",
"eFrodoKEM-640-SHAKE"}};
50 case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS:
51 return {{
"ECDH",
"secp256r1"}, {
"FrodoKEM",
"eFrodoKEM-640-AES"}};
53 case Group_Params::HYBRID_SECP384R1_KYBER_768_R3_OQS:
54 return {{
"ECDH",
"secp384r1"}, {
"Kyber",
"Kyber-768-r3"}};
55 case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS:
56 return {{
"ECDH",
"secp384r1"}, {
"FrodoKEM",
"eFrodoKEM-976-SHAKE"}};
57 case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS:
58 return {{
"ECDH",
"secp384r1"}, {
"FrodoKEM",
"eFrodoKEM-976-AES"}};
60 case Group_Params::HYBRID_SECP521R1_KYBER_1024_R3_OQS:
61 return {{
"ECDH",
"secp521r1"}, {
"Kyber",
"Kyber-1024-r3"}};
62 case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS:
63 return {{
"ECDH",
"secp521r1"}, {
"FrodoKEM",
"eFrodoKEM-1344-SHAKE"}};
64 case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS:
65 return {{
"ECDH",
"secp521r1"}, {
"FrodoKEM",
"eFrodoKEM-1344-AES"}};
72std::vector<AlgorithmIdentifier> algorithm_identifiers_for_group(Group_Params group) {
75 const auto specs = algorithm_specs_for_group(group);
76 std::vector<AlgorithmIdentifier> result;
77 result.reserve(specs.size());
86 for(
const auto& spec : specs) {
93std::vector<size_t> public_value_lengths_for_group(Group_Params group) {
100 switch(group.code()) {
101 case Group_Params::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE:
102 case Group_Params::HYBRID_X25519_KYBER_512_R3_OQS:
104 case Group_Params::HYBRID_X25519_KYBER_768_R3_OQS:
106 case Group_Params::HYBRID_X448_KYBER_768_R3_OQS:
108 case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS:
110 case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS:
112 case Group_Params::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS:
114 case Group_Params::HYBRID_X448_eFRODOKEM_976_AES_OQS:
117 case Group_Params::HYBRID_SECP256R1_KYBER_512_R3_OQS:
119 case Group_Params::HYBRID_SECP256R1_KYBER_768_R3_OQS:
121 case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS:
123 case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS:
126 case Group_Params::HYBRID_SECP384R1_KYBER_768_R3_OQS:
128 case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS:
130 case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS:
133 case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS:
135 case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS:
137 case Group_Params::HYBRID_SECP521R1_KYBER_1024_R3_OQS:
148 Group_Params group, std::span<const uint8_t> concatenated_public_values) {
149 const auto public_value_lengths = public_value_lengths_for_group(group);
150 auto alg_ids = algorithm_identifiers_for_group(group);
153 const auto expected_public_values_length =
154 reduce(public_value_lengths,
size_t(0), [](
size_t acc,
size_t len) {
return acc + len; });
155 BOTAN_ARG_CHECK(expected_public_values_length == concatenated_public_values.size(),
156 "Concatenated public values have an unexpected length");
158 BufferSlicer public_value_slicer(concatenated_public_values);
159 std::vector<std::unique_ptr<Public_Key>> pks;
160 for(
size_t idx = 0; idx < alg_ids.size(); ++idx) {
161 pks.emplace_back(
load_public_key(alg_ids[idx], public_value_slicer.
take(public_value_lengths[idx])));
164 return std::make_unique<Hybrid_KEM_PublicKey>(std::move(pks));
168 BOTAN_ARG_CHECK(pks.size() >= 2,
"List of public keys must include at least two keys");
169 BOTAN_ARG_CHECK(std::all_of(pks.begin(), pks.end(), [](
const auto& pk) { return pk != nullptr; }),
170 "List of public keys contains a nullptr");
174 return pk->supports_operation(PublicKeyOperation::KeyEncapsulation) ||
175 pk->supports_operation(PublicKeyOperation::KeyAgreement);
177 "Some provided public key is not compatible with this hybrid wrapper");
180 pks.begin(), pks.end(), std::back_inserter(
m_public_keys), [](
auto& key) -> std::unique_ptr<Public_Key> {
181 if(key->supports_operation(PublicKeyOperation::KeyAgreement) &&
182 !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) {
183 return std::make_unique<KEX_to_KEM_Adapter_PublicKey>(std::move(key));
185 return std::move(key);
190 reduce(m_public_keys,
size_t(0), [](
size_t kl,
const auto& key) {
return std::max(kl, key->key_length()); });
191 m_estimated_strength =
reduce(
192 m_public_keys,
size_t(0), [](
size_t es,
const auto& key) {
return std::max(es, key->estimated_strength()); });
195std::string Hybrid_KEM_PublicKey::algo_name()
const {
196 std::ostringstream algo_name(
"Hybrid(");
197 for(
size_t i = 0; i < m_public_keys.size(); ++i) {
201 algo_name << m_public_keys[i]->algo_name();
204 return algo_name.str();
207size_t Hybrid_KEM_PublicKey::estimated_strength()
const {
208 return m_estimated_strength;
211size_t Hybrid_KEM_PublicKey::key_length()
const {
216 return reduce(m_public_keys,
true, [&](
bool ckr,
const auto& key) {
return ckr && key->check_key(rng, strong); });
223std::vector<uint8_t> Hybrid_KEM_PublicKey::public_key_bits()
const {
229 return public_value();
232std::vector<uint8_t> Hybrid_KEM_PublicKey::public_value()
const {
240 return reduce(m_public_keys, std::vector<uint8_t>(), [](
auto pkb,
const auto& key) {
249 return concat(pkb, key->public_key_bits());
254 std::vector<std::unique_ptr<Private_Key>> new_private_keys;
256 m_public_keys.begin(), m_public_keys.end(), std::back_inserter(new_private_keys), [&](
const auto& public_key) {
257 return public_key->generate_another(rng);
259 return std::make_unique<Hybrid_KEM_PrivateKey>(std::move(new_private_keys));
271 std::string_view kdf,
272 std::string_view provider) :
273 PK_Ops::KEM_Encryption_with_KDF(kdf), m_raw_kem_shared_key_length(0), m_encapsulated_key_length(0) {
274 m_kem_encryptors.reserve(key.
public_keys().size());
275 for(
const auto& k : key.public_keys()) {
276 const auto& newenc = m_kem_encryptors.emplace_back(*k,
"Raw", provider);
277 m_raw_kem_shared_key_length += newenc.shared_key_length(0 );
278 m_encapsulated_key_length += newenc.encapsulated_key_length();
282 size_t raw_kem_shared_key_length()
const override {
return m_raw_kem_shared_key_length; }
284 size_t encapsulated_key_length()
const override {
return m_encapsulated_key_length; }
286 void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
287 std::span<uint8_t> raw_shared_key,
292 BufferStuffer encaps_key_stuffer(out_encapsulated_key);
293 BufferStuffer shared_key_stuffer(raw_shared_key);
295 for(
auto& kem_enc : m_kem_encryptors) {
296 kem_enc.encrypt(encaps_key_stuffer.next(kem_enc.encapsulated_key_length()),
297 shared_key_stuffer.next(kem_enc.shared_key_length(0 )),
303 std::vector<PK_KEM_Encryptor> m_kem_encryptors;
304 size_t m_raw_kem_shared_key_length;
305 size_t m_encapsulated_key_length;
310std::unique_ptr<Botan::PK_Ops::KEM_Encryption> Hybrid_KEM_PublicKey::create_kem_encryption_op(
311 std::string_view kdf, std::string_view provider)
const {
312 return std::make_unique<Hybrid_KEM_Encryption_Operation>(*
this, kdf, provider);
317auto extract_public_keys(
const std::vector<std::unique_ptr<Private_Key>>& private_keys) {
318 std::vector<std::unique_ptr<Public_Key>> public_keys;
319 public_keys.reserve(private_keys.size());
320 for(
const auto& private_key : private_keys) {
321 BOTAN_ARG_CHECK(private_key !=
nullptr,
"List of private keys contains a nullptr");
322 public_keys.push_back(private_key->public_key());
329std::unique_ptr<Hybrid_KEM_PrivateKey> Hybrid_KEM_PrivateKey::generate_from_group(
Group_Params group,
331 const auto algo_spec = algorithm_specs_for_group(group);
332 std::vector<std::unique_ptr<Private_Key>> private_keys;
333 private_keys.reserve(algo_spec.size());
334 for(
const auto& spec : algo_spec) {
337 return std::make_unique<Hybrid_KEM_PrivateKey>(std::move(private_keys));
340Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::vector<std::unique_ptr<Private_Key>> sks) :
342 BOTAN_ARG_CHECK(sks.size() >= 2,
"List of private keys must include at least two keys");
346 return sk->supports_operation(PublicKeyOperation::KeyEncapsulation) ||
347 sk->supports_operation(PublicKeyOperation::KeyAgreement);
349 "Some provided private key is not compatible with this hybrid wrapper");
352 sks.begin(), sks.end(), std::back_inserter(m_private_keys), [](
auto& key) -> std::unique_ptr<Private_Key> {
353 if(key->supports_operation(PublicKeyOperation::KeyAgreement) &&
354 !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) {
355 auto ka_key = dynamic_cast<PK_Key_Agreement_Key*>(key.get());
356 BOTAN_ASSERT_NONNULL(ka_key);
358 return std::make_unique<KEX_to_KEM_Adapter_PrivateKey>(std::unique_ptr<PK_Key_Agreement_Key>(ka_key));
360 return std::move(key);
370 return std::make_unique<Hybrid_KEM_PublicKey>(extract_public_keys(m_private_keys));
383 const std::string_view kdf,
384 const std::string_view provider) :
385 PK_Ops::KEM_Decryption_with_KDF(kdf), m_encapsulated_key_length(0), m_raw_kem_shared_key_length(0) {
387 for(
const auto& private_key : key.private_keys()) {
388 const auto& newdec = m_decryptors.emplace_back(*private_key, rng,
"Raw", provider);
389 m_encapsulated_key_length += newdec.encapsulated_key_length();
390 m_raw_kem_shared_key_length += newdec.shared_key_length(0 );
394 void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encap_key)
override {
398 BufferSlicer encap_key_slicer(encap_key);
399 BufferStuffer shared_secret_stuffer(out_shared_key);
401 for(
auto& decryptor : m_decryptors) {
402 decryptor.decrypt(shared_secret_stuffer.next(decryptor.shared_key_length(0 )),
403 encap_key_slicer.take(decryptor.encapsulated_key_length()));
407 size_t encapsulated_key_length()
const override {
return m_encapsulated_key_length; }
409 size_t raw_kem_shared_key_length()
const override {
return m_raw_kem_shared_key_length; }
412 std::vector<PK_KEM_Decryptor> m_decryptors;
413 size_t m_encapsulated_key_length;
414 size_t m_raw_kem_shared_key_length;
421 return std::make_unique<Hybrid_KEM_Decryption>(*
this, rng, kdf, provider);
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_ARG_CHECK(expr, msg)
std::span< const uint8_t > take(const size_t count)
bool check_key(RandomNumberGenerator &rng, bool strong) const override
std::unique_ptr< Public_Key > public_key() const override
std::unique_ptr< PK_Ops::KEM_Decryption > create_kem_decryption_op(RandomNumberGenerator &rng, std::string_view kdf, std::string_view provider="base") const override
const auto & private_keys() const
secure_vector< uint8_t > private_key_bits() const override
std::vector< std::unique_ptr< Public_Key > > m_public_keys
const auto & public_keys() const
Hybrid_KEM_PublicKey(std::vector< std::unique_ptr< Public_Key > > pks)
bool check_key(RandomNumberGenerator &rng, bool strong) const override
static std::unique_ptr< Hybrid_KEM_PublicKey > load_for_group(Group_Params group, std::span< const uint8_t > concatenated_public_values)
int(* final)(unsigned char *, CTX *)
std::unique_ptr< Private_Key > create_private_key(std::string_view alg_name, RandomNumberGenerator &rng, std::string_view params, std::string_view provider)
RetT reduce(const std::vector< KeyT > &keys, RetT acc, ReducerT reducer)
std::vector< T, secure_allocator< T > > secure_vector
std::unique_ptr< Public_Key > load_public_key(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
decltype(auto) concat(Ts &&... buffers)