• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

randombit / botan / 29630228535

18 Jul 2026 02:46AM UTC coverage: 89.407% (-2.3%) from 91.66%
29630228535

push

github

web-flow
Merge pull request #5740 from randombit/jack/x509-serial-number

Add an explicit type for X509 certificate serial numbers

114089 of 127606 relevant lines covered (89.41%)

10801388.62 hits per line

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

82.21
/src/tests/test_hss_lms.cpp
1
/*
2
* (C) 2023 Jack Lloyd
3
*     2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_HSS_LMS)
11
   #include "test_arb_eq.h"
12
   #include "test_pubkey.h"
13
   #include <botan/asn1_obj.h>
14
   #include <botan/exceptn.h>
15
   #include <botan/hss_lms.h>
16
   #include <botan/pk_algs.h>
17
   #include <botan/pubkey.h>
18
   #include <botan/internal/fmt.h>
19
   #include <botan/internal/hss.h>
20
   #include <botan/internal/loadstor.h>
21
   #include <limits>
22

23
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
24
      #include <sys/wait.h>
25
      #include <unistd.h>
26
   #endif
27

28
namespace Botan_Tests {
29

30
namespace {
31

32
/**
33
 * @brief Test the correct parsing of HSS-LMS parameters
34
 */
35
std::vector<Test::Result> test_hss_lms_params_parsing() {
1✔
36
   return {
1✔
37
      CHECK("HSS Parameter Parsing",
38
            [&](Test::Result& result) {
1✔
39
               result.test_no_throw("no throw", [&] {
1✔
40
                  const Botan::HSS_LMS_Params hss_params("SHA-256,HW(5,1),HW(25,8)");
1✔
41

42
                  test_arb_eq(result, "hss levels", hss_params.L(), Botan::HSS_Level(2));
1✔
43
                  const auto& top_lms_params = hss_params.params_at_level(Botan::HSS_Level(0));
1✔
44
                  result.test_str_eq("hash name", top_lms_params.lms_params().hash_name(), std::string("SHA-256"));
1✔
45
                  result.test_enum_eq("top level - lms type",
2✔
46
                                      top_lms_params.lms_params().algorithm_type(),
1✔
47
                                      Botan::LMS_Algorithm_Type::SHA256_M32_H5);
1✔
48
                  result.test_enum_eq("top level - ots type",
2✔
49
                                      top_lms_params.lmots_params().algorithm_type(),
1✔
50
                                      Botan::LMOTS_Algorithm_Type::SHA256_N32_W1);
1✔
51

52
                  const auto& second_lms_params = hss_params.params_at_level(Botan::HSS_Level(1));
1✔
53
                  result.test_enum_eq("2nd level - lms type",
2✔
54
                                      second_lms_params.lms_params().algorithm_type(),
1✔
55
                                      Botan::LMS_Algorithm_Type::SHA256_M32_H25);
1✔
56
                  result.test_enum_eq("2nd level - ots type",
2✔
57
                                      second_lms_params.lmots_params().algorithm_type(),
58
                                      Botan::LMOTS_Algorithm_Type::SHA256_N32_W8);
1✔
59
               });
3✔
60
            }),
1✔
61

62
   };
2✔
63
}
1✔
64

65
/**
66
 * @brief Test signature generation using the raw private key bytes
67
 */
68
class HSS_LMS_Signature_Generation_Test final : public PK_Signature_Generation_Test {
69
   public:
70
      HSS_LMS_Signature_Generation_Test() :
1✔
71
            PK_Signature_Generation_Test("HSS-LMS", "pubkey/hss_lms_sig.vec", "Msg,PrivateKey,Signature") {}
2✔
72

73
      std::string default_padding(const VarMap& /*vars*/) const final { return ""; }
2✔
74

75
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) final {
2✔
76
         const auto sk_bytes = vars.get_req_bin("PrivateKey");
2✔
77
         return std::make_unique<Botan::HSS_LMS_PrivateKey>(Botan::AlgorithmIdentifier(), sk_bytes);
2✔
78
      }
