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

randombit / botan / 29471089948

15 Jul 2026 11:28PM UTC coverage: 89.402% (-2.3%) from 91.656%
29471089948

push

github

web-flow
Merge pull request #5724 from randombit/jack/filter-fixes

Fix various bugs in the Pipe/Filter library

113560 of 127022 relevant lines covered (89.4%)

10935343.63 hits per line

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

92.86
/src/tests/test_sphincsplus.cpp
1
/*
2
* (C) 2023 Jack Lloyd
3
*     2023 Fabian Albert, René Meusel, Amos Treiber - 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_SPHINCS_PLUS_COMMON) && defined(BOTAN_HAS_AES)
11

12
   #include <botan/assert.h>
13
   #include <botan/exceptn.h>
14
   #include <botan/hash.h>
15
   #include <botan/pubkey.h>
16
   #include <botan/secmem.h>
17
   #include <botan/sp_parameters.h>
18
   #include <botan/sphincsplus.h>
19

20
   #if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
21
      #include <botan/certstor.h>
22
      #include <botan/x509cert.h>
23
      #include <botan/x509path.h>
24
   #endif
25

26
   #include "test_pubkey.h"
27
   #include "test_rng.h"
28

29
namespace Botan_Tests {
30

31
namespace {
32

33
/**
34
 * Test all implemented SLH-DSA instances using the data of the KAT files.
35
 */
36
class SPHINCS_Plus_Test_Base : public Text_Based_Test {
×
37
   public:
38
      explicit SPHINCS_Plus_Test_Base(std::string_view kat_path) :
2✔
39
            Text_Based_Test(std::string(kat_path), "SphincsParameterSet,seed,pk,sk,msg,HashSigRand", "HashSigDet") {}
6✔
40

41
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
24✔
42
         auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet"));
24✔
43

44
         if(!params.is_available()) {
24✔
45
            return true;
46
         }
47

48
   #if not defined(BOTAN_HAS_SHA3)
49
         // The SLH-DSA shake-based signatures are hashed with SHA-3 in the test file.
50
         if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) {
51
            return true;
52
         }
53
   #endif
54

55
         // Execute the small (slow) instances only with --run-long-tests
56
         switch(params.parameter_set()) {
24✔
57
            case Botan::Sphincs_Parameter_Set::Sphincs128Fast:
58
            case Botan::Sphincs_Parameter_Set::Sphincs192Fast:
59
            case Botan::Sphincs_Parameter_Set::Sphincs256Fast:
60
            case Botan::Sphincs_Parameter_Set::SLHDSA128Fast:
61
            case Botan::Sphincs_Parameter_Set::SLHDSA192Fast:
62
            case Botan::Sphincs_Parameter_Set::SLHDSA256Fast:
63
               return false;
64
            case Botan::Sphincs_Parameter_Set::Sphincs128Small:
12✔
65
            case Botan::Sphincs_Parameter_Set::Sphincs192Small:
12✔
66
            case Botan::Sphincs_Parameter_Set::Sphincs256Small:
12✔
67
            case Botan::Sphincs_Parameter_Set::SLHDSA128Small:
12✔
68
            case Botan::Sphincs_Parameter_Set::SLHDSA192Small:
12✔
69
            case Botan::Sphincs_Parameter_Set::SLHDSA256Small:
12✔
70
               return !Test::run_long_tests();
12✔
71
         }
72
         BOTAN_ASSERT_UNREACHABLE();
×
73
      }
74

