Coverage Report

Created: 2025-04-11 06:34

/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