2✔
79
};
80

81
/**
82
 * @brief Test signature verification using the raw public key bytes
83
 */
84
class HSS_LMS_Signature_Verify_Tests final : public PK_Signature_Verification_Test {
85
   public:
86
      HSS_LMS_Signature_Verify_Tests() :
1✔
87
            PK_Signature_Verification_Test("HSS-LMS", "pubkey/hss_lms_verify.vec", "Msg,PublicKey,Signature") {}
2✔
88

89
      std::string default_padding(const VarMap& /*vars*/) const final { return ""; }
5✔
90

91
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
5✔
92
         const std::vector<uint8_t> pk_bytes = vars.get_req_bin("PublicKey");
5✔
93
         return std::make_unique<Botan::HSS_LMS_PublicKey>(Botan::AlgorithmIdentifier(), pk_bytes);
10✔
94
      }
5✔
95
};
96

97
/**
98
 * @brief Test the correct revocation of invalid signatures
99
 */
100
class HSS_LMS_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerification_Test {
101
   public:
102
      HSS_LMS_Signature_Verify_Invalid_Tests() :
1✔
103
            PK_Signature_NonVerification_Test(
104
               "HSS_LMS", "pubkey/hss_lms_invalid.vec", "Msg,PublicKey,InvalidSignature") {}
2✔
105

106
      std::string default_padding(const VarMap& /*vars*/) const override { return ""; }
4✔
107

108
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
4✔
109
         const std::vector<uint8_t> raw_key = vars.get_req_bin("PublicKey");
4✔
110
         return std::make_unique<Botan::HSS_LMS_PublicKey>(Botan::AlgorithmIdentifier(), raw_key);
8✔
111
      }
4✔
112
};
113

114
/**
115
 * @brief Test HSS-LMS public key creation
116
 */
117
class HSS_LMS_Key_Generation_Test final : public PK_Key_Generation_Test {
1✔
118
   public:
119
      std::vector<std::string> keygen_params() const final { return {"SHA-256,HW(10,4),HW(5,8)"}; }
1✔
120

121
      std::string algo_name() const final { return "HSS-LMS"; }
1✔
122

123
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view /* keygen_params */,
1✔
124
                                                             std::string_view /* provider */,
125
                                                             std::span<const uint8_t> raw_pk) const override {
126
         return std::make_unique<Botan::HSS_LMS_PublicKey>(Botan::AlgorithmIdentifier(), raw_pk);
2✔
127
      }
128
};
129

130
/**
131
 * @brief Test that for manipulated signatures and too short signatures, private keys, and public keys a DecodeError occurs.
132
 */
