• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 22341213552

24 Feb 2026 07:38AM UTC coverage: 90.335% (-1.6%) from 91.974%
22341213552

Pull #5362

github

web-flow
Merge b312daa66 into af19f625f
Pull Request #5362: store pqcrystals polynomial in secure memory

103041 of 114065 relevant lines covered (90.34%)

11812295.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.12
/src/tests/test_concurrent_pk.cpp
1
/*
2
* (C) 2026 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8

9
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) && defined(BOTAN_TARGET_OS_HAS_THREADS)
10
   #include <botan/pk_algs.h>
11
   #include <botan/pubkey.h>
12
   #include <botan/rng.h>
13
   #include <botan/internal/fmt.h>
14
   #include <future>
15
   #include <sstream>
16
#endif
17

18
namespace Botan_Tests {
19

20
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) && defined(BOTAN_TARGET_OS_HAS_THREADS)
21

22
/*
23
* Test that public key operations (signing, verification, encryption, decryption, KEM, key
24
* agreement) with a shared key from multiple threads produce correct results without racing.
25
*
26
* TODO: Add concurrent test for ECIES handling
27
*/
28

29
namespace {
30

31
constexpr size_t ConcurrentThreads = 10;  // arbitrary
32

33
class ConcurrentPkTestCase {
34
   public:
35
      ConcurrentPkTestCase(std::string_view pk_algo, std::string_view keygen_params, std::string_view op_params = "") :
43✔
36
            m_pk_algo(pk_algo), m_keygen_params(keygen_params), m_op_params(op_params) {}
129✔
37

38
      const std::string& algo_name() const { return m_pk_algo; }
24✔
39

40
      const std::string& op_params() const { return m_op_params; }
572✔
41

42
      Test::Result result(std::string_view operation) const {
67✔
43
         std::ostringstream name;
67✔
44
         name << "Concurrent " << m_pk_algo;
67✔
45
         if(!m_keygen_params.empty()) {
67✔
46
            name << " " << m_keygen_params;
57✔
47
         }
48
         if(!m_op_params.empty()) {
67✔
49
            name << " " << m_op_params;
40✔
50
         }
51
         name << " " << operation;
67✔
52

53
         return Test::Result(name.str());
134✔
54
      }
67✔
55

56
      Test::Result skip_missing(std::string_view operation) const {
×
57
         auto result = this->result(operation);
×
58
         result.test_note("Skipping due to missing algorithm", this->algo_name());
×
59
         return result;
×
60
      }
×
61

62
      std::unique_ptr<Botan::Private_Key> try_create_key(Botan::RandomNumberGenerator& rng) const {
197✔
63
         try {
197✔
64
            return Botan::create_private_key(m_pk_algo, rng, m_keygen_params);
197✔
65
         } catch(Botan::Lookup_Error&) {
×
66
            return nullptr;
×
67
         } catch(Botan::Not_Implemented&) {
×
68
            return nullptr;
×
69
         }
×
70
      }
71

72
   private:
73
      std::string m_pk_algo;
74
      std::string m_keygen_params;
75
      std::string m_op_params;
76
};
77

78
Test::Result test_concurrent_signing(const ConcurrentPkTestCase& tc,
14✔
79
                                     const Botan::Private_Key& privkey,
80
                                     const Botan::Public_Key& pubkey) {
81
   auto result = tc.result("signing");
14✔
82
   auto rng = Test::new_rng(result.who());
14✔
83
   const auto test_message = rng->random_vec(32);
14✔
84

85
   const auto operations_remaining_at_start = privkey.remaining_operations();
14✔
86

87
   std::vector<std::future<std::vector<uint8_t>>> futures;
14✔
88
   futures.reserve(ConcurrentThreads);
14✔
89

90
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
154✔
91
      futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector<uint8_t> {
280✔
92
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
140✔
93
         Botan::PK_Signer signer(privkey, *thread_rng, tc.op_params());
140✔
94
         return signer.sign_message(test_message, *thread_rng);
140✔
95
      }));
280✔
96
   }
97

98
   Botan::PK_Verifier verifier(pubkey, tc.op_params());
14✔
99

