Botan 3.5.0
Crypto and TLS for C&
sphincsplus.cpp
Go to the documentation of this file.
1/*
2* Sphincs+
3* (C) 2023 Jack Lloyd
4* 2023 Fabian Albert, René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7**/
8
9#include <botan/sphincsplus.h>
10
11#include <botan/rng.h>
12#include <botan/internal/pk_ops_impl.h>
13#include <botan/internal/sp_fors.h>
14#include <botan/internal/sp_hash.h>
15#include <botan/internal/sp_hypertree.h>
16#include <botan/internal/sp_treehash.h>
17#include <botan/internal/sp_types.h>
18#include <botan/internal/sp_wots.h>
19#include <botan/internal/sp_xmss.h>
20#include <botan/internal/stl_util.h>
21
22#if !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) and !defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
23static_assert(
24 false,
25 "botan module 'sphincsplus_common' is useful only when enabling at least 'sphincsplus_sha2' or 'sphincsplus_shake'");
26#endif
27
28namespace Botan {
29
30class SphincsPlus_PublicKeyInternal final {
31 public:
32 SphincsPlus_PublicKeyInternal(Sphincs_Parameters params,
33 SphincsPublicSeed public_seed,
34 SphincsTreeNode sphincs_root) :
35 m_params(params), m_public_seed(std::move(public_seed)), m_sphincs_root(std::move(sphincs_root)) {}
36
37 SphincsPlus_PublicKeyInternal(Sphincs_Parameters params, std::span<const uint8_t> key_bits) : m_params(params) {
38 if(key_bits.size() != m_params.public_key_bytes()) {
39 throw Decoding_Error("Sphincs Public Key doesn't have the expected length");
40 }
41
42 BufferSlicer s(key_bits);
43 m_public_seed = s.copy<SphincsPublicSeed>(params.n());
44 m_sphincs_root = s.copy<SphincsTreeNode>(params.n());
45
46 BOTAN_ASSERT_NOMSG(s.empty());
47 }
48
49 std::vector<uint8_t> key_bits() const { return concat<std::vector<uint8_t>>(m_public_seed, m_sphincs_root); }
50
51 const SphincsPublicSeed& seed() const { return m_public_seed; }
52
53 const SphincsTreeNode& root() const { return m_sphincs_root; }
54
55 const Sphincs_Parameters& parameters() const { return m_params; }
56
57 private:
58 Sphincs_Parameters m_params;
59 SphincsPublicSeed m_public_seed;
60 SphincsTreeNode m_sphincs_root;
61};
62
63class SphincsPlus_PrivateKeyInternal final {
64 public:
65 SphincsPlus_PrivateKeyInternal(SphincsSecretSeed secret_seed, SphincsSecretPRF prf) :
66 m_secret_seed(std::move(secret_seed)), m_prf(std::move(prf)) {}
67
68 SphincsPlus_PrivateKeyInternal(const Sphincs_Parameters& params, std::span<const uint8_t> key_bits) {
69 if(key_bits.size() != params.private_key_bytes() - params.public_key_bytes()) {
70 throw Decoding_Error("Sphincs Private Key doesn't have the expected length");
71 }
72
73 BufferSlicer s(key_bits);
74 m_secret_seed = s.copy<SphincsSecretSeed>(params.n());
75 m_prf = s.copy<SphincsSecretPRF>(params.n());
76
77 BOTAN_ASSERT_NOMSG(s.empty());
78 }
79
80 const SphincsSecretSeed& seed() const { return m_secret_seed; }
81
82 const SphincsSecretPRF& prf() const { return m_prf; }
83
84 secure_vector<uint8_t> key_bits() const { return concat<secure_vector<uint8_t>>(m_secret_seed, m_prf); }
85
86 private:
87 SphincsSecretSeed m_secret_seed;
88 SphincsSecretPRF m_prf;
89};
90
91SphincsPlus_PublicKey::SphincsPlus_PublicKey(std::span<const uint8_t> pub_key,
93 Sphincs_Hash_Type hash) :
94 m_public(std::make_shared<SphincsPlus_PublicKeyInternal>(Sphincs_Parameters::create(type, hash), pub_key)) {}
95
96SphincsPlus_PublicKey::SphincsPlus_PublicKey(std::span<const uint8_t> pub_key, Sphincs_Parameters params) :
97 m_public(std::make_shared<SphincsPlus_PublicKeyInternal>(params, pub_key)) {}
98
99SphincsPlus_PublicKey::SphincsPlus_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
100 m_public(std::make_shared<SphincsPlus_PublicKeyInternal>(Sphincs_Parameters::create(alg_id.oid()), key_bits)) {}
101
103
105 return m_public->parameters().n() * 8;
106}
107
109 return m_public->parameters().bitsec();
110}
111
113 return m_public->parameters().algorithm_identifier();
114}
115
117 return m_public->parameters().object_identifier();
118}
119
121 // Nothing to check. It's literally just hashes. :-)
122 return true;
123}
124
125std::vector<uint8_t> SphincsPlus_PublicKey::raw_public_key_bits() const {
126 return m_public->key_bits();
127}
128
129std::vector<uint8_t> SphincsPlus_PublicKey::public_key_bits() const {
130 // Currently, there isn't a finalized definition of an ASN.1 structure for
131 // SPHINCS+ aka SLH-DSA public keys. Therefore, we return the raw public key bits.
132 return raw_public_key_bits();
133}
134
135std::unique_ptr<Private_Key> SphincsPlus_PublicKey::generate_another(RandomNumberGenerator& rng) const {
136 return std::make_unique<SphincsPlus_PrivateKey>(rng, m_public->parameters());
137}
138
139class SphincsPlus_Verification_Operation final : public PK_Ops::Verification {
140 public:
141 SphincsPlus_Verification_Operation(std::shared_ptr<SphincsPlus_PublicKeyInternal> pub_key) :
142 m_public(std::move(pub_key)),
143 m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())) {}
144
145 /**
146 * Add more data to the message currently being signed
147 * @param msg the message
148 * @param msg_len the length of msg in bytes
149 */
150 void update(const uint8_t msg[], size_t msg_len) override {
151 m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
152 }
153
154 /*
155 * Perform a verification operation
156 * @param rng a random number generator
157 */
158 bool is_valid_signature(const uint8_t* sig, size_t sig_len) override {
159 const auto& p = m_public->parameters();
160 if(sig_len != p.sphincs_signature_bytes()) {
161 m_msg_buffer.clear();
162 return false;
163 }
164
165 BufferSlicer s({sig, sig_len});
166 // Compute leaf and tree index from R
167 const auto msg_random_s = s.take<SphincsMessageRandomness>(p.n());
168 auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), m_msg_buffer);
169 // Clear the message buffer, the data is not needed anymore
170 m_msg_buffer.clear();
171
172 // Reconstruct the FORS tree
173 Sphincs_Address fors_addr(Sphincs_Address_Type::ForsTree);
174 fors_addr.set_tree(tree_idx).set_keypair(leaf_idx);
175 const auto fors_sig_s = s.take<ForsSignature>(p.fors_signature_bytes());
176 auto fors_root = fors_public_key_from_signature(mhash, fors_sig_s, fors_addr, p, *m_hashes);
177
178 // Verify the hypertree signature
179 const auto ht_sig_s = s.take<SphincsHypertreeSignature>(p.ht_signature_bytes());
180 BOTAN_ASSERT_NOMSG(s.empty());
181 return ht_verify(fors_root, ht_sig_s, m_public->root(), tree_idx, leaf_idx, p, *m_hashes);
182 }
183
184 std::string hash_function() const override { return m_hashes->msg_hash_function_name(); }
185
186 private:
187 std::shared_ptr<SphincsPlus_PublicKeyInternal> m_public;
188 std::unique_ptr<Sphincs_Hash_Functions> m_hashes;
189 std::vector<uint8_t> m_msg_buffer;
190};
191
192std::unique_ptr<PK_Ops::Verification> SphincsPlus_PublicKey::create_verification_op(std::string_view /*params*/,
193 std::string_view provider) const {
194 if(provider.empty() || provider == "base") {
195 return std::make_unique<SphincsPlus_Verification_Operation>(m_public);
196 }
197 throw Provider_Not_Found(algo_name(), provider);
198}
199
200std::unique_ptr<PK_Ops::Verification> SphincsPlus_PublicKey::create_x509_verification_op(
201 const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
202 if(provider.empty() || provider == "base") {
203 if(signature_algorithm != this->algorithm_identifier()) {
204 throw Decoding_Error("Unexpected AlgorithmIdentifier for SPHINCS+ signature");
205 }
206 return std::make_unique<SphincsPlus_Verification_Operation>(m_public);
207 }
208 throw Provider_Not_Found(algo_name(), provider);
209}
210
214
215namespace {
216
217std::span<const uint8_t> slice_off_public_key(const OID& oid, std::span<const uint8_t> key_bits) {
218 const auto params = Sphincs_Parameters::create(oid);
219 // Note: We need to transiently instantiate the `Sphincs_Parameters` object
220 // to know the size of the public/private key. That's slightly
221 // inefficient but was the best we could do. Once we get rid of the
222 // PublicKey-PrivateKey inheritance, we might want to reconsider this
223 // control flow.
224 if(key_bits.size() != params.private_key_bytes()) {
225 throw Decoding_Error("Sphincs Private Key doesn't have the expected length");
226 }
227
228 return key_bits.subspan(params.private_key_bytes() - params.public_key_bytes());
229}
230
231} // namespace
232
233SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(std::span<const uint8_t> private_key,
235 Sphincs_Hash_Type hash) :
236 SphincsPlus_PrivateKey(private_key, Sphincs_Parameters::create(type, hash)) {}
237
238SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
239 SphincsPlus_PrivateKey(key_bits, Sphincs_Parameters::create(alg_id.oid())) {}
240
241SphincsPlus_PrivateKey::SphincsPlus_PrivateKey(std::span<const uint8_t> private_key, Sphincs_Parameters params) :
242 SphincsPlus_PublicKey(slice_off_public_key(params.object_identifier(), private_key), params) {
243 const auto private_portion_bytes = params.private_key_bytes() - params.public_key_bytes();
244 BOTAN_ASSERT_NOMSG(private_key.size() >= private_portion_bytes);
245
246 m_private = std::make_shared<SphincsPlus_PrivateKeyInternal>(params, private_key.first(private_portion_bytes));
247}
248
253
255 auto sk_seed = rng.random_vec<SphincsSecretSeed>(params.n());
256 auto sk_prf = rng.random_vec<SphincsSecretPRF>(params.n());
257
258 m_private = std::make_shared<SphincsPlus_PrivateKeyInternal>(std::move(sk_seed), std::move(sk_prf));
259
260 auto pub_seed = rng.random_vec<SphincsPublicSeed>(params.n());
261 auto hashes = Sphincs_Hash_Functions::create(params, pub_seed);
262 auto root = xmss_gen_root(params, m_private->seed(), *hashes);
263
264 m_public = std::make_shared<SphincsPlus_PublicKeyInternal>(params, std::move(pub_seed), std::move(root));
265}
266
268
270 return concat(m_private->key_bits(), m_public->key_bits());
271}
272
276
277std::unique_ptr<Public_Key> SphincsPlus_PrivateKey::public_key() const {
278 return std::make_unique<SphincsPlus_PublicKey>(*this);
279}
280
281class SphincsPlus_Signature_Operation final : public PK_Ops::Signature {
282 public:
283 SphincsPlus_Signature_Operation(std::shared_ptr<SphincsPlus_PrivateKeyInternal> private_key,
284 std::shared_ptr<SphincsPlus_PublicKeyInternal> public_key,
285 bool randomized) :
286 m_private(std::move(private_key)),
287 m_public(std::move(public_key)),
288 m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())),
289 m_randomized(randomized) {}
290
291 void update(const uint8_t msg[], size_t msg_len) override {
292 m_msg_buffer.insert(m_msg_buffer.end(), msg, msg + msg_len);
293 }
294
295 secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
296 const auto& p = m_public->parameters();
297
298 secure_vector<uint8_t> sphincs_sig_buffer(p.sphincs_signature_bytes());
299 BufferStuffer sphincs_sig(sphincs_sig_buffer);
300
301 // Compute and append the digest randomization value (R of spec).
302 SphincsOptionalRandomness opt_rand(m_public->seed());
303 if(m_randomized) {
304 opt_rand = rng.random_vec<SphincsOptionalRandomness>(p.n());
305 }
306 auto msg_random_s = sphincs_sig.next<SphincsMessageRandomness>(p.n());
307 m_hashes->PRF_msg(msg_random_s, m_private->prf(), opt_rand, m_msg_buffer);
308
309 // Derive the message digest and leaf index from R, PK and M.
310 auto [mhash, tree_idx, leaf_idx] = m_hashes->H_msg(msg_random_s, m_public->root(), m_msg_buffer);
311
312 // Clear the message buffer, the data is not needed anymore
313 m_msg_buffer.clear();
314
315 // Compute and append the FORS signature
316 Sphincs_Address fors_addr(Sphincs_Address_Type::ForsTree);
317 fors_addr.set_tree(tree_idx).set_keypair(leaf_idx);
318 auto fors_root = fors_sign_and_pkgen(sphincs_sig.next<ForsSignature>(p.fors_signature_bytes()),
319 mhash,
320 m_private->seed(),
321 fors_addr,
322 p,
323 *m_hashes);
324
325 // Compute and append the XMSS hypertree signature
326 ht_sign(sphincs_sig.next<SphincsHypertreeSignature>(p.ht_signature_bytes()),
327 fors_root,
328 m_private->seed(),
329 tree_idx,
330 leaf_idx,
331 p,
332 *m_hashes);
333
334 BOTAN_ASSERT_NOMSG(sphincs_sig.full());
335 return sphincs_sig_buffer;
336 }
337
338 size_t signature_length() const override { return m_public->parameters().sphincs_signature_bytes(); }
339
340 AlgorithmIdentifier algorithm_identifier() const override {
341 return m_public->parameters().algorithm_identifier();
342 }
343
344 std::string hash_function() const override { return m_hashes->msg_hash_function_name(); }
345
346 private:
347 std::shared_ptr<SphincsPlus_PrivateKeyInternal> m_private;
348 std::shared_ptr<SphincsPlus_PublicKeyInternal> m_public;
349 std::unique_ptr<Sphincs_Hash_Functions> m_hashes;
350 std::vector<uint8_t> m_msg_buffer;
351 bool m_randomized;
352};
353
355 std::string_view params,
356 std::string_view provider) const {
357 BOTAN_UNUSED(rng);
358 BOTAN_ARG_CHECK(params.empty() || params == "Deterministic" || params == "Randomized",
359 "Unexpected parameters for signing with SPHINCS+");
360
361 const bool randomized = (params == "Randomized");
362 if(provider.empty() || provider == "base") {
363 return std::make_unique<SphincsPlus_Signature_Operation>(m_private, m_public, randomized);
364 }
365 throw Provider_Not_Found(algo_name(), provider);
366}
367
368} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
const std::vector< uint8_t > & parameters() const
Definition asn1_obj.h:466
void random_vec(std::span< uint8_t > v)
Definition rng.h:179
secure_vector< uint8_t > raw_private_key_bits() const override
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
SphincsPlus_PrivateKey(std::span< const uint8_t > private_key, Sphincs_Parameter_Set type, Sphincs_Hash_Type hash)
secure_vector< uint8_t > private_key_bits() const override
std::unique_ptr< Public_Key > public_key() const override
std::vector< uint8_t > public_key_bits() const override
std::vector< uint8_t > raw_public_key_bits() const override
std::unique_ptr< PK_Ops::Verification > create_x509_verification_op(const AlgorithmIdentifier &signature_algorithm, std::string_view provider) const override
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
bool supports_operation(PublicKeyOperation op) const override
std::string algo_name() const override
Definition sphincsplus.h:43
size_t key_length() const override
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
bool check_key(RandomNumberGenerator &rng, bool strong) const override
AlgorithmIdentifier algorithm_identifier() const override
std::shared_ptr< SphincsPlus_PublicKeyInternal > m_public
Definition sphincsplus.h:65
size_t estimated_strength() const override
OID object_identifier() const override
static std::unique_ptr< Sphincs_Hash_Functions > create(const Sphincs_Parameters &sphincs_params, const SphincsPublicSeed &pub_seed)
Definition sp_hash.cpp:34
uint32_t private_key_bytes() const
uint32_t public_key_bytes() const
static Sphincs_Parameters create(Sphincs_Parameter_Set set, Sphincs_Hash_Type hash)
int(* update)(CTX *, const void *, CC_LONG len)
int(* final)(unsigned char *, CTX *)
PublicKeyOperation
Definition pk_keys.h:45
Gf448Elem root(const Gf448Elem &elem)
Compute the root of elem in the field.
Strong< std::vector< uint8_t >, struct SphincsTreeNode_ > SphincsTreeNode
Either an XMSS or FORS tree node or leaf.
Definition sp_types.h:56
SphincsTreeNode xmss_gen_root(const Sphincs_Parameters &params, const SphincsSecretSeed &secret_seed, Sphincs_Hash_Functions &hashes)
Definition sp_xmss.cpp:58
Strong< secure_vector< uint8_t >, struct SphincsMessageRandomness_ > SphincsMessageRandomness
Definition sp_types.h:50
SphincsTreeNode fors_sign_and_pkgen(StrongSpan< ForsSignature > sig_out, const SphincsHashedMessage &hashed_message, const SphincsSecretSeed &secret_seed, const Sphincs_Address &address, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
Definition sp_fors.cpp:46
SphincsTreeNode fors_public_key_from_signature(const SphincsHashedMessage &hashed_message, StrongSpan< const ForsSignature > signature, const Sphincs_Address &address, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
Definition sp_fors.cpp:112
Strong< std::vector< uint8_t >, struct ForsSignature_ > ForsSignature
Definition sp_types.h:58
void ht_sign(StrongSpan< SphincsHypertreeSignature > out_sig, const SphincsTreeNode &message_to_sign, const SphincsSecretSeed &secret_seed, XmssTreeIndexInLayer tree_index_in_layer, TreeNodeIndex idx_leaf, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
Sphincs_Parameter_Set
bool ht_verify(const SphincsTreeNode &signed_msg, StrongSpan< const SphincsHypertreeSignature > ht_sig, const SphincsTreeNode &pk_root, XmssTreeIndexInLayer tree_index_in_layer, TreeNodeIndex idx_leaf, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes)
Strong< std::vector< uint8_t >, struct SphincsXmssSignature_ > SphincsHypertreeSignature
Definition sp_types.h:52
Sphincs_Hash_Type
Strong< std::vector< uint8_t >, struct SphincsPublicSeed_ > SphincsPublicSeed
Definition sp_types.h:46
Strong< secure_vector< uint8_t >, struct SphincsSecretSeed_ > SphincsSecretSeed
Definition sp_types.h:47
Strong< secure_vector< uint8_t >, struct SphincsSecretPRF_ > SphincsSecretPRF
Definition sp_types.h:48
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:262
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
Strong< secure_vector< uint8_t >, struct SphincsOptionalRandomness_ > SphincsOptionalRandomness
Definition sp_types.h:49