133
class HSS_LMS_Negative_Tests final : public Test {
1✔
134
      Test::Result test_flipped_signature_bits() {
1✔
135
         Test::Result result("HSS-LMS - flipped signature bits");
1✔
136

137
         auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
138

139
         Botan::PK_Signer signer(*sk, Test::rng(), "");
1✔
140
         Botan::PK_Verifier verifier(*sk, "");
2✔
141

142
         std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
143

144
         signer.update(mes);
1✔
145
         auto valid_sig = signer.signature(Test::rng());
1✔
146
         verifier.update(mes);
1✔
147
         result.test_is_true("Entire signature is valid", verifier.check_signature(valid_sig));
1✔
148
         for(size_t idx = 0; idx < valid_sig.size(); ++idx) {
785✔
149
            auto bad_sig = valid_sig;
784✔
150
            bad_sig.at(idx) ^= 0x80;
784✔
151
            result.test_no_throw(Botan::fmt("Verification does not throw (byte idx {})", idx), [&]() {
1,568✔
152
               verifier.update(mes);
784✔
153
               const bool valid = verifier.check_signature(bad_sig);
784✔
154
               result.test_is_true(Botan::fmt("Manipulated signature is invalid (byte idx {})", idx), !valid);
784✔
155
            });
784✔
156
         }
784✔
157

158
         return result;
1✔
159
      }
3✔
160

161
      Test::Result test_too_short_signature() {
1✔
162
         Test::Result result("HSS-LMS");
1✔
163

164
         auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
165

166
         Botan::PK_Signer signer(*sk, Test::rng(), "");
1✔
167
         Botan::PK_Verifier verifier(*sk, "");
1✔
168

169
         std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
170

171
         signer.update(mes);
1✔
172
         auto valid_sig = signer.signature(Test::rng());
1✔
173
         verifier.update(mes);
1✔
174
         result.test_is_true("Entire signature is valid", verifier.check_signature(valid_sig));
1✔
175
         for(size_t n = 0; n < valid_sig.size(); ++n) {
785✔
176
            result.test_no_throw("Verification does not throw", [&]() {
784✔
177
               verifier.update(mes);
784✔
178
               const bool valid = verifier.check_signature(valid_sig.data(), n);
784✔
179
               result.test_is_true("Too short signature is invalid", !valid);
784✔
180
            });
784✔
181
         }
182

183
         return result;
1✔
184
      }
3✔
185

186
      Test::Result test_too_short_private_key() {
1✔
187
         Test::Result result("HSS-LMS");
1✔
188

189
         // HSS_LMS_PublicKey::key_length()
190
         auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
191

192
         auto sk_bytes = sk->private_key_bits();
1✔
193
         result.test_no_throw("Entire private key valid", [&]() {
1✔
194
            const Botan::HSS_LMS_PrivateKey key(sk_bytes);
1✔
195
            BOTAN_UNUSED(key);
1✔
196
         });
1✔
197
         for(size_t n = 0; n < sk_bytes.size(); ++n) {
61✔
198
            result.test_throws<Botan::Decoding_Error>("Partial private key invalid", [&]() {
60✔
199
               const std::span<const uint8_t> partial_key = {sk_bytes.data(), n};
60✔
200
               const Botan::HSS_LMS_PrivateKey key(partial_key);
60✔
201
               BOTAN_UNUSED(key);
×
202
            });
×
203
         }
204
         return result;
1✔
205
      }
2✔
206

207
      Test::Result test_too_short_public_key() {
1✔
208
         Test::Result result("HSS-LMS");
1✔
209

210
         // HSS_LMS_PublicKey::key_length()
211
         auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
212

213
         auto sk_bytes = sk->public_key_bits();
1✔
214
         result.test_no_throw("Entire public key valid", [&]() {
1✔
215
            const Botan::HSS_LMS_PublicKey key(sk_bytes);
2✔
216
            BOTAN_UNUSED(key);
1✔
217
         });
1✔
218
         for(size_t n = 0; n < sk_bytes.size(); ++n) {
53✔
219
            result.test_throws<Botan::Decoding_Error>("Partial public key invalid", [&]() {
52✔
220
               const std::span<const uint8_t> partial_key = {sk_bytes.data(), n};
52✔
221
               const Botan::HSS_LMS_PublicKey key(partial_key);
52✔
222
               BOTAN_UNUSED(key);
×
223
            });
×
224
         }
225
         return result;
1✔
226
      }
2✔
227

228
      std::vector<Test::Result> run() final {
1✔
229
         return {test_flipped_signature_bits(),
1✔
230
                 test_too_short_signature(),
231
                 test_too_short_private_key(),
232
                 test_too_short_public_key()};
5✔
233
      }
1✔
234
};
235

236
/**
237
 * @brief Test the correct handling of the HSS-LMS private key's state.
238
 */