100
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
154✔
101
      try {
140✔
102
         const auto signature = futures[i].get();
140✔
103

104
         if(signature.empty()) {
140✔
105
            result.test_failure(Botan::fmt("Thread {} produced empty signature", i));
×
106
         } else {
107
            const bool valid = verifier.verify_message(test_message, signature);
140✔
108
            result.test_is_true(Botan::fmt("Thread {} signature is valid", i), valid);
280✔
109
         }
110
      } catch(std::exception& e) {
140✔
111
         result.test_failure(Botan::fmt("Thread {} failed: {}", i, e.what()));
×
112
      }
×
113
   }
114

115
   if(operations_remaining_at_start.has_value()) {
14✔
116
      result.test_is_true("Private key should be stateful", privkey.stateful_operation());
2✔
117
      const auto left_at_end = privkey.remaining_operations();
2✔
118

119
      if(left_at_end.has_value()) {
2✔
120
         result.test_u64_lt(
2✔
121
            "Number of operations went down", left_at_end.value(), operations_remaining_at_start.value());
2✔
122

123
         const uint64_t consumed = operations_remaining_at_start.value() - left_at_end.value();
2✔
124

125
         result.test_u64_eq(
2✔
126
            "Private key should have consumed exactly ConcurrentThreads many operations", consumed, ConcurrentThreads);
127
      } else {
128
         result.test_failure("Private key remaining_operations should return something both times");
×
129
      }
130
   } else {
131
      result.test_is_false("Private key should not be stateful", privkey.stateful_operation());
12✔
132
   }
133

134
   return result;
28✔
135
}
42✔
136

137
Test::Result test_concurrent_verification(const ConcurrentPkTestCase& tc,
14✔
138
                                          const Botan::Private_Key& privkey,
139
                                          const Botan::Public_Key& pubkey) {
140
   auto result = tc.result("verification");
28✔
141
   auto rng = Test::new_rng(result.who());
14✔
142
   const auto test_message = rng->random_vec(32);
14✔
143

144
   Botan::PK_Signer signer(privkey, *rng, tc.op_params());
14✔
145
   const auto signature = signer.sign_message(test_message, *rng);
14✔
146

147
   std::vector<std::future<bool>> futures;
14✔
148
   futures.reserve(ConcurrentThreads);
14✔
149

150
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
154✔
151
      futures.push_back(std::async(std::launch::async, [&]() -> bool {
280✔
152
         Botan::PK_Verifier verifier(pubkey, tc.op_params());
140✔
153
         return verifier.verify_message(test_message, signature);
280✔
154
      }));
140✔
155
   }
156

157
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
154✔
158
      try {
140✔
159
         const bool valid = futures[i].get();
140✔
160
         result.test_is_true(Botan::fmt("Thread {} verification succeeded", i), valid);
280✔
161
      } catch(std::exception& e) {
×
162
         result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what()));
×
163
      }
×
164
   }
165

166
   return result;
28✔
167
}
56✔
168

169
Test::Result test_concurrent_encryption(const ConcurrentPkTestCase& tc,
2✔
170
                                        const Botan::Private_Key& privkey,
171
                                        const Botan::Public_Key& pubkey) {
172
   auto result = tc.result("encryption");
2✔
173
   auto rng = Test::new_rng(result.who());
2✔
174
   const auto test_message = rng->random_vec(32);
2✔
175

176
   std::vector<std::future<std::vector<uint8_t>>> futures;
2✔
177
   futures.reserve(ConcurrentThreads);
2✔
178

179
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
22✔
180
      futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector<uint8_t> {
40✔
181
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
20✔
182
         const Botan::PK_Encryptor_EME encryptor(pubkey, *thread_rng, tc.op_params());
20✔
183
         return encryptor.encrypt(test_message, *thread_rng);
20✔
184
      }));
40✔
185
   }
186

187
   const Botan::PK_Decryptor_EME decryptor(privkey, *rng, tc.op_params());
2✔
188

189
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
22✔
190
      try {
20✔
191
         const auto ciphertext = futures[i].get();
20✔
192
         const auto plaintext = decryptor.decrypt(ciphertext);
20✔
193
         result.test_bin_eq(Botan::fmt("Thread {} decrypts correctly", i), plaintext, test_message);
40✔
194
      } catch(std::exception& e) {
40✔
195
         result.test_failure(Botan::fmt("Thread {} encrypt threw: {}", i, e.what()));
×
196
      }