75
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) final {
24✔
76
         auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet"));
24✔
77
         Test::Result result(params.is_slh_dsa() ? "SLH-DSA" : "SPHINCS+");
36✔
78

79
         const std::vector<uint8_t> seed_ref = vars.get_req_bin("seed");
24✔
80
         const std::vector<uint8_t> msg_ref = vars.get_req_bin("msg");
24✔
81
         const std::vector<uint8_t> pk_ref = vars.get_req_bin("pk");
24✔
82
         const std::vector<uint8_t> sk_ref = vars.get_req_bin("sk");
24✔
83
         const std::vector<uint8_t> sig_rand_hash = vars.get_req_bin("HashSigRand");
24✔
84
         const auto sig_det_hash = [&]() -> std::optional<std::vector<uint8_t>> {
×
85
            if(vars.has_key("HashSigDet")) {
24✔
86
               return vars.get_opt_bin("HashSigDet");
12✔
87
            } else {
88
               return std::nullopt;
12✔
89
            }
90
         }();
24✔
91

92
         // Depending on the SLH-DSA configuration the resulting signature is
93
         // hashed either with SHA-3 or SHA-256 to reduce the inner dependencies
94
         // on other hash function modules.
95
         auto hash_algo_spec = [&]() -> std::string {
×
96
            if(params.hash_type() == Botan::Sphincs_Hash_Type::Shake256) {
24✔
97
               return "SHA-3(256)";
12✔
98
            } else {
99
               return "SHA-256";
12✔
100
            }
101
         }();
24✔
102
         auto hash = Botan::HashFunction::create_or_throw(hash_algo_spec);
24✔
103

104
         /*
105
          * To get sk_seed || sk_prf || pk_seed and opt_rand from the given seed
106
          * (from KAT), we create a CTR_DRBG_AES256 rng and "simulate" the
107
          * invocation-pattern in the reference. We feed the created randomness
108
          * to the fixed output rng, to allow for our (slightly different)
109
          * invocation-pattern. Be careful, some KATs are generated by 3 separate
110
          * invocations of dbrg.random_vals which is different.
111
          */
112
         auto kat_rng = CTR_DRBG_AES256(seed_ref);
24✔
113
         Fixed_Output_RNG fixed_rng;
24✔
114
         fixed_rng.add_entropy(kat_rng.random_vec<std::vector<uint8_t>>(3 * params.n()));
24✔
115

116
         // push the entropy used for signing twice, as we want to perform two
117
         // signing operations
118
         const auto entropy_for_signing = kat_rng.random_vec<std::vector<uint8_t>>(1 * params.n());
24✔
119
         // Depending on the configuration, up to 2 signatures with 'Randomized' are created
120
         fixed_rng.add_entropy(entropy_for_signing);
24✔
121
         fixed_rng.add_entropy(entropy_for_signing);
24✔
122

123
         // Generate Keypair
124
         const Botan::SphincsPlus_PrivateKey priv_key(fixed_rng, params);
24✔
125

126
         result.test_bin_eq("public key bits", priv_key.public_key_bits(), pk_ref);
24✔
127
         result.test_bin_eq("private key bits", priv_key.private_key_bits(), sk_ref);
24✔
128

129
         // Signature roundtrip (Randomized mode)
130
         auto signer_rand = Botan::PK_Signer(priv_key, fixed_rng, "Randomized");
24✔
131
         auto signature_rand = signer_rand.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng);
24✔
132

133
         result.test_bin_eq("signature creation randomized", hash->process(signature_rand), sig_rand_hash);
24✔
134

135
         Botan::PK_Verifier verifier(*priv_key.public_key(), params.algorithm_identifier());
48✔
136
         const bool verify_success =
24✔
137
            verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
24✔
138
         result.test_is_true("verification of valid randomized signature", verify_success);
24✔
139

140
         // Signature roundtrip (Deterministic mode) - not available for all parameter sets
141
         // For testing time reasons we only test this for some tests if not --run-long-tests
142
         if(sig_det_hash.has_value() &&
24✔
143
            (run_long_tests() || params.parameter_set() == Botan::Sphincs_Parameter_Set::SLHDSA128Fast)) {
×
144
            auto signer_det = Botan::PK_Signer(priv_key, fixed_rng, "Deterministic");
12✔
145
            auto signature_det = signer_det.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng);
12✔
146

147
            result.test_bin_eq("signature creation deterministic", hash->process(signature_det), *sig_det_hash);