239
class HSS_LMS_Statefulness_Test final : public Test {
1✔
240
      Botan::HSS_LMS_PrivateKey create_private_key_with_idx(uint64_t idx) {
4✔
241
         auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
4✔
242
         auto bytes = sk.private_key_bits();
4✔
243
         // The index is store after the level (uint32_t)
244
         Botan::store_be(idx, bytes.data() + sizeof(uint32_t));
4✔
245
         return Botan::HSS_LMS_PrivateKey(Botan::AlgorithmIdentifier(), bytes);
8✔
246
      }
4✔
247

248
      Test::Result test_sig_changes_state() {
1✔
249
         Test::Result result("HSS-LMS");
1✔
250

251
         auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8),HW(5,8)");
1✔
252
         Botan::PK_Signer signer(sk, Test::rng(), "");
1✔
253
         std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
254
         auto sk_bytes_begin = sk.private_key_bits();
1✔
255

256
         // Tree heights: 5,5 => 2^(5+5) = 1024 signatures available
257
         const uint64_t expected_total = 1024;
1✔
258
         result.test_opt_u64_eq(
1✔
259
            "Fresh key starts with total number of remaining signatures.", sk.remaining_operations(), expected_total);
260

261
         // Creating a signature should update the private key's state
262
         auto sig_0 = signer.sign_message(mes, Test::rng());
1✔
263
         result.test_is_true(
1✔
264
            "First signature uses index 0.",
265
            Botan::HSS_Signature::from_bytes_or_throw(sig_0).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(0));
1✔
266

267
         auto sk_bytes_after_sig = sk.private_key_bits();
1✔
268

269
         result.test_opt_u64_eq(
1✔
270
            "Signature decreases number of remaining signatures.", sk.remaining_operations(), expected_total - 1);
1✔
271
         result.test_bin_ne("Signature updates private key.", sk_bytes_after_sig, sk_bytes_begin);
1✔
272

273
         auto sig_1 = signer.sign_message(mes, Test::rng());
1✔
274
         result.test_is_true(
1✔
275
            "Next signature uses the new index.",
276
            Botan::HSS_Signature::from_bytes_or_throw(sig_1).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(1));
1✔
277

278
         return result;
2✔
279
      }
5✔
280

281
      Test::Result test_max_sig_count() {
1✔
282
         Test::Result result("HSS-LMS");
1✔
283

284
         const uint64_t total_sig_count = 32;
1✔
285
         auto sk = create_private_key_with_idx(total_sig_count - 1);
1✔
286

287
         Botan::PK_Signer signer(sk, Test::rng(), "");
1✔
288
         std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
289
         auto sk_bytes_begin = sk.private_key_bits();
1✔
290

291
         result.test_opt_u64_eq("One remaining signature.", sk.remaining_operations(), 1);
1✔
292
         result.test_no_throw("Use last signature index.", [&]() { signer.sign_message(mes, Test::rng()); });
3✔
293
         result.test_opt_u64_eq("No remaining signatures.", sk.remaining_operations(), 0);
1✔
294
         result.test_throws("Cannot sign with exhausted key.", [&]() { signer.sign_message(mes, Test::rng()); });
2✔
295
         result.test_opt_u64_eq("Still zero remaining signatures.", sk.remaining_operations(), 0);
1✔
296

297
         return result;
2✔
298
      }
2✔
299

300
      Test::Result test_idx_bound_checked_on_load() {
1✔
301
         Test::Result result("HSS-LMS");
1✔
302

303
         // create_private_key_with_idx uses a single HW(5,8) layer, so the
304
         // maximum signature count is 32
305
         result.test_no_throw("Index == max_sig_count is accepted on load", [&]() {
1✔
306
            auto sk = create_private_key_with_idx(32);
1✔
307
            result.test_opt_u64_eq("Exhausted key loads with no remaining signatures", sk.remaining_operations(), 0);
1✔
308
            Botan::PK_Signer signer(sk, Test::rng(), "");
1✔
309
            const std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
310
            result.test_throws("Cannot sign with exhausted key", [&]() { signer.sign_message(mes, Test::rng()); });
2✔
311
         });
1✔
312

313
         result.test_throws<Botan::Decoding_Error>("Index > max_sig_count is rejected on load",
1✔
314
                                                   [&]() { create_private_key_with_idx(33); });
2✔
315

316
         result.test_throws<Botan::Decoding_Error>("Huge index is rejected on load", [&]() {
1✔
317
            create_private_key_with_idx(std::numeric_limits<uint64_t>::max());
1✔
318
         });
×
319

320
         return result;
1✔
321
      }
