Botan 3.4.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 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/internal/fmt.h>
14#include <botan/internal/pk_ops_impl.h>
15#include <botan/internal/stl_util.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/ecdh.h>
24#endif
25
26#if defined(BOTAN_HAS_CURVE_25519)
27 #include <botan/curve25519.h>
28#endif
29
30#if defined(BOTAN_HAS_X448)
31 #include <botan/x448.h>
32#endif
33
34namespace Botan::TLS {
35
36namespace {
37
38/**
39 * This helper converts a key agreement public key into its raw public
40 * value. In contrast to the private key (cf. PK_Key_AgreementKey) there
41 * is no generic interface class to do this.
42 *
43 * TODO: Have a decent generic API to get the raw public value from any
44 * Key Agreement public key.
45 */
46std::vector<uint8_t> kex_public_value(const Public_Key& kex_public_key) {
47 BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement));
48
49#if defined(BOTAN_HAS_ECDH)
50 if(const auto* ecdh = dynamic_cast<const ECDH_PublicKey*>(&kex_public_key)) {
51 return ecdh->public_value();
52 }
53#endif
54
55#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
56 if(const auto* dh = dynamic_cast<const DH_PublicKey*>(&kex_public_key)) {
57 return dh->public_value();
58 }
59#endif
60
61#if defined(BOTAN_HAS_CURVE_25519)
62 if(const auto* curve = dynamic_cast<const Curve25519_PublicKey*>(&kex_public_key)) {
63 return curve->public_value();
64 }
65#endif
66
67#if defined(BOTAN_HAS_X448)
68 if(const auto* curve = dynamic_cast<const X448_PublicKey*>(&kex_public_key)) {
69 return curve->public_value();
70 }
71#endif
72
73 throw Not_Implemented(
74 fmt("Cannot get public value from unknown key agreement public key of type '{}' in the hybrid KEM key",
75 kex_public_key.algo_name()));
76}
77
78/**
79 * This helper determines the length of the agreed-upon value depending
80 * on the key agreement public key's algorithm type. It would be better
81 * to get this value via PK_Key_Agreement::agreed_value_size(), but
82 * instantiating a PK_Key_Agreement object requires a PrivateKey object
83 * which we don't have (yet) in the context this is used.
84 *
85 * TODO: Find a way to get this information without duplicating those
86 * implementation details of the key agreement algorithms.
87 */
88size_t kex_shared_key_length(const Public_Key& kex_public_key) {
89 BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement));
90
91#if defined(BOTAN_HAS_ECDH)
92 if(const auto* ecdh = dynamic_cast<const ECDH_PublicKey*>(&kex_public_key)) {
93 return ecdh->domain().get_p_bytes();
94 }
95#endif
96
97#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
98 if(const auto* dh = dynamic_cast<const DH_PublicKey*>(&kex_public_key)) {
99 return dh->group().p_bytes();
100 }
101#endif
102
103#if defined(BOTAN_HAS_CURVE_25519)
104 if(const auto* curve = dynamic_cast<const Curve25519_PublicKey*>(&kex_public_key)) {
105 BOTAN_UNUSED(curve);
106 return 32; /* TODO: magic number */
107 }
108#endif
109
110#if defined(BOTAN_HAS_X448)
111 if(const auto* curve = dynamic_cast<const X448_PublicKey*>(&kex_public_key)) {
112 BOTAN_UNUSED(curve);
113 return 56; /* TODO: magic number */
114 }
115#endif
116
117 throw Not_Implemented(
118 fmt("Cannot get shared kex key length from unknown key agreement public key of type '{}' in the hybrid KEM key",
119 kex_public_key.algo_name()));
120}
121
122/**
123 * This helper generates an ephemeral key agreement private key given a
124 * public key instance of a certain key agreement algorithm.
125 */
126std::unique_ptr<PK_Key_Agreement_Key> generate_key_agreement_private_key(const Public_Key& kex_public_key,
127 RandomNumberGenerator& rng) {
128 BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement));
129
130 auto new_kex_key = [&] {
131 auto new_private_key = kex_public_key.generate_another(rng);
132 const auto kex_key = dynamic_cast<PK_Key_Agreement_Key*>(new_private_key.get());
133 if(kex_key) [[likely]] {
134 (void)new_private_key.release();
135 }
136 return std::unique_ptr<PK_Key_Agreement_Key>(kex_key);
137 }();
138
139 BOTAN_ASSERT(new_kex_key, "Keys wrapped in this adapter are always key-agreement keys");
140 return new_kex_key;
141}
142
143std::unique_ptr<Public_Key> maybe_get_public_key(const std::unique_ptr<PK_Key_Agreement_Key>& private_key) {
144 BOTAN_ARG_CHECK(private_key != nullptr, "Private key is a nullptr");
145 return private_key->public_key();
146}
147
148class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF {
149 public:
150 KEX_to_KEM_Adapter_Encryption_Operation(const Public_Key& key, std::string_view kdf, std::string_view provider) :
151 PK_Ops::KEM_Encryption_with_KDF(kdf), m_provider(provider), m_public_key(key) {}
152
153 size_t raw_kem_shared_key_length() const override { return kex_shared_key_length(m_public_key); }
154
155 size_t encapsulated_key_length() const override { return kex_public_value(m_public_key).size(); }
156
157 void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
158 std::span<uint8_t> raw_shared_key,
159 Botan::RandomNumberGenerator& rng) override {
160 const auto sk = generate_key_agreement_private_key(m_public_key, rng);
161 const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider)
162 .derive_key(0 /* no KDF */, kex_public_value(m_public_key))
163 .bits_of();
164
165 const auto public_value = sk->public_value();
166
167 // TODO: perhaps avoid these copies by providing std::span out-params
168 // for `PK_Key_Agreement::derive_key()` and
169 // `PK_Key_Agreement_Key::public_value()`
170 BOTAN_ASSERT_EQUAL(public_value.size(),
171 out_encapsulated_key.size(),
172 "KEX-to-KEM Adapter: encapsulated key out-param has correct length");
174 shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length");
175 std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin());
176 std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin());
177 }
178
179 private:
180 std::string m_provider;
181 const Public_Key& m_public_key;
182};
183
184class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF {
185 public:
186 KEX_to_KEM_Decryption_Operation(const PK_Key_Agreement_Key& key,
187 RandomNumberGenerator& rng,
188 const std::string_view kdf,
189 const std::string_view provider) :
190 PK_Ops::KEM_Decryption_with_KDF(kdf),
191 m_operation(key, rng, "Raw", provider),
192 m_encapsulated_key_length(key.public_value().size()) {}
193
194 void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encap_key) override {
195 secure_vector<uint8_t> shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of();
197 shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length");
198 std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin());
199 }
200
201 size_t encapsulated_key_length() const override { return m_encapsulated_key_length; }
202
203 size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); }
204
205 private:
206 PK_Key_Agreement m_operation;
207 size_t m_encapsulated_key_length;
208};
209
210} // namespace
211
212KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::unique_ptr<Public_Key> public_key) :
213 m_public_key(std::move(public_key)) {
214 BOTAN_ARG_CHECK(m_public_key != nullptr, "Public key is a nullptr");
215 BOTAN_ARG_CHECK(m_public_key->supports_operation(PublicKeyOperation::KeyAgreement), "Public key is no KEX key");
216}
217
219 return fmt("KEX-to-KEM({})", m_public_key->algo_name());
220}
221
223 return m_public_key->estimated_strength();
224}
225
227 return m_public_key->key_length();
228}
229
231 return m_public_key->check_key(rng, strong);
232}
233
235 return m_public_key->algorithm_identifier();
236}
237
239 // Technically, this is not fully correct. This method is supposed to return
240 // a BER-encoded public key. Though, for other (modern) KEMs -- like Kyber --
241 // it returns the raw encoding of the public key.
242 //
243 // TODO: Provide something like Public_Key::raw_public_key_bits() to
244 // reflect that difference. Also: Bare key agreement keys could return
245 // their raw public value there.
246 return kex_public_value(*m_public_key);
247}
248
250 return std::make_unique<KEX_to_KEM_Adapter_PrivateKey>(generate_key_agreement_private_key(*m_public_key, rng));
251}
252
256
257KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr<PK_Key_Agreement_Key> private_key) :
258 KEX_to_KEM_Adapter_PublicKey(maybe_get_public_key(private_key)), m_private_key(std::move(private_key)) {
259 BOTAN_ARG_CHECK(m_private_key->supports_operation(PublicKeyOperation::KeyAgreement), "Private key is no KEX key");
260}
261
263 return m_private_key->private_key_bits();
264}
265
266std::unique_ptr<Public_Key> KEX_to_KEM_Adapter_PrivateKey::public_key() const {
267 return std::make_unique<KEX_to_KEM_Adapter_PublicKey>(m_private_key->public_key());
268}
269
271 return m_private_key->check_key(rng, strong);
272}
273
274std::unique_ptr<PK_Ops::KEM_Encryption> KEX_to_KEM_Adapter_PublicKey::create_kem_encryption_op(
275 std::string_view kdf, std::string_view provider) const {
276 return std::make_unique<KEX_to_KEM_Adapter_Encryption_Operation>(*m_public_key, kdf, provider);
277}
278
279std::unique_ptr<PK_Ops::KEM_Decryption> KEX_to_KEM_Adapter_PrivateKey::create_kem_decryption_op(
280 RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const {
281 return std::make_unique<KEX_to_KEM_Decryption_Operation>(*m_private_key, rng, kdf, provider);
282}
283
284} // namespace Botan::TLS
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:68
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr< PK_Key_Agreement_Key > private_key)
std::unique_ptr< PK_Ops::KEM_Decryption > create_kem_decryption_op(RandomNumberGenerator &rng, std::string_view kdf, std::string_view provider="base") const override
bool check_key(RandomNumberGenerator &rng, bool strong) const override
secure_vector< uint8_t > private_key_bits() const override
std::unique_ptr< Public_Key > public_key() const override
bool supports_operation(PublicKeyOperation op) 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 > public_key_bits() const override
AlgorithmIdentifier algorithm_identifier() const override
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
KEX_to_KEM_Adapter_PublicKey(std::unique_ptr< Public_Key > public_key)
int(* final)(unsigned char *, CTX *)
PublicKeyOperation
Definition pk_keys.h:45
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61