12✔
148

149
            auto verify_success_det =
12✔
150
               verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_det.data(), signature_det.size());
12✔
151
            result.test_is_true("verification of valid deterministic signature", verify_success_det);
12✔
152
         }
12✔
153

154
         // Verification with generated Keypair
155

156
         // Run additional parsing and re-verification tests on one parameter
157
         // set only to save test runtime. Given the current test vector we will
158
         // run this exactly once per hash function.
159
         if(params.parameter_set() == Botan::Sphincs_Parameter_Set::Sphincs128Fast ||
24✔
160
            params.parameter_set() == Botan::Sphincs_Parameter_Set::SLHDSA128Fast) {
22✔
161
            // Deserialization of Keypair from test vector
162
            const Botan::SphincsPlus_PrivateKey deserialized_priv_key(sk_ref, params);
4✔
163
            const Botan::SphincsPlus_PublicKey deserialized_pub_key(pk_ref, params);
4✔
164

165
            // Signature with deserialized Keypair
166
            auto deserialized_signer = Botan::PK_Signer(deserialized_priv_key, fixed_rng, "Randomized");
4✔
167
            auto deserialized_signature = deserialized_signer.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng);
4✔
168

169
            result.test_bin_eq(
4✔
170
               "signature creation after deserialization", hash->process(deserialized_signature), sig_rand_hash);
4✔
171

172
            // Verification with deserialized Keypair
173
            Botan::PK_Verifier deserialized_verifier(deserialized_pub_key, params.algorithm_identifier());
4✔
174
            const bool verify_success_deserialized = deserialized_verifier.verify_message(
4✔
175
               msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
4✔
176
            result.test_is_true("verification of valid signature after deserialization", verify_success_deserialized);
4✔
177

178
            // Verification of invalid signature
179
            auto broken_sig = Test::mutate_vec(deserialized_signature, this->rng());
4✔
180
            const bool verify_fail = deserialized_verifier.verify_message(
4✔
181
               msg_ref.data(), msg_ref.size(), broken_sig.data(), broken_sig.size());
4✔
182
            result.test_is_true("verification of invalid signature", !verify_fail);
4✔
183

184
            const bool verify_success_after_fail = deserialized_verifier.verify_message(
4✔
185
               msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
4✔
186
            result.test_is_true("verification of valid signature after broken signature", verify_success_after_fail);
4✔
187
         }
8✔
188

189
         // Misc
190
         result.test_is_true("parameter serialization works",
48✔
191
                             params.to_string() == vars.get_req_str("SphincsParameterSet"));
48✔
192

193
         return result;
48✔
194
      }
240✔
195
};
196

197
class SPHINCS_Plus_Test final : public SPHINCS_Plus_Test_Base {
×
198
   public:
199
      SPHINCS_Plus_Test() : SPHINCS_Plus_Test_Base("pubkey/sphincsplus.vec") {}
1✔
200
};
201

202
class SLH_DSA_Test final : public SPHINCS_Plus_Test_Base {
×
203
   public:
204
      SLH_DSA_Test() : SPHINCS_Plus_Test_Base("pubkey/slh_dsa.vec") {}
1✔
205
};
206