×
197
   }
198

199
   return result;
4✔
200
}
6✔
201

202
Test::Result test_concurrent_decryption(const ConcurrentPkTestCase& tc,
2✔
203
                                        const Botan::Private_Key& privkey,
204
                                        const Botan::Public_Key& pubkey) {
205
   auto result = tc.result("decryption");
2✔
206
   auto rng = Test::new_rng(result.who());
2✔
207
   const auto test_message = rng->random_vec(32);
2✔
208

209
   const Botan::PK_Encryptor_EME encryptor(pubkey, *rng, tc.op_params());
2✔
210
   const auto ciphertext = encryptor.encrypt(test_message, *rng);
2✔
211

212
   std::vector<std::future<Botan::secure_vector<uint8_t>>> futures;
2✔
213
   futures.reserve(ConcurrentThreads);
2✔
214

215
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
22✔
216
      futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::secure_vector<uint8_t> {
40✔
217
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
20✔
218
         const Botan::PK_Decryptor_EME decryptor(privkey, *thread_rng, tc.op_params());
20✔
219
         return decryptor.decrypt(ciphertext);
20✔
220
      }));
40✔
221
   }
222

223
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
22✔
224
      try {
20✔
225
         const auto plaintext = futures[i].get();
20✔
226
         result.test_bin_eq(Botan::fmt("Thread {} decrypts correctly", i), plaintext, test_message);
40✔
227
      } catch(std::exception& e) {
20✔
228
         result.test_failure(Botan::fmt("Thread {} decrypt threw: {}", i, e.what()));
×
229
      }
×
230
   }
231

232
   return result;
4✔
233
}
8✔
234

235
Test::Result test_concurrent_kem_encap(const ConcurrentPkTestCase& tc,
8✔
236
                                       const Botan::Private_Key& privkey,
237
                                       const Botan::Public_Key& pubkey) {
238
   auto result = tc.result("KEM encapsulate");
8✔
239
   auto rng = Test::new_rng(result.who());
8✔
240

241
   std::vector<std::future<Botan::KEM_Encapsulation>> futures;
8✔
242
   futures.reserve(ConcurrentThreads);
8✔
243

244
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
88✔
245
      futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::KEM_Encapsulation {
160✔
246
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
80✔
247
         Botan::PK_KEM_Encryptor encryptor(pubkey, tc.op_params());
80✔
248
         return encryptor.encrypt(*thread_rng);
80✔
249
      }));
160✔
250
   }
251

252
   Botan::PK_KEM_Decryptor decryptor(privkey, *rng, tc.op_params());
8✔
253

254
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
88✔
255
      try {
80✔
256
         const auto kr = futures[i].get();
80✔
257
         const auto shared_key = decryptor.decrypt(kr.encapsulated_shared_key(), 32);
80✔
258
         result.test_bin_eq(Botan::fmt("Thread {} shared key matches", i), shared_key, kr.shared_key());
160✔
259
      } catch(std::exception& e) {
80✔
260
         result.test_failure(Botan::fmt("Thread {} encapsulate threw: {}", i, e.what()));
×
261
      }
×
262
   }
263

264
   return result;
16✔
265
}
16✔
266

267
Test::Result test_concurrent_kem_decap(const ConcurrentPkTestCase& tc,
8✔
268
                                       const Botan::Private_Key& privkey,
269
                                       const Botan::Public_Key& pubkey) {
270
   auto result = tc.result("KEM decapsulate");
8✔
271
   auto rng = Test::new_rng(result.who());
8✔
272

273
   Botan::PK_KEM_Encryptor encryptor(pubkey, tc.op_params());
8✔
274
   auto kem_enc = encryptor.encrypt(*rng);
8✔
275

276
   std::vector<std::future<Botan::secure_vector<uint8_t>>> futures;
8✔
277
   futures.reserve(ConcurrentThreads);
8✔
278

279
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
88✔
280
      futures.push_back(std::async(std::launch::async, [&, i]() -> Botan::secure_vector<uint8_t> {
160✔
281
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
80✔
282
         Botan::PK_KEM_Decryptor decryptor(privkey, *thread_rng, tc.op_params());
80✔
283
         return decryptor.decrypt(kem_enc.encapsulated_shared_key(), 0);
80✔
284
      }));
160✔
285
   }
