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

randombit / botan / 13215274653

08 Feb 2025 11:38AM UTC coverage: 91.655% (-0.009%) from 91.664%
13215274653

Pull #4650

github

web-flow
Merge 107f31833 into bc555cd3c
Pull Request #4650: Reorganize code and reduce header dependencies

94836 of 103471 relevant lines covered (91.65%)

11230958.94 hits per line

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

93.08
/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 "test_rng.h"
9
#include "tests.h"
10

11
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON) && defined(BOTAN_HAS_AES)
12

13
   #include <botan/assert.h>
14
   #include <botan/hash.h>
15
   #include <botan/pk_algs.h>
16
   #include <botan/pubkey.h>
17
   #include <botan/secmem.h>
18
   #include <botan/sp_parameters.h>
19
   #include <botan/sphincsplus.h>
20
   #include <botan/internal/loadstor.h>
21
   #include <botan/internal/sp_hash.h>
22
   #include <algorithm>
23

24
   #include "test_pubkey.h"
25

26
namespace Botan_Tests {
27

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

36
      bool skip_this_test(const std::string&, const VarMap& vars) override {
24✔
37
         auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet"));
48✔
38

39
         if(!params.is_available()) {
24✔
40
            return true;
41
         }
42

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

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

70
      Test::Result run_one_test(const std::string&, const VarMap& vars) final {
24✔
71
         auto params = Botan::Sphincs_Parameters::create(vars.get_req_str("SphincsParameterSet"));
48✔
72
         Test::Result result(params.is_slh_dsa() ? "SLH-DSA" : "SPHINCS+");
36✔
73

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

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

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

111
         // push the entropy used for signing twice, as we want to perform two
112
         // signing operations
113
         const auto entropy_for_signing = kat_rng.random_vec<std::vector<uint8_t>>(1 * params.n());
24✔
114
         // Depending on the configuation, upto 2 signatures with 'Randomized' are created
115
         fixed_rng.add_entropy(entropy_for_signing);
24✔
116
         fixed_rng.add_entropy(entropy_for_signing);
24✔
117

118
         // Generate Keypair
119
         Botan::SphincsPlus_PrivateKey priv_key(fixed_rng, params);
24✔
120

121
         result.test_is_eq("public key bits", priv_key.public_key_bits(), pk_ref);
48✔
122
         result.test_is_eq("private key bits", unlock(priv_key.private_key_bits()), sk_ref);
96✔
123

124
         // Signature roundtrip (Randomized mode)
125
         auto signer_rand = Botan::PK_Signer(priv_key, fixed_rng, "Randomized");
24✔
126
         auto signature_rand = signer_rand.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng);
24✔
127

128
         result.test_is_eq("signature creation randomized", unlock(hash->process(signature_rand)), sig_rand_hash);
96✔
129

130
         Botan::PK_Verifier verifier(*priv_key.public_key(), params.algorithm_identifier());
48✔
131
         bool verify_success =
24✔
132
            verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
24✔
133
         result.confirm("verification of valid randomized signature", verify_success);
48✔
134

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

142
            result.test_is_eq("signature creation deterministic", unlock(hash->process(signature_det)), *sig_det_hash);
48✔
143

144
            auto verify_success_det =
12✔
145
               verifier.verify_message(msg_ref.data(), msg_ref.size(), signature_det.data(), signature_det.size());
12✔
146
            result.confirm("verification of valid deterministic signature", verify_success_det);
24✔
147
         }
12✔
148

149
         // Verification with generated Keypair
150

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

160
            // Signature with deserialized Keypair
161
            auto deserialized_signer = Botan::PK_Signer(deserialized_priv_key, fixed_rng, "Randomized");
4✔
162
            auto deserialized_signature = deserialized_signer.sign_message(msg_ref.data(), msg_ref.size(), fixed_rng);
4✔
163

164
            result.test_is_eq("signature creation after deserialization",
12✔
165
                              unlock(hash->process(deserialized_signature)),
12✔
166
                              sig_rand_hash);
167

168
            // Verification with deserialized Keypair
169
            Botan::PK_Verifier deserialized_verifier(deserialized_pub_key, params.algorithm_identifier());
4✔
170
            bool verify_success_deserialized = deserialized_verifier.verify_message(
4✔
171
               msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
4✔
172
            result.confirm("verification of valid signature after deserialization", verify_success_deserialized);
8✔
173

174
            // Verification of invalid signature
175
            auto broken_sig = Test::mutate_vec(deserialized_signature, this->rng());
4✔
176
            bool verify_fail = deserialized_verifier.verify_message(
4✔
177
               msg_ref.data(), msg_ref.size(), broken_sig.data(), broken_sig.size());
4✔
178
            result.confirm("verification of invalid signature", !verify_fail);
8✔
179

180
            bool verify_success_after_fail = deserialized_verifier.verify_message(
4✔
181
               msg_ref.data(), msg_ref.size(), signature_rand.data(), signature_rand.size());
4✔
182
            result.confirm("verification of valid signature after broken signature", verify_success_after_fail);
8✔
183
         }
8✔
184

185
         // Misc
186
         result.confirm("parameter serialization works", params.to_string() == vars.get_req_str("SphincsParameterSet"));
48✔
187

188
         return result;
48✔
189
      }
240✔
190
};
191

192
class SPHINCS_Plus_Test final : public SPHINCS_Plus_Test_Base {
×
193
   public:
194
      SPHINCS_Plus_Test() : SPHINCS_Plus_Test_Base("pubkey/sphincsplus.vec") {}
1✔
195
};
196

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