×
322

323
      Test::Result test_exhausted_key_stays_exhausted() {
1✔
324
         Test::Result result("HSS-LMS");
1✔
325

326
         // With a total tree height >= 64 the maximum signature count is
327
         // clamped to 2^64 - 1, so an index of 2^64 - 1 is accepted on load
328
         auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8),HW(25,8),HW(25,8),HW(25,8)");
1✔
329
         auto bytes = sk.private_key_bits();
1✔
330
         Botan::store_be(std::numeric_limits<uint64_t>::max(), bytes.data() + sizeof(uint32_t));
1✔
331

332
         auto exhausted_sk = Botan::HSS_LMS_PrivateKey(Botan::AlgorithmIdentifier(), bytes);
1✔
333
         result.test_opt_u64_eq("Exhausted key has no remaining signatures", exhausted_sk.remaining_operations(), 0);
1✔
334

335
         Botan::PK_Signer signer(exhausted_sk, Test::rng(), "");
1✔
336
         const std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
337

338
         // A failed signing attempt must not wrap the index back to zero
339
         result.test_throws("Cannot sign with exhausted key", [&]() { signer.sign_message(mes, Test::rng()); });
2✔
340
         result.test_opt_u64_eq("Failed signing does not reset the state", exhausted_sk.remaining_operations(), 0);
1✔
341
         result.test_throws("Exhausted key stays exhausted", [&]() { signer.sign_message(mes, Test::rng()); });
2✔
342

343
         return result;
2✔
344
      }
2✔
345

346
      Test::Result test_params_are_part_of_key_identity() {
1✔
347
         Test::Result result("HSS-LMS");
1✔
348

349
         const auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
350
         auto bytes = sk.private_key_bits();
1✔
351

352
         // Patch the LMOTS algorithm type from SHA256_N24_W8 (0x08) to
353
         // SHA256_N24_W4 (0x07), pretending the same seed and identifier
354
         // belong to a key with a different Winternitz parameter
355
         result.require("LMOTS type byte has expected value", bytes[19] == 0x08);
1✔
356
         bytes[19] = 0x07;
1✔
357

358
         // The index registry tracks the same key material under different
359
         // parameter sets independently. Nothing can prevent such (insecurely)
360
         // related keys from issuing overlapping one time signatures.
361
         const Botan::HSS_LMS_PrivateKey patched(Botan::AlgorithmIdentifier(), bytes);
1✔
362

363
         Botan::PK_Signer signer(sk, Test::rng(), "");
1✔
364
         const std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
365
         signer.sign_message(mes, Test::rng());
2✔
366

367
         result.test_opt_u64_eq("Original key consumed an index", sk.remaining_operations(), 31);
1✔
368
         result.test_opt_u64_eq(
1✔
369
            "Key with the same material but other params is unaffected", patched.remaining_operations(), 32);
1✔
370

371
         return result;
2✔
372
      }
2✔
373