286

287
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
88✔
288
      try {
80✔
289
         const auto shared_key = futures[i].get();
80✔
290
         result.test_bin_eq(Botan::fmt("Thread {} shared key matches", i), shared_key, kem_enc.shared_key());
160✔
291
      } catch(std::exception& e) {
80✔
292
         result.test_failure(Botan::fmt("Thread {} decapsulate threw: {}", i, e.what()));
×
293
      }
×
294
   }
295

296
   return result;
16✔
297
}
16✔
298

299
Test::Result test_concurrent_key_agreement(const ConcurrentPkTestCase& tc) {
4✔
300
   auto result = tc.result("key agreement");
4✔
301

302
   auto rng = Test::new_rng(result.who());
4✔
303
   auto our_key = tc.try_create_key(*rng);
4✔
304
   if(!our_key) {
4✔
305
      result.test_note("Skipping due to missing algorithm");
×
306
      return result;
×
307
   }
308

309
   auto peer_key = tc.try_create_key(*rng);
4✔
310

311
   const auto* our_ka_key = dynamic_cast<Botan::PK_Key_Agreement_Key*>(our_key.get());
4✔
312
   const auto* peer_ka_key = dynamic_cast<Botan::PK_Key_Agreement_Key*>(peer_key.get());
4✔
313
   if(our_ka_key == nullptr || peer_ka_key == nullptr) {
4✔
314
      result.test_failure("Key does not support key agreement");
×
315
      return result;
316
   }
317

318
   const auto peer_public = peer_ka_key->public_value();
4✔
319

320
   // Compute reference shared secret single-threaded
321
   const Botan::PK_Key_Agreement ref_ka(*our_key, *rng, tc.op_params());
4✔
322
   const auto reference_secret = ref_ka.derive_key(32, peer_public).bits_of();
8✔
323

324
   std::vector<std::future<std::vector<uint8_t>>> futures;
4✔
325
   futures.reserve(ConcurrentThreads);
4✔
326

327
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
44✔
328
      futures.push_back(std::async(std::launch::async, [&, i]() -> std::vector<uint8_t> {
80✔
329
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
40✔
330
         const Botan::PK_Key_Agreement ka(*our_key, *thread_rng, tc.op_params());
40✔
331
         return Botan::unlock(ka.derive_key(32, peer_public).bits_of());
120✔
332
      }));
80✔
333
   }
334

335
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
44✔
336
      try {
40✔
337
         const auto shared_secret = futures[i].get();
40✔
338
         result.test_bin_eq(Botan::fmt("Thread {} shared secret matches", i), shared_secret, reference_secret);
80✔
339
      } catch(std::exception& e) {
40✔
340
         result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what()));
×
341
      }
×
342
   }
343

344
   return result;
4✔
345
}
24✔
346

347
Test::Result test_concurrent_key_generation(const ConcurrentPkTestCase& tc) {
15✔
348
   auto result = tc.result("key generation");
15✔
349

350
   auto rng = Test::new_rng(result.who());
15✔
351

352
   if(tc.try_create_key(*rng) == nullptr) {
30✔
353
      result.test_note("Keygen not available");
×
354
      return result;
×
355
   }
356

357
   std::vector<std::future<std::unique_ptr<Botan::Private_Key>>> futures;
15✔
358
   futures.reserve(ConcurrentThreads);
15✔
359

360
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
165✔
361
      futures.push_back(std::async(std::launch::async, [&, i]() -> std::unique_ptr<Botan::Private_Key> {
300✔
362
         auto thread_rng = Test::new_rng(Botan::fmt("{} thread {}", result.who(), i));
150✔
363
         return tc.try_create_key(*thread_rng);
150✔
364
      }));
150✔
365
   }
366

