/src/botan/src/lib/tls/tls13_pqc/hybrid_public_key.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * Composite key pair that exposes the Public/Private key API but combines |
3 | | * multiple key agreement schemes into a hybrid algorithm. |
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/hybrid_public_key.h> |
12 | | |
13 | | #include <botan/ec_group.h> |
14 | | #include <botan/pk_algs.h> |
15 | | |
16 | | #include <botan/internal/fmt.h> |
17 | | #include <botan/internal/hybrid_kem_ops.h> |
18 | | #include <botan/internal/kex_to_kem_adapter.h> |
19 | | #include <botan/internal/pk_ops_impl.h> |
20 | | #include <botan/internal/stl_util.h> |
21 | | |
22 | | namespace Botan::TLS { |
23 | | |
24 | | namespace { |
25 | | |
26 | 0 | std::vector<std::pair<std::string, std::string>> algorithm_specs_for_group(Group_Params group) { |
27 | 0 | BOTAN_ARG_CHECK(group.is_pqc_hybrid(), "Group is not hybrid"); |
28 | |
|
29 | 0 | switch(group.code()) { |
30 | | // draft-kwiatkowski-tls-ecdhe-mlkem-02 Section 3 |
31 | | // |
32 | | // NIST's special publication 800-56Cr2 approves the usage of HKDF with |
33 | | // two distinct shared secrets, with the condition that the first one |
34 | | // is computed by a FIPS-approved key-establishment scheme. FIPS also |
35 | | // requires a certified implementation of the scheme, which will remain |
36 | | // more ubiqutous for secp256r1 in the coming years. |
37 | | // |
38 | | // For this reason we put the ML-KEM-768 shared secret first in |
39 | | // X25519MLKEM768, and the secp256r1 shared secret first in |
40 | | // SecP256r1MLKEM768. |
41 | 0 | case Group_Params::HYBRID_X25519_ML_KEM_768: |
42 | 0 | return {{"ML-KEM", "ML-KEM-768"}, {"X25519", "X25519"}}; |
43 | 0 | case Group_Params::HYBRID_SECP256R1_ML_KEM_768: |
44 | 0 | return {{"ECDH", "secp256r1"}, {"ML-KEM", "ML-KEM-768"}}; |
45 | 0 | case Group_Params::HYBRID_SECP384R1_ML_KEM_1024: |
46 | 0 | return {{"ECDH", "secp384r1"}, {"ML-KEM", "ML-KEM-1024"}}; |
47 | | |
48 | 0 | case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: |
49 | 0 | return {{"X25519", "X25519"}, {"FrodoKEM", "eFrodoKEM-640-SHAKE"}}; |
50 | 0 | case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: |
51 | 0 | return {{"X25519", "X25519"}, {"FrodoKEM", "eFrodoKEM-640-AES"}}; |
52 | 0 | case Group_Params::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS: |
53 | 0 | return {{"X448", "X448"}, {"FrodoKEM", "eFrodoKEM-976-SHAKE"}}; |
54 | 0 | case Group_Params::HYBRID_X448_eFRODOKEM_976_AES_OQS: |
55 | 0 | return {{"X448", "X448"}, {"FrodoKEM", "eFrodoKEM-976-AES"}}; |
56 | | |
57 | 0 | case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: |
58 | 0 | return {{"ECDH", "secp256r1"}, {"FrodoKEM", "eFrodoKEM-640-SHAKE"}}; |
59 | 0 | case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: |
60 | 0 | return {{"ECDH", "secp256r1"}, {"FrodoKEM", "eFrodoKEM-640-AES"}}; |
61 | | |
62 | 0 | case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: |
63 | 0 | return {{"ECDH", "secp384r1"}, {"FrodoKEM", "eFrodoKEM-976-SHAKE"}}; |
64 | 0 | case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: |
65 | 0 | return {{"ECDH", "secp384r1"}, {"FrodoKEM", "eFrodoKEM-976-AES"}}; |
66 | | |
67 | 0 | case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: |
68 | 0 | return {{"ECDH", "secp521r1"}, {"FrodoKEM", "eFrodoKEM-1344-SHAKE"}}; |
69 | 0 | case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: |
70 | 0 | return {{"ECDH", "secp521r1"}, {"FrodoKEM", "eFrodoKEM-1344-AES"}}; |
71 | | |
72 | 0 | default: |
73 | 0 | return {}; |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | 0 | std::vector<AlgorithmIdentifier> algorithm_identifiers_for_group(Group_Params group) { |
78 | 0 | BOTAN_ASSERT_NOMSG(group.is_pqc_hybrid()); |
79 | |
|
80 | 0 | const auto specs = algorithm_specs_for_group(group); |
81 | 0 | std::vector<AlgorithmIdentifier> result; |
82 | 0 | result.reserve(specs.size()); |
83 | | |
84 | | // This maps the string-based algorithm specs hard-coded above to OID-based |
85 | | // AlgorithmIdentifier objects. The mapping is needed because |
86 | | // load_public_key() depends on those while create_private_key() requires the |
87 | | // strong-based spec. |
88 | | // |
89 | | // TODO: This is inconvenient, confusing and error-prone. Find a better way |
90 | | // to load arbitrary public keys. |
91 | 0 | for(const auto& spec : specs) { |
92 | 0 | if(spec.first == "ECDH") { |
93 | 0 | result.push_back(AlgorithmIdentifier("ECDH", EC_Group::from_name(spec.second).DER_encode())); |
94 | 0 | } else { |
95 | 0 | result.push_back(AlgorithmIdentifier(spec.second, AlgorithmIdentifier::USE_EMPTY_PARAM)); |
96 | 0 | } |
97 | 0 | } |
98 | |
|
99 | 0 | return result; |
100 | 0 | } |
101 | | |
102 | 0 | std::vector<size_t> public_key_lengths_for_group(Group_Params group) { |
103 | 0 | BOTAN_ASSERT_NOMSG(group.is_pqc_hybrid()); |
104 | | |
105 | | // This duplicates information of the algorithm internals. |
106 | | // |
107 | | // TODO: Find a way to expose important algorithm constants globally |
108 | | // in the library, to avoid violating the DRY principle. |
109 | 0 | switch(group.code()) { |
110 | 0 | case Group_Params::HYBRID_X25519_ML_KEM_768: |
111 | 0 | return {1184, 32}; |
112 | 0 | case Group_Params::HYBRID_SECP256R1_ML_KEM_768: |
113 | 0 | return {65, 1184}; |
114 | 0 | case Group_Params::HYBRID_SECP384R1_ML_KEM_1024: |
115 | 0 | return {97, 1568}; |
116 | | |
117 | 0 | case Group_Params::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: |
118 | 0 | return {32, 9616}; |
119 | 0 | case Group_Params::HYBRID_X25519_eFRODOKEM_640_AES_OQS: |
120 | 0 | return {32, 9616}; |
121 | 0 | case Group_Params::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS: |
122 | 0 | return {56, 15632}; |
123 | 0 | case Group_Params::HYBRID_X448_eFRODOKEM_976_AES_OQS: |
124 | 0 | return {56, 15632}; |
125 | | |
126 | 0 | case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: |
127 | 0 | return {65, 9616}; |
128 | 0 | case Group_Params::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: |
129 | 0 | return {65, 9616}; |
130 | | |
131 | 0 | case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: |
132 | 0 | return {97, 15632}; |
133 | 0 | case Group_Params::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: |
134 | 0 | return {97, 15632}; |
135 | | |
136 | 0 | case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: |
137 | 0 | return {133, 21520}; |
138 | 0 | case Group_Params::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: |
139 | 0 | return {133, 21520}; |
140 | | |
141 | 0 | default: |
142 | 0 | return {}; |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | 0 | std::vector<std::unique_ptr<Public_Key>> convert_kex_to_kem_pks(std::vector<std::unique_ptr<Public_Key>> pks) { |
147 | 0 | std::vector<std::unique_ptr<Public_Key>> result; |
148 | 0 | std::transform(pks.begin(), pks.end(), std::back_inserter(result), [](auto& key) -> std::unique_ptr<Public_Key> { |
149 | 0 | BOTAN_ARG_CHECK(key != nullptr, "Public key list contains a nullptr"); |
150 | 0 | if(key->supports_operation(PublicKeyOperation::KeyAgreement) && |
151 | 0 | !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { |
152 | 0 | return std::make_unique<KEX_to_KEM_Adapter_PublicKey>(std::move(key)); |
153 | 0 | } else { |
154 | 0 | return std::move(key); |
155 | 0 | } |
156 | 0 | }); |
157 | 0 | return result; |
158 | 0 | } |
159 | | |
160 | 0 | std::vector<std::unique_ptr<Private_Key>> convert_kex_to_kem_sks(std::vector<std::unique_ptr<Private_Key>> sks) { |
161 | 0 | std::vector<std::unique_ptr<Private_Key>> result; |
162 | 0 | std::transform(sks.begin(), sks.end(), std::back_inserter(result), [](auto& key) -> std::unique_ptr<Private_Key> { |
163 | 0 | BOTAN_ARG_CHECK(key != nullptr, "Private key list contains a nullptr"); |
164 | 0 | if(key->supports_operation(PublicKeyOperation::KeyAgreement) && |
165 | 0 | !key->supports_operation(PublicKeyOperation::KeyEncapsulation)) { |
166 | 0 | auto ka_key = dynamic_cast<PK_Key_Agreement_Key*>(key.get()); |
167 | 0 | BOTAN_ASSERT_NONNULL(ka_key); |
168 | 0 | (void)key.release(); |
169 | 0 | return std::make_unique<KEX_to_KEM_Adapter_PrivateKey>(std::unique_ptr<PK_Key_Agreement_Key>(ka_key)); |
170 | 0 | } else { |
171 | 0 | return std::move(key); |
172 | 0 | } |
173 | 0 | }); |
174 | 0 | return result; |
175 | 0 | } |
176 | | |
177 | | template <typename KEM_Operation> |
178 | | void concat_secret_combiner(KEM_Operation& op, |
179 | | std::span<uint8_t> out_shared_secret, |
180 | | const std::vector<secure_vector<uint8_t>>& shared_secrets, |
181 | 0 | size_t desired_shared_key_len) { |
182 | 0 | BOTAN_ARG_CHECK(out_shared_secret.size() == op.shared_key_length(desired_shared_key_len), |
183 | 0 | "Invalid output buffer size"); |
184 | |
|
185 | 0 | BufferStuffer shared_secret_stuffer(out_shared_secret); |
186 | 0 | for(size_t idx = 0; idx < shared_secrets.size(); idx++) { |
187 | 0 | shared_secret_stuffer.append(shared_secrets.at(idx)); |
188 | 0 | } |
189 | 0 | BOTAN_ASSERT_NOMSG(shared_secret_stuffer.full()); |
190 | 0 | } Unexecuted instantiation: hybrid_public_key.cpp:void Botan::TLS::(anonymous namespace)::concat_secret_combiner<Botan::TLS::(anonymous namespace)::Hybrid_TLS_KEM_Encryptor>(Botan::TLS::(anonymous namespace)::Hybrid_TLS_KEM_Encryptor&, std::__1::span<unsigned char, 18446744073709551615ul>, std::__1::vector<std::__1::vector<unsigned char, Botan::secure_allocator<unsigned char> >, std::__1::allocator<std::__1::vector<unsigned char, Botan::secure_allocator<unsigned char> > > > const&, unsigned long) Unexecuted instantiation: hybrid_public_key.cpp:void Botan::TLS::(anonymous namespace)::concat_secret_combiner<Botan::TLS::(anonymous namespace)::Hybrid_TLS_KEM_Decryptor>(Botan::TLS::(anonymous namespace)::Hybrid_TLS_KEM_Decryptor&, std::__1::span<unsigned char, 18446744073709551615ul>, std::__1::vector<std::__1::vector<unsigned char, Botan::secure_allocator<unsigned char> >, std::__1::allocator<std::__1::vector<unsigned char, Botan::secure_allocator<unsigned char> > > > const&, unsigned long) |
191 | | |
192 | | template <typename KEM_Operation> |
193 | 0 | size_t concat_shared_key_length(const std::vector<KEM_Operation>& operation) { |
194 | 0 | return reduce( |
195 | 0 | operation, size_t(0), [](size_t acc, const auto& op) { return acc + op.shared_key_length(0 /*no KDF*/); }); Unexecuted instantiation: hybrid_public_key.cpp:auto Botan::TLS::(anonymous namespace)::concat_shared_key_length<Botan::PK_KEM_Encryptor>(std::__1::vector<Botan::PK_KEM_Encryptor, std::__1::allocator<Botan::PK_KEM_Encryptor> > const&)::{lambda(unsigned long, auto:1 const&)#1}::operator()<Botan::PK_KEM_Encryptor>(unsigned long, Botan::PK_KEM_Encryptor const&) const Unexecuted instantiation: hybrid_public_key.cpp:auto Botan::TLS::(anonymous namespace)::concat_shared_key_length<Botan::PK_KEM_Decryptor>(std::__1::vector<Botan::PK_KEM_Decryptor, std::__1::allocator<Botan::PK_KEM_Decryptor> > const&)::{lambda(unsigned long, auto:1 const&)#1}::operator()<Botan::PK_KEM_Decryptor>(unsigned long, Botan::PK_KEM_Decryptor const&) const |
196 | 0 | } Unexecuted instantiation: hybrid_public_key.cpp:unsigned long Botan::TLS::(anonymous namespace)::concat_shared_key_length<Botan::PK_KEM_Encryptor>(std::__1::vector<Botan::PK_KEM_Encryptor, std::__1::allocator<Botan::PK_KEM_Encryptor> > const&) Unexecuted instantiation: hybrid_public_key.cpp:unsigned long Botan::TLS::(anonymous namespace)::concat_shared_key_length<Botan::PK_KEM_Decryptor>(std::__1::vector<Botan::PK_KEM_Decryptor, std::__1::allocator<Botan::PK_KEM_Decryptor> > const&) |
197 | | |
198 | | /// Encryptor that simply concatenates the multiple shared secrets |
199 | | class Hybrid_TLS_KEM_Encryptor final : public KEM_Encryption_with_Combiner { |
200 | | public: |
201 | | Hybrid_TLS_KEM_Encryptor(const std::vector<std::unique_ptr<Public_Key>>& public_keys, std::string_view provider) : |
202 | 0 | KEM_Encryption_with_Combiner(public_keys, provider) {} |
203 | | |
204 | | void combine_shared_secrets(std::span<uint8_t> out_shared_secret, |
205 | | const std::vector<secure_vector<uint8_t>>& shared_secrets, |
206 | | const std::vector<std::vector<uint8_t>>& /*ciphertexts*/, |
207 | | size_t desired_shared_key_len, |
208 | 0 | std::span<const uint8_t> /*salt*/) override { |
209 | 0 | concat_secret_combiner(*this, out_shared_secret, shared_secrets, desired_shared_key_len); |
210 | 0 | } |
211 | | |
212 | 0 | size_t shared_key_length(size_t /*desired_shared_key_len*/) const override { |
213 | 0 | return concat_shared_key_length(encryptors()); |
214 | 0 | } |
215 | | }; |
216 | | |
217 | | /// Decryptor that simply concatenates the multiple shared secrets |
218 | | class Hybrid_TLS_KEM_Decryptor final : public KEM_Decryption_with_Combiner { |
219 | | public: |
220 | | Hybrid_TLS_KEM_Decryptor(const std::vector<std::unique_ptr<Private_Key>>& private_keys, |
221 | | RandomNumberGenerator& rng, |
222 | | const std::string_view provider) : |
223 | 0 | KEM_Decryption_with_Combiner(private_keys, rng, provider) {} |
224 | | |
225 | | void combine_shared_secrets(std::span<uint8_t> out_shared_secret, |
226 | | const std::vector<secure_vector<uint8_t>>& shared_secrets, |
227 | | const std::vector<std::vector<uint8_t>>& /*ciphertexts*/, |
228 | | size_t desired_shared_key_len, |
229 | 0 | std::span<const uint8_t> /*salt*/) override { |
230 | 0 | concat_secret_combiner(*this, out_shared_secret, shared_secrets, desired_shared_key_len); |
231 | 0 | } |
232 | | |
233 | 0 | size_t shared_key_length(size_t /*desired_shared_key_len*/) const override { |
234 | 0 | return concat_shared_key_length(decryptors()); |
235 | 0 | } |
236 | | }; |
237 | | |
238 | | } // namespace |
239 | | |
240 | | std::unique_ptr<Hybrid_KEM_PublicKey> Hybrid_KEM_PublicKey::load_for_group( |
241 | 0 | Group_Params group, std::span<const uint8_t> concatenated_public_keys) { |
242 | 0 | const auto public_key_lengths = public_key_lengths_for_group(group); |
243 | 0 | auto alg_ids = algorithm_identifiers_for_group(group); |
244 | 0 | BOTAN_ASSERT_NOMSG(public_key_lengths.size() == alg_ids.size()); |
245 | |
|
246 | 0 | const auto expected_public_keys_length = |
247 | 0 | reduce(public_key_lengths, size_t(0), [](size_t acc, size_t len) { return acc + len; }); |
248 | 0 | if(expected_public_keys_length != concatenated_public_keys.size()) { |
249 | 0 | throw Decoding_Error("Concatenated public values have an unexpected length"); |
250 | 0 | } |
251 | | |
252 | 0 | BufferSlicer public_key_slicer(concatenated_public_keys); |
253 | 0 | std::vector<std::unique_ptr<Public_Key>> pks; |
254 | 0 | pks.reserve(alg_ids.size()); |
255 | 0 | for(size_t idx = 0; idx < alg_ids.size(); ++idx) { |
256 | 0 | pks.emplace_back(load_public_key(alg_ids[idx], public_key_slicer.take(public_key_lengths[idx]))); |
257 | 0 | } |
258 | 0 | BOTAN_ASSERT_NOMSG(public_key_slicer.empty()); |
259 | 0 | return std::make_unique<Hybrid_KEM_PublicKey>(std::move(pks)); |
260 | 0 | } |
261 | | |
262 | | Hybrid_KEM_PublicKey::Hybrid_KEM_PublicKey(std::vector<std::unique_ptr<Public_Key>> pks) : |
263 | 0 | Hybrid_PublicKey(convert_kex_to_kem_pks(std::move(pks))) {} Unexecuted instantiation: Botan::TLS::Hybrid_KEM_PublicKey::Hybrid_KEM_PublicKey(std::__1::vector<std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> >, std::__1::allocator<std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> > > >) Unexecuted instantiation: Botan::TLS::Hybrid_KEM_PublicKey::Hybrid_KEM_PublicKey(std::__1::vector<std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> >, std::__1::allocator<std::__1::unique_ptr<Botan::Public_Key, std::__1::default_delete<Botan::Public_Key> > > >) |
264 | | |
265 | | Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::vector<std::unique_ptr<Private_Key>> sks) : |
266 | 0 | Hybrid_PublicKey(convert_kex_to_kem_pks(extract_public_keys(sks))), |
267 | 0 | Hybrid_PrivateKey(convert_kex_to_kem_sks(std::move(sks))) {} Unexecuted instantiation: Botan::TLS::Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::__1::vector<std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> >, std::__1::allocator<std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> > > >) Unexecuted instantiation: Botan::TLS::Hybrid_KEM_PrivateKey::Hybrid_KEM_PrivateKey(std::__1::vector<std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> >, std::__1::allocator<std::__1::unique_ptr<Botan::Private_Key, std::__1::default_delete<Botan::Private_Key> > > >) |
268 | | |
269 | 0 | std::string Hybrid_KEM_PublicKey::algo_name() const { |
270 | 0 | std::ostringstream algo_name("Hybrid("); |
271 | 0 | for(size_t i = 0; i < public_keys().size(); ++i) { |
272 | 0 | if(i > 0) { |
273 | 0 | algo_name << ","; |
274 | 0 | } |
275 | 0 | algo_name << public_keys().at(i)->algo_name(); |
276 | 0 | } |
277 | 0 | algo_name << ")"; |
278 | 0 | return algo_name.str(); |
279 | 0 | } |
280 | | |
281 | 0 | AlgorithmIdentifier Hybrid_KEM_PublicKey::algorithm_identifier() const { |
282 | 0 | throw Botan::Not_Implemented("Hybrid keys don't have an algorithm identifier"); |
283 | 0 | } |
284 | | |
285 | 0 | std::vector<uint8_t> Hybrid_KEM_PublicKey::public_key_bits() const { |
286 | 0 | return raw_public_key_bits(); |
287 | 0 | } |
288 | | |
289 | 0 | std::vector<uint8_t> Hybrid_KEM_PublicKey::raw_public_key_bits() const { |
290 | | // draft-ietf-tls-hybrid-design-06 3.2 |
291 | | // The values are directly concatenated, without any additional encoding |
292 | | // or length fields; this assumes that the representation and length of |
293 | | // elements is fixed once the algorithm is fixed. If concatenation were |
294 | | // to be used with values that are not fixed-length, a length prefix or |
295 | | // other unambiguous encoding must be used to ensure that the composition |
296 | | // of the two values is injective. |
297 | 0 | return reduce(public_keys(), std::vector<uint8_t>(), [](auto pkb, const auto& key) { |
298 | 0 | return concat(pkb, key->raw_public_key_bits()); |
299 | 0 | }); |
300 | 0 | } |
301 | | |
302 | 0 | std::unique_ptr<Private_Key> Hybrid_KEM_PublicKey::generate_another(RandomNumberGenerator& rng) const { |
303 | 0 | return std::make_unique<Hybrid_KEM_PrivateKey>(generate_other_sks_from_pks(rng)); |
304 | 0 | } |
305 | | |
306 | | std::unique_ptr<Botan::PK_Ops::KEM_Encryption> Hybrid_KEM_PublicKey::create_kem_encryption_op( |
307 | 0 | std::string_view params, std::string_view provider) const { |
308 | 0 | if(params != "Raw" && !params.empty()) { |
309 | 0 | throw Botan::Invalid_Argument("Hybrid KEM encryption does not support KDFs"); |
310 | 0 | } |
311 | 0 | return std::make_unique<Hybrid_TLS_KEM_Encryptor>(public_keys(), provider); |
312 | 0 | } |
313 | | |
314 | | std::unique_ptr<Hybrid_KEM_PrivateKey> Hybrid_KEM_PrivateKey::generate_from_group(Group_Params group, |
315 | 0 | RandomNumberGenerator& rng) { |
316 | 0 | const auto algo_spec = algorithm_specs_for_group(group); |
317 | 0 | std::vector<std::unique_ptr<Private_Key>> private_keys; |
318 | 0 | private_keys.reserve(algo_spec.size()); |
319 | 0 | for(const auto& spec : algo_spec) { |
320 | 0 | private_keys.push_back(create_private_key(spec.first, rng, spec.second)); |
321 | 0 | } |
322 | 0 | return std::make_unique<Hybrid_KEM_PrivateKey>(std::move(private_keys)); |
323 | 0 | } |
324 | | |
325 | | std::unique_ptr<Botan::PK_Ops::KEM_Decryption> Hybrid_KEM_PrivateKey::create_kem_decryption_op( |
326 | 0 | RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { |
327 | 0 | if(params != "Raw" && !params.empty()) { |
328 | 0 | throw Botan::Invalid_Argument("Hybrid KEM decryption does not support KDFs"); |
329 | 0 | } |
330 | 0 | return std::make_unique<Hybrid_TLS_KEM_Decryptor>(private_keys(), rng, provider); |
331 | 0 | } |
332 | | |
333 | | } // namespace Botan::TLS |