Botan 3.13.0
Crypto and TLS for C&
kex_to_kem_adapter.cpp
Go to the documentation of this file.
1/**
2 * Adapter that allows using a KEX key as a KEM, using an ephemeral
3 * key in the KEM encapsulation.
4 *
5 * (C) 2023 Jack Lloyd
6 * 2023,2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity
7 *
8 * Botan is released under the Simplified BSD License (see license.txt)
9 */
10
11#include <botan/internal/kex_to_kem_adapter.h>
12
13#include <botan/assert.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/pk_ops_impl.h>
16
17#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
18 #include <botan/dh.h>
19 #include <botan/dl_group.h>
20#endif
21
22#if defined(BOTAN_HAS_ECDH)
23 #include <botan/ec_group.h>
24 #include <botan/ecdh.h>
25#endif
26
27#if defined(BOTAN_HAS_X25519)
28 #include <botan/x25519.h>
29#endif
30
31#if defined(BOTAN_HAS_X448)
32 #include <botan/x448.h>
33#endif
34
35namespace Botan {
36
37namespace {
38
39/**
40 * This helper determines the length of the agreed-upon value depending
41 * on the key agreement public key's algorithm type. It would be better
42 * to get this value via PK_Key_Agreement::agreed_value_size(), but
43 * instantiating a PK_Key_Agreement object requires a PrivateKey object
44 * which we don't have (yet) in the context this is used.
45 *
46 * TODO: Find a way to get this information without duplicating those
47 * implementation details of the key agreement algorithms.
48 */
49size_t kex_shared_key_length(const Public_Key& kex_public_key) {
50 BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement));
51
52#if defined(BOTAN_HAS_ECDH)
53 if(const auto* ecdh = dynamic_cast<const ECDH_PublicKey*>(&kex_public_key)) {
54 return ecdh->domain().get_p_bytes();
55 }
56#endif
57
58#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
59 if(const auto* dh = dynamic_cast<const DH_PublicKey*>(&kex_public_key)) {
60 return dh->group().p_bytes();
61 }
62#endif
63
64#if defined(BOTAN_HAS_X25519)
65 if(const auto* curve = dynamic_cast<const X25519_PublicKey*>(&kex_public_key)) {
66 BOTAN_UNUSED(curve);
67 return 32; /* TODO: magic number */
68 }
69#endif
70
71#if defined(BOTAN_HAS_X448)
72 if(const auto* curve = dynamic_cast<const X448_PublicKey*>(&kex_public_key)) {
73 BOTAN_UNUSED(curve);
74 return 56; /* TODO: magic number */
75 }
76#endif
77
78 throw Not_Implemented(
79 fmt("Cannot get shared kex key length from unknown key agreement public key of type '{}' in the hybrid KEM key",
80 kex_public_key.algo_name()));
81}
82
83/**
84 * This helper generates an ephemeral key agreement private key given a
85 * public key instance of a certain key agreement algorithm.
86 */
87std::unique_ptr<PK_Key_Agreement_Key> generate_key_agreement_private_key(const Public_Key& kex_public_key,
89 BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement));
90
91 auto new_kex_key = [&] {
92 auto new_private_key = kex_public_key.generate_another(rng);
93 auto* const kex_key = dynamic_cast<PK_Key_Agreement_Key*>(new_private_key.get());
94 if(kex_key != nullptr) [[likely]] {
95 // Intentionally leak new_private_key since we hold an alias of it in kex_key,
96 // which is captured in a unique_ptr below
97 // NOLINTNEXTLINE(*-unused-return-value)
98 (void)new_private_key.release();
99 }
100 return std::unique_ptr<PK_Key_Agreement_Key>(kex_key);
101 }();
102
103 BOTAN_ASSERT(new_kex_key, "Keys wrapped in this adapter are always key-agreement keys");
104 return new_kex_key;
105}
106
107std::unique_ptr<Public_Key> maybe_get_public_key(const std::unique_ptr<Private_Key>& private_key) {
108 BOTAN_ARG_CHECK(private_key != nullptr, "Private key is a nullptr");
109 return private_key->public_key();
110}
111
112class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF {
113 public:
114 KEX_to_KEM_Adapter_Encryption_Operation(std::shared_ptr<const Public_Key> key,
115 std::string_view kdf,
116 std::string_view provider) :
117 PK_Ops::KEM_Encryption_with_KDF(kdf), m_provider(provider), m_public_key(std::move(key)) {}
118
119 size_t raw_kem_shared_key_length() const override { return kex_shared_key_length(*m_public_key); }
120
121 size_t encapsulated_key_length() const override {
122 // Serializing the public value into a short-lived heap-allocated
123 // vector is not ideal.
124 //
125 // TODO: Find a way to get the public value length without copying
126 // the public value into a vector. See GH #3706 (point 5).
127 return m_public_key->raw_public_key_bits().size();
128 }
129
130 void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
131 std::span<uint8_t> raw_shared_key,
132 Botan::RandomNumberGenerator& rng) override {
133 const auto sk = generate_key_agreement_private_key(*m_public_key, rng);
134 const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider)
135 .derive_key(0 /* no KDF */, m_public_key->raw_public_key_bits())
136 .bits_of();
137
138 const auto public_value = sk->public_value();
139
140 // TODO: perhaps avoid these copies by providing std::span out-params
141 // for `PK_Key_Agreement::derive_key()` and
142 // `PK_Key_Agreement_Key::public_value()`
143 BOTAN_ASSERT_EQUAL(public_value.size(),
144 out_encapsulated_key.size(),
145 "KEX-to-KEM Adapter: encapsulated key out-param has correct length");
147 shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length");
148 std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin());
149 std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin());
150 }
151
152 private:
153 std::string m_provider;
154 std::shared_ptr<const Public_Key> m_public_key;
155};
156
157class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF {
158 public:
159 KEX_to_KEM_Decryption_Operation(const PK_Key_Agreement_Key& key,
160 RandomNumberGenerator& rng,
161 const std::string_view kdf,
162 const std::string_view provider) :
163 PK_Ops::KEM_Decryption_with_KDF(kdf),
164 m_operation(key, rng, "Raw", provider),
165 m_encapsulated_key_length(key.public_value().size()) {}
166
167 void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encap_key) override {
168 secure_vector<uint8_t> shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of();
170 shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length");
171 std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin());
172 }
173
174 size_t encapsulated_key_length() const override { return m_encapsulated_key_length; }
175
176 size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); }
177
178 private:
179 PK_Key_Agreement m_operation;
180 size_t m_encapsulated_key_length;
181};
182
183} // namespace
184
185KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::unique_ptr<Public_Key> public_key) :
186 m_public_key(std::move(public_key)) {
187 BOTAN_ARG_CHECK(m_public_key != nullptr, "Public key is a nullptr");
188 BOTAN_ARG_CHECK(m_public_key->supports_operation(PublicKeyOperation::KeyAgreement), "Public key is no KEX key");
189}
190
192 return fmt("KEX-to-KEM({})", m_public_key->algo_name());
193}
194
196 return m_public_key->estimated_strength();
197}
198
200 return m_public_key->key_length();
201}
202
204 return m_public_key->check_key(rng, strong);
205}
206
208 return m_public_key->algorithm_identifier();
209}
210
212 return m_public_key->raw_public_key_bits();
213}
214
216 return m_public_key->public_key_bits();
217}
218
220 return std::make_unique<KEX_to_KEM_Adapter_PrivateKey>(generate_key_agreement_private_key(*m_public_key, rng));
221}
222
226
227namespace {
228
229std::unique_ptr<PK_Key_Agreement_Key> capture_as_ka_key(std::unique_ptr<Private_Key> private_key) {
230 auto* raw_ptr = private_key.release();
231 if(auto* sk = dynamic_cast<PK_Key_Agreement_Key*>(raw_ptr)) {
232 return std::unique_ptr<PK_Key_Agreement_Key>(sk);
233 } else {
234 delete raw_ptr; // NOLINT(*-owning-memory)
236 "Private key must implement PK_Key_Agreement_Key", "KEX_to_KEM_Adapter_PrivateKey", __FILE__);
237 }
238}
239
240} // namespace
241
242KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr<Private_Key> private_key) :
243 KEX_to_KEM_Adapter_PublicKey(maybe_get_public_key(private_key)),
244 m_private_key(capture_as_ka_key(std::move(private_key))) {}
245
247 return m_private_key->private_key_bits();
248}
249
251 return m_private_key->raw_private_key_bits();
252}
253
254std::unique_ptr<Public_Key> KEX_to_KEM_Adapter_PrivateKey::public_key() const {
255 return std::make_unique<KEX_to_KEM_Adapter_PublicKey>(m_private_key->public_key());
256}
257
259 return m_private_key->check_key(rng, strong);
260}
261
262std::unique_ptr<PK_Ops::KEM_Encryption> KEX_to_KEM_Adapter_PublicKey::create_kem_encryption_op(
263 std::string_view kdf, std::string_view provider) const {
264 return std::make_unique<KEX_to_KEM_Adapter_Encryption_Operation>(m_public_key, kdf, provider);
265}
266
267std::unique_ptr<PK_Ops::KEM_Decryption> KEX_to_KEM_Adapter_PrivateKey::create_kem_decryption_op(
268 RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const {
269 return std::make_unique<KEX_to_KEM_Decryption_Operation>(*m_private_key, rng, kdf, provider);
270}
271
272} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:144
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:88
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
secure_vector< uint8_t > private_key_bits() const override
std::unique_ptr< Public_Key > public_key() const override
secure_vector< uint8_t > raw_private_key_bits() const override
KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr< Private_Key > private_key)
bool check_key(RandomNumberGenerator &rng, bool strong) 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
KEX_to_KEM_Adapter_PublicKey(std::unique_ptr< Public_Key > public_key)
std::string algo_name() const override
std::unique_ptr< PK_Ops::KEM_Encryption > create_kem_encryption_op(std::string_view kdf, std::string_view provider="base") const override
bool check_key(RandomNumberGenerator &rng, bool strong) const override
std::vector< uint8_t > raw_public_key_bits() const override
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
AlgorithmIdentifier algorithm_identifier() const override
bool supports_operation(PublicKeyOperation op) const override
std::vector< uint8_t > public_key_bits() const override
A public key for the X448 key agreement scheme according to RFC 7748.
Definition x448.h:24
void throw_invalid_argument(const char *message, const char *func, const char *file)
Definition assert.cpp:23
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
PublicKeyOperation
Definition pk_keys.h:46
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128