367
   for(size_t i = 0; i != ConcurrentThreads; ++i) {
165✔
368
      try {
150✔
369
         const auto sk = futures[i].get();
150✔
370
         result.test_not_null(Botan::fmt("Thread {} generated a key", i), sk.get());
150✔
371

372
         if(sk) {
150✔
373
            result.test_is_true(Botan::fmt("Thread {} generated key seems valid", i), sk->check_key(*rng, true));
300✔
374
         }
375
      } catch(std::exception& e) {
150✔
376
         result.test_failure(Botan::fmt("Thread {} threw: {}", i, e.what()));
×
377
      }
×
378
   }
379

380
   return result;
15✔
381
}
30✔
382

383
class Concurrent_Public_Key_Operations_Test : public Test {
1✔
384
   public:
385
      std::vector<Test::Result> run() override {
1✔
386
         std::vector<Test::Result> results;
1✔
387

388
         concurrent_signing_and_verification_tests(results);
1✔
389
         concurrent_encryption_tests(results);
1✔
390
         concurrent_kem_tests(results);
1✔
391
         concurrent_key_agreement_tests(results);
1✔
392
         concurrent_key_generation_tests(results);
1✔
393

394
         return results;
1✔
395
      }
×
396

397
   private:
398
      void concurrent_signing_and_verification_tests(std::vector<Test::Result>& results) {
1✔
399
         const std::vector<ConcurrentPkTestCase> test_cases = {
1✔
400
            ConcurrentPkTestCase("RSA", "1536", "PKCS1v15(SHA-256)"),
401
            ConcurrentPkTestCase("ECDSA", "secp256r1", "SHA-256"),
402
            ConcurrentPkTestCase("ECKCDSA", "secp256r1", "SHA-256"),
403
            ConcurrentPkTestCase("ECGDSA", "secp256r1", "SHA-256"),
404
            ConcurrentPkTestCase("DSA", "dsa/jce/1024", "SHA-256"),
405
            ConcurrentPkTestCase("SM2", "sm2p256v1", "SM3"),
406
            ConcurrentPkTestCase("Ed25519", "", "Pure"),
407
            ConcurrentPkTestCase("Ed448", "", "Pure"),
408
            ConcurrentPkTestCase("ML-DSA", "ML-DSA-4x4"),
409
            ConcurrentPkTestCase("Dilithium", "Dilithium-4x4-r3"),
410
            ConcurrentPkTestCase("Dilithium", "Dilithium-4x4-AES-r3"),
411
            ConcurrentPkTestCase("SLH-DSA", "SLH-DSA-SHA2-128f"),
412
            ConcurrentPkTestCase("HSS-LMS", "SHA-256,HW(5,8)"),
413
            ConcurrentPkTestCase("XMSS", "XMSS-SHA2_10_256"),
414
         };
15✔
415

416
         for(const auto& tc : test_cases) {
15✔
417
            auto rng = Test::new_rng(tc.algo_name());
14✔
418

419
            if(auto privkey = tc.try_create_key(*rng)) {
14✔
420
               auto pubkey = privkey->public_key();
14✔
421
               results.push_back(test_concurrent_signing(tc, *privkey, *pubkey));
28✔
422
               results.push_back(test_concurrent_verification(tc, *privkey, *pubkey));
28✔
423
            } else {
14✔
424
               results.push_back(tc.skip_missing("signing"));
×
425
            }
14✔
426
         }
14✔
427
      }
3✔
428

429
      void concurrent_encryption_tests(std::vector<Test::Result>& results) {
1✔
430
         const std::vector<ConcurrentPkTestCase> test_cases = {
1✔
431
            ConcurrentPkTestCase("RSA", "1536", "OAEP(SHA-256)"),
432
            ConcurrentPkTestCase("ElGamal", "modp/ietf/1536", "PKCS1v15"),
433
         };
3✔
434

435
         for(const auto& tc : test_cases) {
3✔
436
            auto rng = Test::new_rng(tc.algo_name());
2✔
437

438
            if(auto privkey = tc.try_create_key(*rng)) {
2✔
439
               auto pubkey = privkey->public_key();
2✔
440
               results.push_back(test_concurrent_encryption(tc, *privkey, *pubkey));
4✔
441
               results.push_back(test_concurrent_decryption(tc, *privkey, *pubkey));
4✔
442
            } else {
2✔
443
               results.push_back(tc.skip_missing("encryption"));
×
444
            }
2✔
445
         }
2✔
446
      }
3✔
447

448
      void concurrent_kem_tests(std::vector<Test::Result>& results) {
1✔
449
         const std::vector<ConcurrentPkTestCase> test_cases = {
1✔
450
            ConcurrentPkTestCase("RSA", "1536", "Raw"),
451
            ConcurrentPkTestCase("ClassicMcEliece", "348864f", "Raw"),
452
            ConcurrentPkTestCase("McEliece", "1632,33", "Raw"),
453
            ConcurrentPkTestCase("FrodoKEM", "FrodoKEM-640-SHAKE", "Raw"),
454
            ConcurrentPkTestCase("FrodoKEM", "FrodoKEM-640-AES", "Raw"),
455
            ConcurrentPkTestCase("ML-KEM", "ML-KEM-512", "Raw"),
456
            ConcurrentPkTestCase("Kyber", "Kyber-512-90s-r3", "Raw"),
457
            ConcurrentPkTestCase("Kyber", "Kyber-512-r3", "Raw"),
458
         };
9✔
459

460
         for(const auto& tc : test_cases) {
9✔
461
            auto rng = Test::new_rng(tc.algo_name());
8✔
462
            if(auto privkey = tc.try_create_key(*rng)) {
8✔
463
               auto pubkey = privkey->public_key();
8✔
464
               results.push_back(test_concurrent_kem_encap(tc, *privkey, *pubkey));
16✔
465
               results.push_back(test_concurrent_kem_decap(tc, *privkey, *pubkey));
16✔
466
            } else {
8✔
467
               results.push_back(tc.skip_missing("KEM encapsulate"));
×
468
            }
8✔
469
         }
8✔
470
      }
3✔
471

472
      void concurrent_key_agreement_tests(std::vector<Test::Result>& results) {
1✔
473
         const std::vector<ConcurrentPkTestCase> test_cases = {
1✔
474
            ConcurrentPkTestCase("DH", "modp/ietf/1536", "Raw"),
475
            ConcurrentPkTestCase("ECDH", "secp256r1", "Raw"),
476
            ConcurrentPkTestCase("X25519", "", "Raw"),
477
            ConcurrentPkTestCase("X448", "", "Raw"),
478
         };
5✔
479

480
         for(const auto& tc : test_cases) {
5✔
481
            results.push_back(test_concurrent_key_agreement(tc));
8✔
482
         }
483
      }
3✔
484

485
      void concurrent_key_generation_tests(std::vector<Test::Result>& results) {
1✔
486
         const std::vector<ConcurrentPkTestCase> test_cases = {
1✔
487
            ConcurrentPkTestCase("ClassicMcEliece", "348864f"),
488
            ConcurrentPkTestCase("DH", "modp/ietf/1536"),
489
            ConcurrentPkTestCase("DSA", "dsa/jce/1024"),
490
            ConcurrentPkTestCase("ECDH", "secp256r1"),
491
            ConcurrentPkTestCase("ECDSA", "secp256r1"),
492
            ConcurrentPkTestCase("ECGDSA", "secp256r1"),
493
            ConcurrentPkTestCase("ECKCDSA", "secp256r1"),
494
            ConcurrentPkTestCase("Ed25519", ""),
495
            ConcurrentPkTestCase("Ed448", ""),
496
            ConcurrentPkTestCase("HSS-LMS", "SHA-256,HW(5,8)"),
497
            ConcurrentPkTestCase("RSA", "1536"),
498
            ConcurrentPkTestCase("SLH-DSA", "SLH-DSA-SHA2-128f"),
499
            ConcurrentPkTestCase("SM2", "sm2p256v1"),
500
            ConcurrentPkTestCase("X25519", ""),
501
            ConcurrentPkTestCase("X448", ""),
502
         };
16✔
503

504
         for(const auto& tc : test_cases) {
16✔
505
            results.push_back(test_concurrent_key_generation(tc));
30✔
506
         }
507
      }
3✔
508
};
509

510
BOTAN_REGISTER_SERIALIZED_TEST("pubkey", "pk_concurrent_ops", Concurrent_Public_Key_Operations_Test);
511

512
}  // namespace
513

514
#endif
515

516
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc