/src/botan/src/lib/pubkey/kex_to_kem_adapter/kex_to_kem_adapter.cpp
Line | Count | Source (jump to first uncovered line) |
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/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_X25519) |
27 | | #include <botan/x25519.h> |
28 | | #endif |
29 | | |
30 | | #if defined(BOTAN_HAS_X448) |
31 | | #include <botan/x448.h> |
32 | | #endif |
33 | | |
34 | | namespace Botan { |
35 | | |
36 | | namespace { |
37 | | |
38 | | /** |
39 | | * This helper determines the length of the agreed-upon value depending |
40 | | * on the key agreement public key's algorithm type. It would be better |
41 | | * to get this value via PK_Key_Agreement::agreed_value_size(), but |
42 | | * instantiating a PK_Key_Agreement object requires a PrivateKey object |
43 | | * which we don't have (yet) in the context this is used. |
44 | | * |
45 | | * TODO: Find a way to get this information without duplicating those |
46 | | * implementation details of the key agreement algorithms. |
47 | | */ |
48 | 0 | size_t kex_shared_key_length(const Public_Key& kex_public_key) { |
49 | 0 | BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); |
50 | |
|
51 | 0 | #if defined(BOTAN_HAS_ECDH) |
52 | 0 | if(const auto* ecdh = dynamic_cast<const ECDH_PublicKey*>(&kex_public_key)) { |
53 | 0 | return ecdh->domain().get_p_bytes(); |
54 | 0 | } |
55 | 0 | #endif |
56 | | |
57 | 0 | #if defined(BOTAN_HAS_DIFFIE_HELLMAN) |
58 | 0 | if(const auto* dh = dynamic_cast<const DH_PublicKey*>(&kex_public_key)) { |
59 | 0 | return dh->group().p_bytes(); |
60 | 0 | } |
61 | 0 | #endif |
62 | | |
63 | 0 | #if defined(BOTAN_HAS_X25519) |
64 | 0 | if(const auto* curve = dynamic_cast<const X25519_PublicKey*>(&kex_public_key)) { |
65 | 0 | BOTAN_UNUSED(curve); |
66 | 0 | return 32; /* TODO: magic number */ |
67 | 0 | } |
68 | 0 | #endif |
69 | | |
70 | 0 | #if defined(BOTAN_HAS_X448) |
71 | 0 | if(const auto* curve = dynamic_cast<const X448_PublicKey*>(&kex_public_key)) { |
72 | 0 | BOTAN_UNUSED(curve); |
73 | 0 | return 56; /* TODO: magic number */ |
74 | 0 | } |
75 | 0 | #endif |
76 | | |
77 | 0 | throw Not_Implemented( |
78 | 0 | fmt("Cannot get shared kex key length from unknown key agreement public key of type '{}' in the hybrid KEM key", |
79 | 0 | kex_public_key.algo_name())); |
80 | 0 | } |
81 | | |
82 | | /** |
83 | | * This helper generates an ephemeral key agreement private key given a |
84 | | * public key instance of a certain key agreement algorithm. |
85 | | */ |
86 | | std::unique_ptr<PK_Key_Agreement_Key> generate_key_agreement_private_key(const Public_Key& kex_public_key, |
87 | 0 | RandomNumberGenerator& rng) { |
88 | 0 | BOTAN_ASSERT_NOMSG(kex_public_key.supports_operation(PublicKeyOperation::KeyAgreement)); |
89 | |
|
90 | 0 | auto new_kex_key = [&] { |
91 | 0 | auto new_private_key = kex_public_key.generate_another(rng); |
92 | 0 | const auto kex_key = dynamic_cast<PK_Key_Agreement_Key*>(new_private_key.get()); |
93 | 0 | if(kex_key) [[likely]] { |
94 | | // Intentionally leak new_private_key since we hold an alias of it in kex_key, |
95 | | // which is captured in a unique_ptr below |
96 | | // NOLINTNEXTLINE(*-unused-return-value) |
97 | 0 | (void)new_private_key.release(); |
98 | 0 | } |
99 | 0 | return std::unique_ptr<PK_Key_Agreement_Key>(kex_key); |
100 | 0 | }(); |
101 | |
|
102 | 0 | BOTAN_ASSERT(new_kex_key, "Keys wrapped in this adapter are always key-agreement keys"); |
103 | 0 | return new_kex_key; |
104 | 0 | } |
105 | | |
106 | 0 | std::unique_ptr<Public_Key> maybe_get_public_key(const std::unique_ptr<Private_Key>& private_key) { |
107 | 0 | BOTAN_ARG_CHECK(private_key != nullptr, "Private key is a nullptr"); |
108 | 0 | return private_key->public_key(); |
109 | 0 | } |
110 | | |
111 | | class KEX_to_KEM_Adapter_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF { |
112 | | public: |
113 | | KEX_to_KEM_Adapter_Encryption_Operation(const Public_Key& key, std::string_view kdf, std::string_view provider) : |
114 | 0 | PK_Ops::KEM_Encryption_with_KDF(kdf), m_provider(provider), m_public_key(key) {} |
115 | | |
116 | 0 | size_t raw_kem_shared_key_length() const override { return kex_shared_key_length(m_public_key); } |
117 | | |
118 | 0 | size_t encapsulated_key_length() const override { |
119 | | // Serializing the public value into a short-lived heap-allocated |
120 | | // vector is not ideal. |
121 | | // |
122 | | // TODO: Find a way to get the public value length without copying |
123 | | // the public value into a vector. See GH #3706 (point 5). |
124 | 0 | return m_public_key.raw_public_key_bits().size(); |
125 | 0 | } |
126 | | |
127 | | void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key, |
128 | | std::span<uint8_t> raw_shared_key, |
129 | 0 | Botan::RandomNumberGenerator& rng) override { |
130 | 0 | const auto sk = generate_key_agreement_private_key(m_public_key, rng); |
131 | 0 | const auto shared_key = PK_Key_Agreement(*sk, rng, "Raw", m_provider) |
132 | 0 | .derive_key(0 /* no KDF */, m_public_key.raw_public_key_bits()) |
133 | 0 | .bits_of(); |
134 | |
|
135 | 0 | const auto public_value = sk->public_value(); |
136 | | |
137 | | // TODO: perhaps avoid these copies by providing std::span out-params |
138 | | // for `PK_Key_Agreement::derive_key()` and |
139 | | // `PK_Key_Agreement_Key::public_value()` |
140 | 0 | BOTAN_ASSERT_EQUAL(public_value.size(), |
141 | 0 | out_encapsulated_key.size(), |
142 | 0 | "KEX-to-KEM Adapter: encapsulated key out-param has correct length"); |
143 | 0 | BOTAN_ASSERT_EQUAL( |
144 | 0 | shared_key.size(), raw_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); |
145 | 0 | std::copy(public_value.begin(), public_value.end(), out_encapsulated_key.begin()); |
146 | 0 | std::copy(shared_key.begin(), shared_key.end(), raw_shared_key.begin()); |
147 | 0 | } |
148 | | |
149 | | private: |
150 | | std::string m_provider; |
151 | | const Public_Key& m_public_key; |
152 | | }; |
153 | | |
154 | | class KEX_to_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF { |
155 | | public: |
156 | | KEX_to_KEM_Decryption_Operation(const PK_Key_Agreement_Key& key, |
157 | | RandomNumberGenerator& rng, |
158 | | const std::string_view kdf, |
159 | | const std::string_view provider) : |
160 | 0 | PK_Ops::KEM_Decryption_with_KDF(kdf), |
161 | 0 | m_operation(key, rng, "Raw", provider), |
162 | 0 | m_encapsulated_key_length(key.public_value().size()) {} |
163 | | |
164 | 0 | void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encap_key) override { |
165 | 0 | secure_vector<uint8_t> shared_secret = m_operation.derive_key(0 /* no KDF */, encap_key).bits_of(); |
166 | 0 | BOTAN_ASSERT_EQUAL( |
167 | 0 | shared_secret.size(), out_shared_key.size(), "KEX-to-KEM Adapter: shared key out-param has correct length"); |
168 | 0 | std::copy(shared_secret.begin(), shared_secret.end(), out_shared_key.begin()); |
169 | 0 | } |
170 | | |
171 | 0 | size_t encapsulated_key_length() const override { return m_encapsulated_key_length; } |
172 | | |
173 | 0 | size_t raw_kem_shared_key_length() const override { return m_operation.agreed_value_size(); } |
174 | | |
175 | | private: |
176 | | PK_Key_Agreement m_operation; |
177 | | size_t m_encapsulated_key_length; |
178 | | }; |
179 | | |
180 | | } // namespace |
181 | | |
182 | | KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::unique_ptr<Public_Key> public_key) : |
183 | 0 | m_public_key(std::move(public_key)) { |
184 | 0 | BOTAN_ARG_CHECK(m_public_key != nullptr, "Public key is a nullptr"); |
185 | 0 | BOTAN_ARG_CHECK(m_public_key->supports_operation(PublicKeyOperation::KeyAgreement), "Public key is no KEX key"); |
186 | 0 | } Unexecuted instantiation: Botan::KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> >) Unexecuted instantiation: Botan::KEX_to_KEM_Adapter_PublicKey::KEX_to_KEM_Adapter_PublicKey(std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> >) |
187 | | |
188 | 0 | std::string KEX_to_KEM_Adapter_PublicKey::algo_name() const { |
189 | 0 | return fmt("KEX-to-KEM({})", m_public_key->algo_name()); |
190 | 0 | } |
191 | | |
192 | 0 | size_t KEX_to_KEM_Adapter_PublicKey::estimated_strength() const { |
193 | 0 | return m_public_key->estimated_strength(); |
194 | 0 | } |
195 | | |
196 | 0 | size_t KEX_to_KEM_Adapter_PublicKey::key_length() const { |
197 | 0 | return m_public_key->key_length(); |
198 | 0 | } |
199 | | |
200 | 0 | bool KEX_to_KEM_Adapter_PublicKey::check_key(RandomNumberGenerator& rng, bool strong) const { |
201 | 0 | return m_public_key->check_key(rng, strong); |
202 | 0 | } |
203 | | |
204 | 0 | AlgorithmIdentifier KEX_to_KEM_Adapter_PublicKey::algorithm_identifier() const { |
205 | 0 | return m_public_key->algorithm_identifier(); |
206 | 0 | } |
207 | | |
208 | 0 | std::vector<uint8_t> KEX_to_KEM_Adapter_PublicKey::raw_public_key_bits() const { |
209 | 0 | return m_public_key->raw_public_key_bits(); |
210 | 0 | } |
211 | | |
212 | 0 | std::vector<uint8_t> KEX_to_KEM_Adapter_PublicKey::public_key_bits() const { |
213 | 0 | return m_public_key->public_key_bits(); |
214 | 0 | } |
215 | | |
216 | 0 | std::unique_ptr<Private_Key> KEX_to_KEM_Adapter_PublicKey::generate_another(RandomNumberGenerator& rng) const { |
217 | 0 | return std::make_unique<KEX_to_KEM_Adapter_PrivateKey>(generate_key_agreement_private_key(*m_public_key, rng)); |
218 | 0 | } |
219 | | |
220 | 0 | bool KEX_to_KEM_Adapter_PublicKey::supports_operation(PublicKeyOperation op) const { |
221 | 0 | return op == PublicKeyOperation::KeyEncapsulation; |
222 | 0 | } |
223 | | |
224 | | KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::unique_ptr<Private_Key> private_key) : |
225 | 0 | KEX_to_KEM_Adapter_PublicKey(maybe_get_public_key(private_key)), m_private_key([&]() { |
226 | 0 | auto sk = dynamic_cast<PK_Key_Agreement_Key*>(private_key.release()); |
227 | 0 | BOTAN_ARG_CHECK(sk != nullptr, "Private Key must implement the PK_Key_Agreement_Key interface"); |
228 | 0 | return std::unique_ptr<PK_Key_Agreement_Key>(sk); |
229 | 0 | }()) {} Unexecuted instantiation: Botan::KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> >) Unexecuted instantiation: Botan::KEX_to_KEM_Adapter_PrivateKey::KEX_to_KEM_Adapter_PrivateKey(std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> >) |
230 | | |
231 | 0 | secure_vector<uint8_t> KEX_to_KEM_Adapter_PrivateKey::private_key_bits() const { |
232 | 0 | return m_private_key->private_key_bits(); |
233 | 0 | } |
234 | | |
235 | 0 | secure_vector<uint8_t> KEX_to_KEM_Adapter_PrivateKey::raw_private_key_bits() const { |
236 | 0 | return m_private_key->raw_private_key_bits(); |
237 | 0 | } |
238 | | |
239 | 0 | std::unique_ptr<Public_Key> KEX_to_KEM_Adapter_PrivateKey::public_key() const { |
240 | 0 | return std::make_unique<KEX_to_KEM_Adapter_PublicKey>(m_private_key->public_key()); |
241 | 0 | } |
242 | | |
243 | 0 | bool KEX_to_KEM_Adapter_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { |
244 | 0 | return m_private_key->check_key(rng, strong); |
245 | 0 | } |
246 | | |
247 | | std::unique_ptr<PK_Ops::KEM_Encryption> KEX_to_KEM_Adapter_PublicKey::create_kem_encryption_op( |
248 | 0 | std::string_view kdf, std::string_view provider) const { |
249 | 0 | return std::make_unique<KEX_to_KEM_Adapter_Encryption_Operation>(*m_public_key, kdf, provider); |
250 | 0 | } |
251 | | |
252 | | std::unique_ptr<PK_Ops::KEM_Decryption> KEX_to_KEM_Adapter_PrivateKey::create_kem_decryption_op( |
253 | 0 | RandomNumberGenerator& rng, std::string_view kdf, std::string_view provider) const { |
254 | 0 | return std::make_unique<KEX_to_KEM_Decryption_Operation>(*m_private_key, rng, kdf, provider); |
255 | 0 | } |
256 | | |
257 | | } // namespace Botan |