207
class SPHINCS_Plus_Keygen_Tests final : public PK_Key_Generation_Test {
1✔
208
   public:
209
      std::vector<std::string> keygen_params() const override {
1✔
210
         const std::vector<std::string> short_test_params = {
1✔
211
            "SphincsPlus-shake-128s-r3.1",
212
            "SLH-DSA-SHAKE-128s",
213
            "SphincsPlus-sha2-128f-r3.1",
214
            "SLH-DSA-SHA2-128f",
215
         };
1✔
216
         const std::vector<std::string> all_params = {
1✔
217
            "SphincsPlus-shake-128s-r3.1", "SphincsPlus-shake-128f-r3.1", "SphincsPlus-shake-192s-r3.1",
218
            "SphincsPlus-shake-192f-r3.1", "SphincsPlus-shake-256s-r3.1", "SphincsPlus-shake-256f-r3.1",
219
            "SLH-DSA-SHAKE-128s",          "SLH-DSA-SHAKE-128f",          "SLH-DSA-SHAKE-192s",
220
            "SLH-DSA-SHAKE-192f",          "SLH-DSA-SHAKE-256s",          "SLH-DSA-SHAKE-256f",
221
            "SphincsPlus-sha2-128s-r3.1",  "SphincsPlus-sha2-128f-r3.1",  "SphincsPlus-sha2-192s-r3.1",
222
            "SphincsPlus-sha2-192f-r3.1",  "SphincsPlus-sha2-256s-r3.1",  "SphincsPlus-sha2-256f-r3.1",
223
            "SLH-DSA-SHA2-128s",           "SLH-DSA-SHA2-128f",           "SLH-DSA-SHA2-192s",
224
            "SLH-DSA-SHA2-192f",           "SLH-DSA-SHA2-256s",           "SLH-DSA-SHA2-256f",
225
         };
1✔
226
         const auto& tested_params = Test::run_long_tests() ? all_params : short_test_params;
1✔
227
         std::vector<std::string> available_params;
1✔
228
         for(const auto& param : tested_params) {
25✔
229
            if(Botan::Sphincs_Parameters::create(param).is_available()) {
24✔
230
               available_params.push_back(param);
24✔
231
            }
232
         }
233
         return available_params;
1✔
234
      }
1✔
235

236
      std::string algo_name() const override { return "SLH-DSA"; }
24✔
237

238
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view keygen_params,
24✔
239
                                                             std::string_view /* provider */,
240
                                                             std::span<const uint8_t> raw_pk) const override {
241
         return std::make_unique<Botan::SphincsPlus_PublicKey>(raw_pk,
24✔
242
                                                               Botan::Sphincs_Parameters::create(keygen_params));
24✔
243
      }
244
};
245

246
class Generic_SlhDsa_Signature_Tests final : public PK_Signature_Generation_Test {
247
   public:
248
      Generic_SlhDsa_Signature_Tests() :
1✔
249
            PK_Signature_Generation_Test(
250
               "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature", "Nonce") {}
2✔
251

252
      bool clear_between_callbacks() const override { return false; }
2✔
253

254
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
2✔
255
         const std::string instance = vars.get_req_str("Instance");
2✔
256
         const std::vector<uint8_t> privkey = vars.get_req_bin("PrivateKey");
2✔
257
         const std::vector<uint8_t> pubkey = vars.get_req_bin("PublicKey");
2✔
258
         auto sk =
2✔
259
            std::make_unique<Botan::SphincsPlus_PrivateKey>(privkey, Botan::Sphincs_Parameters::create(instance));
2✔
260

261
         if(sk->public_key_bits() != pubkey) {
4✔
262
            throw Test_Error("Invalid SLH-DSA key in test data");
×
263
         }
264

265
         return sk;
2✔
266
      }
6✔
267

268
      std::string default_padding(const VarMap& vars) const override {
2✔
269
         return vars.has_key("Nonce") ? "Randomized" : "Deterministic";
3✔
270
      }
271

272
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
2✔
273
         return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available();
2✔
274
      }
275
};
276

277
class Generic_SlhDsa_Verification_Tests final : public PK_Signature_Verification_Test {
278
   public:
279
      Generic_SlhDsa_Verification_Tests() :
1✔
280
            PK_Signature_Verification_Test(
281
               "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature", "Nonce") {}
2✔
282

283
      bool clear_between_callbacks() const override { return false; }
2✔
284

285
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
2✔
286
         const std::string instance = vars.get_req_str("Instance");
2✔
287
         const std::vector<uint8_t> pubkey = vars.get_req_bin("PublicKey");
2✔
288

289
         return std::make_unique<Botan::SphincsPlus_PublicKey>(pubkey, Botan::Sphincs_Parameters::create(instance));
4✔
290
      }