374
      Test::Result test_separately_loaded_copies_share_state() {
1✔
375
         Test::Result result("HSS-LMS");
1✔
376

377
         const auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
1✔
378
         const auto sk_bytes = sk.private_key_bits();
1✔
379

380
         const Botan::HSS_LMS_PrivateKey copy1(Botan::AlgorithmIdentifier(), sk_bytes);
1✔
381
         const Botan::HSS_LMS_PrivateKey copy2(Botan::AlgorithmIdentifier(), sk_bytes);
1✔
382

383
         const std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
1✔
384

385
         Botan::PK_Signer signer1(copy1, Test::rng(), "");
1✔
386
         const auto sig_0 = signer1.sign_message(mes, Test::rng());
1✔
387

388
         result.test_opt_u64_eq("Signing with one copy is seen by the other", copy2.remaining_operations(), 31);
1✔
389

390
         Botan::PK_Signer signer2(copy2, Test::rng(), "");
1✔
391
         const auto sig_1 = signer2.sign_message(mes, Test::rng());
1✔
392

393
         result.test_is_true(
1✔
394
            "First signature uses index 0",
395
            Botan::HSS_Signature::from_bytes_or_throw(sig_0).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(0));
1✔
396
         result.test_is_true(
1✔
397
            "Second signature uses index 1",
398
            Botan::HSS_Signature::from_bytes_or_throw(sig_1).bottom_sig().q() == Botan::LMS_Tree_Node_Idx(1));
1✔
399

400
         return result;
2✔
401
      }
4✔
402

403
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
404
      Test::Result test_forked_key_cannot_sign() {
×
405
         Test::Result result("HSS-LMS fork safety");
×
406

407
         const Botan::HSS_LMS_PrivateKey sk(Test::rng(), "Truncated(SHA-256,192),HW(5,8)");
×
408
         const std::vector<uint8_t> mes = {0xde, 0xad, 0xbe, 0xef};
×
409
         constexpr uint8_t child_signed = 1;
×
410
         constexpr uint8_t child_refused = 2;
×
411
         constexpr uint8_t child_other_exception = 3;
×
412

413
         int fd[2];
×
414
         if(::pipe(fd) != 0) {
×
415
            result.test_failure("failed to create pipe");
×
416
            return result;
417
         }
418

419
         const pid_t pid = ::fork();
×
420
         if(pid == -1) {
×
421
            ::close(fd[0]);
×
422
            ::close(fd[1]);
×
423

424
      #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
425
            result.test_note("failed to fork process");
426
      #else
427
            result.test_failure("failed to fork process");
×
428
      #endif
429

430
            return result;
431
         } else if(pid == 0) {
×
432
            ::close(fd[0]);
×
433

434
            uint8_t child_status = child_signed;
×
435

436
            try {
×
437
               Botan::PK_Signer signer(sk, Test::rng(), "");
×
438
               signer.sign_message(mes, Test::rng());
×
439
            } catch(const Botan::Invalid_State&) {
×
440
               child_status = child_refused;
×
441
            } catch(const std::exception&) {
×
442
               child_status = child_other_exception;
×
443
            }
×
444

445
            [[maybe_unused]] const ssize_t written = ::write(fd[1], &child_status, sizeof(child_status));
×
446
            ::close(fd[1]);
×
447

448
            ::execl("/bin/true", "true", NULL);  // NOLINT(*-vararg)
×
449
            ::_exit(0);
×
450
         }
451

452
         ::close(fd[1]);
×
453

454
         uint8_t child_status = 0;
×
455
         const ssize_t got = ::read(fd[0], &child_status, sizeof(child_status));
×
456
         if(got > 0) {
×
457
            result.test_sz_eq("expected status byte from child", static_cast<size_t>(got), sizeof(child_status));
×
458
            result.test_u8_eq("forked child refused to emit an index", child_status, child_refused);
×
459
         } else {
460
            result.test_failure("failed to read child status");
×
461
         }
462
         ::close(fd[0]);
×
463

464
         int status = 0;
×
465
         if(::waitpid(pid, &status, 0) == pid) {
×
466
            result.test_is_true("child exited successfully", WIFEXITED(status) && WEXITSTATUS(status) == 0);
×
467
         } else {
468
            result.test_failure("failed to wait for child process");
×
469
         }
470

471
         Botan::PK_Signer signer(sk, Test::rng(), "");
×
472
         result.test_no_throw("parent can still sign", [&]() { signer.sign_message(mes, Test::rng()); });
×
473

474
         return result;
×
475
      }