202
class SPHINCS_Plus_Keygen_Tests final : public PK_Key_Generation_Test {
×
203
   public:
204
      std::vector<std::string> keygen_params() const override {
1✔
205
         const std::vector<std::string> short_test_params = {
1✔
206
            "SphincsPlus-shake-128s-r3.1",
207
            "SLH-DSA-SHAKE-128s",
208
            "SphincsPlus-sha2-128f-r3.1",
209
            "SLH-DSA-SHA2-128f",
210
         };
1✔
211
         const std::vector<std::string> all_params = {
1✔
212
            "SphincsPlus-shake-128s-r3.1", "SphincsPlus-shake-128f-r3.1", "SphincsPlus-shake-192s-r3.1",
213
            "SphincsPlus-shake-192f-r3.1", "SphincsPlus-shake-256s-r3.1", "SphincsPlus-shake-256f-r3.1",
214
            "SLH-DSA-SHAKE-128s",          "SLH-DSA-SHAKE-128f",          "SLH-DSA-SHAKE-192s",
215
            "SLH-DSA-SHAKE-192f",          "SLH-DSA-SHAKE-256s",          "SLH-DSA-SHAKE-256f",
216
            "SphincsPlus-sha2-128s-r3.1",  "SphincsPlus-sha2-128f-r3.1",  "SphincsPlus-sha2-192s-r3.1",
217
            "SphincsPlus-sha2-192f-r3.1",  "SphincsPlus-sha2-256s-r3.1",  "SphincsPlus-sha2-256f-r3.1",
218
            "SLH-DSA-SHA2-128s",           "SLH-DSA-SHA2-128f",           "SLH-DSA-SHA2-192s",
219
            "SLH-DSA-SHA2-192f",           "SLH-DSA-SHA2-256s",           "SLH-DSA-SHA2-256f",
220
         };
1✔
221
         const auto& tested_params = Test::run_long_tests() ? all_params : short_test_params;
1✔
222
         std::vector<std::string> available_params;
1✔
223
         std::copy_if(tested_params.begin(),
1✔
224
                      tested_params.end(),
225
                      std::back_inserter(available_params),
226
                      [](const std::string& param) { return Botan::Sphincs_Parameters::create(param).is_available(); });
24✔
227
         return available_params;
1✔
228
      }
1✔
229

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

232
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view keygen_params,
24✔
233
                                                             std::string_view /* provider */,
234
                                                             std::span<const uint8_t> raw_pk) const override {
235
         return std::make_unique<Botan::SphincsPlus_PublicKey>(raw_pk,
24✔
236
                                                               Botan::Sphincs_Parameters::create(keygen_params));
24✔
237
      }
238
};
239

240
class Generic_SlhDsa_Signature_Tests final : public PK_Signature_Generation_Test {
241
   public:
242
      Generic_SlhDsa_Signature_Tests() :
1✔
243
            PK_Signature_Generation_Test(
244
               "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature", "Nonce") {}
2✔
245

246
      bool clear_between_callbacks() const override { return false; }
2✔
247

248
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
2✔
249
         const std::string instance = vars.get_req_str("Instance");
2✔
250
         const std::vector<uint8_t> privkey = vars.get_req_bin("PrivateKey");
2✔
251
         const std::vector<uint8_t> pubkey = vars.get_req_bin("PublicKey");
2✔
252
         auto sk =
2✔
253
            std::make_unique<Botan::SphincsPlus_PrivateKey>(privkey, Botan::Sphincs_Parameters::create(instance));
2✔
254

255
         if(sk->public_key_bits() != pubkey) {
4✔
256
            throw Test_Error("Invalid SLH-DSA key in test data");
×
257
         }
258

259
         return sk;
2✔
260
      }
6✔
261

262
      std::string default_padding(const VarMap& vars) const override {
2✔
263
         return vars.has_key("Nonce") ? "Randomized" : "Deterministic";
6✔
264
      }
265

266
      bool skip_this_test(const std::string&, const VarMap& vars) override {
2✔
267
         return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available();
2✔
268
      }
269
};
270

271
class Generic_SlhDsa_Verification_Tests final : public PK_Signature_Verification_Test {
272
   public:
273
      Generic_SlhDsa_Verification_Tests() :
1✔
274
            PK_Signature_Verification_Test(
275
               "SLH-DSA", "pubkey/slh_dsa_generic.vec", "Instance,Msg,PrivateKey,PublicKey,Valid,Signature", "Nonce") {}
2✔
276

277
      bool clear_between_callbacks() const override { return false; }
2✔
278

279
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
2✔
280
         const std::string instance = vars.get_req_str("Instance");
2✔
281
         const std::vector<uint8_t> pubkey = vars.get_req_bin("PublicKey");
2✔
282

283
         return std::make_unique<Botan::SphincsPlus_PublicKey>(pubkey, Botan::Sphincs_Parameters::create(instance));
4✔
284
      }
2✔
285

286
      std::string default_padding(const VarMap&) const override { return ""; }
2✔
287

288
      bool skip_this_test(const std::string&, const VarMap& vars) override {
2✔
289
         return !Botan::Sphincs_Parameters::create(vars.get_req_str("Instance")).is_available();
2✔
290
      }
291
};
292

293
BOTAN_REGISTER_TEST("pubkey", "sphincsplus", SPHINCS_Plus_Test);
294
BOTAN_REGISTER_TEST("pubkey", "slh_dsa", SLH_DSA_Test);
295
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_keygen", SPHINCS_Plus_Keygen_Tests);
296
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_sign_generic", Generic_SlhDsa_Signature_Tests);
297
BOTAN_REGISTER_TEST("pubkey", "slh_dsa_verify_generic", Generic_SlhDsa_Verification_Tests);
298

299
}  // namespace Botan_Tests
300

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

© 2025 Coveralls, Inc