2✔
291

292
      std::string default_padding(const VarMap& /*vars*/) const override { return ""; }
2✔
293

294
      bool skip_this_test(const std::string& /*header*/, const VarMap& vars) override {
2✔
295
         return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available();
2✔
296
      }
297
};
298

299
   #if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
300
class SLH_DSA_X509_Tests : public Test {
×
301
   public:
302
      SLH_DSA_X509_Tests() = default;
1✔
303

304
      std::vector<Test::Result> run() override {
1✔
305
         return {test_cert_verifies(), test_null_params_rejected(), test_shake_path_validation()};
4✔
306
      }
1✔
307

308
   private:
309
      static Test::Result test_cert_verifies() {
1✔
310
         Test::Result result("SLH-DSA-X.509");
1✔
311
         const Botan::X509_Certificate cert(Test::data_file("x509/slh-dsa/slh-dsa-rfc-9909-cert.pem"));
2✔
312
         auto ver_res = cert.verify_signature(*cert.subject_public_key());
1✔
313
         result.test_is_true("signature of certificate verifies", ver_res.first == Botan::Certificate_Status_Code::OK);
1✔
314
         return result;
2✔
315
      }
1✔
316

317
      static Test::Result test_null_params_rejected() {
1✔
318
         Test::Result result("SLH-DSA-X.509 NULL parameters");
1✔
319
         const Botan::X509_Certificate cert(Test::data_file("x509/slh-dsa/slh-dsa-rfc-9909-cert.pem"));
2✔
320
         auto pub_key = cert.subject_public_key();
1✔
321
         const Botan::AlgorithmIdentifier with_null(pub_key->object_identifier(),
2✔
322
                                                    Botan::AlgorithmIdentifier::USE_NULL_PARAM);
1✔
323
         result.test_throws<Botan::Decoding_Error>("NULL signature algorithm parameters are rejected",
1✔
324
                                                   [&]() { const Botan::PK_Verifier v(*pub_key, with_null); });
2✔
325
         return result;
1✔
326
      }
2✔
327

328
      static Test::Result test_shake_path_validation() {
1✔
329
         Test::Result result("SLH-DSA-X.509 SHAKE path validation");
1✔
330

331
         if(!Botan::Sphincs_Parameters::create("SLH-DSA-SHAKE-128f").is_available()) {
1✔
332
            result.test_note("Skipping due to missing SLH-DSA-SHAKE module");
×
333
            return result;
×
334
         }
335

336
         const Botan::X509_Certificate ee(Test::data_file("x509/slh-dsa/slh-dsa-leaf.pem"));
2✔
337
         Botan::Certificate_Store_In_Memory store;
1✔
338
         store.add_certificate(Botan::X509_Certificate(Test::data_file("x509/slh-dsa/slh-dsa-root.pem")));
1✔
339

340
         const Botan::Path_Validation_Restrictions restrictions;
1✔
341
         const auto path_result = Botan::x509_path_validate(ee, restrictions, store);
1✔
342
         if(!result.test_is_true("SLH-DSA-SHAKE chain validates", path_result.successful_validation())) {
1✔
343
            result.test_note(path_result.result_string());
×
344
         }
345
         return result;
1✔
346
      }
1✔
347
};
348
   #endif
349

350
BOTAN_REGISTER_TEST("pubkey", "sphincsplus", SPHINCS_Plus_Test);
351
BOTAN_REGISTER_TEST("pubkey", "slh_dsa", SLH_DSA_Test);
352
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_keygen", SPHINCS_Plus_Keygen_Tests);
353
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_sign_generic", Generic_SlhDsa_Signature_Tests);
354
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_verify_generic", Generic_SlhDsa_Verification_Tests);
355

356
   #if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
357
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_x509", SLH_DSA_X509_Tests);
358
   #endif
359

360
}  // namespace
361

362
}  // namespace Botan_Tests
363

364
#endif  // BOTAN_HAS_SPHINCS_PLUS
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