×
476
   #endif
477

478
      std::vector<Test::Result> run() final {
1✔
479
         std::vector<Test::Result> results = {test_sig_changes_state(),
1✔
480
                                              test_max_sig_count(),
481
                                              test_idx_bound_checked_on_load(),
482
                                              test_exhausted_key_stays_exhausted(),
483
                                              test_params_are_part_of_key_identity(),
484
                                              test_separately_loaded_copies_share_state()};
7✔
485

486
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
487
         if(Test::options().test_threads() == 1) {
1✔
488
            results.push_back(test_forked_key_cannot_sign());
×
489
         }
490
   #endif
491

492
         return results;
1✔
493
      }
1✔
494
};
495

496
/**
497
 * @brief Test APIs not covered by other tests.
498
 */
499
class HSS_LMS_Missing_API_Test final : public Test {
1✔
500
      std::vector<Test::Result> run() final {
1✔
501
         Test::Result result("HSS-LMS");
1✔
502

503
         // HSS_LMS_PublicKey::key_length()
504
         auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "SHA-256,HW(10,4)");
1✔
505
         sk->key_length();
1✔
506
         result.test_sz_gt("Public key length must be greater than the simply type information plus I",
1✔
507
                           sk->key_length(),
1✔
508
                           3 * sizeof(uint32_t) + Botan::LMS_IDENTIFIER_LEN);
509

510
         // HSS_LMS_Verification_Operation::hash_function()
511
         const Botan::PK_Verifier verifier(*sk, "");
1✔
512
         result.test_str_eq("PK_Verifier should report the hash of the key", verifier.hash_function(), "SHA-256");
1✔
513

514
         // HSS_LMS_PrivateKey::raw_private_key_bits()
515
         result.test_bin_eq("Our BER and raw encoding is the same", sk->raw_private_key_bits(), sk->private_key_bits());
2✔
516

517
         // HSS_LMS_Signature_Operation::algorithm_identifier()
518
         const Botan::PK_Signer signer(*sk, Test::rng(), "");
1✔
519
         result.test_is_true("signature algorithm", signer.algorithm_identifier() == sk->algorithm_identifier());
1✔
520

521
         // HSS_LMS_Signature_Operation::hash_function()
522
         result.test_str_eq("PK_Signer should report the hash of the key", signer.hash_function(), "SHA-256");
1✔
523

524
         return {result};
3✔
525
      }
3✔
526
};
527

528
BOTAN_REGISTER_TEST_FN("pubkey", "hss_lms_params_parsing", test_hss_lms_params_parsing);
529
BOTAN_REGISTER_TEST("pubkey", "hss_lms_sign", HSS_LMS_Signature_Generation_Test);
530
BOTAN_REGISTER_TEST("pubkey", "hss_lms_verify", HSS_LMS_Signature_Verify_Tests);
531
BOTAN_REGISTER_TEST("pubkey", "hss_lms_verify_invalid", HSS_LMS_Signature_Verify_Invalid_Tests);
532
BOTAN_REGISTER_TEST("pubkey", "hss_lms_keygen", HSS_LMS_Key_Generation_Test);
533
BOTAN_REGISTER_TEST("pubkey", "hss_lms_negative", HSS_LMS_Negative_Tests);
534
BOTAN_REGISTER_TEST("pubkey", "hss_lms_state", HSS_LMS_Statefulness_Test);
535
BOTAN_REGISTER_TEST("pubkey", "hss_lms_api", HSS_LMS_Missing_API_Test);
536

537
}  // namespace
538

539
}  // namespace Botan_Tests
540

541
#endif  // BOTAN_HAS_HSS_LMS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc