1 #include <stddef.h>
2 #include <stdint.h>
3 #include <string.h>
4 
5 #include "address.h"
6 #include "api.h"
7 #include "fors.h"
8 #include "hash.h"
9 #include "hash_state.h"
10 #include "params.h"
11 #include "randombytes.h"
12 #include "thash.h"
13 #include "utils.h"
14 #include "wots.h"
15 
16 /**
17  * Computes the leaf at a given address. First generates the WOTS key pair,
18  * then computes leaf by hashing horizontally.
19  */
wots_gen_leaf(unsigned char * leaf,const unsigned char * sk_seed,const unsigned char * pub_seed,uint32_t addr_idx,const uint32_t tree_addr[8],const hash_state * hash_state_seeded)20 static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed,
21                           const unsigned char *pub_seed,
22                           uint32_t addr_idx, const uint32_t tree_addr[8],
23                           const hash_state *hash_state_seeded) {
24     unsigned char pk[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_WOTS_BYTES];
25     uint32_t wots_addr[8] = {0};
26     uint32_t wots_pk_addr[8] = {0};
27 
28     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
29         wots_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_WOTS);
30     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
31         wots_pk_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_WOTSPK);
32 
33     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr(
34         wots_addr, tree_addr);
35     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr(
36         wots_addr, addr_idx);
37     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk(
38         pk, sk_seed, pub_seed, wots_addr, hash_state_seeded);
39 
40     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr(
41         wots_pk_addr, wots_addr);
42     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN(
43         leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded);
44 }
45 
46 /*
47  * Returns the length of a secret key, in bytes
48  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void)49 size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void) {
50     return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES;
51 }
52 
53 /*
54  * Returns the length of a public key, in bytes
55  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void)56 size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void) {
57     return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES;
58 }
59 
60 /*
61  * Returns the length of a signature, in bytes
62  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void)63 size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void) {
64     return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES;
65 }
66 
67 /*
68  * Returns the length of the seed required to generate a key pair, in bytes
69  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void)70 size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void) {
71     return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES;
72 }
73 
74 /*
75  * Generates an SPX key pair given a seed of length
76  * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root]
77  * Format pk: [PUB_SEED || root]
78  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair(uint8_t * pk,uint8_t * sk,const uint8_t * seed)79 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair(
80     uint8_t *pk, uint8_t *sk, const uint8_t *seed) {
81     /* We do not need the auth path in key generation, but it simplifies the
82        code to have just one treehash routine that computes both root and path
83        in one function. */
84     unsigned char auth_path[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N];
85     uint32_t top_tree_addr[8] = {0};
86     hash_state hash_state_seeded;
87 
88     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(
89         top_tree_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_D - 1);
90     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
91         top_tree_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_HASHTREE);
92 
93     /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */
94     memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES);
95 
96     memcpy(pk, sk + 2 * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N);
97 
98     /* This hook allows the hash function instantiation to do whatever
99        preparation or computation it needs, based on the public seed. */
100     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk);
101 
102     /* Compute root node of the top-most subtree. */
103     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT(
104         sk + 3 * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N, auth_path, sk, sk + 2 * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N, 0, 0,
105         wots_gen_leaf, top_tree_addr, &hash_state_seeded);
106 
107     memcpy(pk + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N, sk + 3 * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N);
108 
109     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_destroy_hash_function(&hash_state_seeded);
110     return 0;
111 }
112 
113 /*
114  * Generates an SPX key pair.
115  * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root]
116  * Format pk: [PUB_SEED || root]
117  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair(uint8_t * pk,uint8_t * sk)118 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair(
119     uint8_t *pk, uint8_t *sk) {
120     unsigned char seed[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES];
121     randombytes(seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES);
122     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair(
123         pk, sk, seed);
124 
125     return 0;
126 }
127 
128 /**
129  * Returns an array containing a detached signature.
130  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature(uint8_t * sig,size_t * siglen,const uint8_t * m,size_t mlen,const uint8_t * sk)131 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature(
132     uint8_t *sig, size_t *siglen,
133     const uint8_t *m, size_t mlen, const uint8_t *sk) {
134     const unsigned char *sk_seed = sk;
135     const unsigned char *sk_prf = sk + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
136     const unsigned char *pk = sk + 2 * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
137     const unsigned char *pub_seed = pk;
138 
139     unsigned char optrand[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N];
140     unsigned char mhash[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_FORS_MSG_BYTES];
141     unsigned char root[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N];
142     uint32_t i;
143     uint64_t tree;
144     uint32_t idx_leaf;
145     uint32_t wots_addr[8] = {0};
146     uint32_t tree_addr[8] = {0};
147 
148     hash_state hash_state_seeded;
149 
150     /* This hook allows the hash function instantiation to do whatever
151        preparation or computation it needs, based on the public seed. */
152     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function(
153         &hash_state_seeded,
154         pub_seed, sk_seed);
155 
156     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
157         wots_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_WOTS);
158     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
159         tree_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_HASHTREE);
160 
161     /* Optionally, signing can be made non-deterministic using optrand.
162        This can help counter side-channel attacks that would benefit from
163        getting a large number of traces when the signer uses the same nodes. */
164     randombytes(optrand, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N);
165     /* Compute the digest randomization value. */
166     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random(
167         sig, sk_prf, optrand, m, mlen, &hash_state_seeded);
168 
169     /* Derive the message digest and leaf index from R, PK and M. */
170     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message(
171         mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded);
172     sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
173 
174     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree);
175     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr(
176         wots_addr, idx_leaf);
177 
178     /* Sign the message hash using FORS. */
179     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign(
180         sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded);
181     sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_FORS_BYTES;
182 
183     for (i = 0; i < PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_D; i++) {
184         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i);
185         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree);
186 
187         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr(
188             wots_addr, tree_addr);
189         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr(
190             wots_addr, idx_leaf);
191 
192         /* Compute a WOTS signature. */
193         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign(
194             sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded);
195         sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_WOTS_BYTES;
196 
197         /* Compute the authentication path for the used WOTS leaf. */
198         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT(
199             root, sig, sk_seed, pub_seed, idx_leaf, 0,
200             wots_gen_leaf, tree_addr, &hash_state_seeded);
201         sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
202 
203         /* Update the indices for the next layer. */
204         idx_leaf = (tree & ((1 << PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT) - 1));
205         tree = tree >> PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT;
206     }
207 
208     *siglen = PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES;
209 
210     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_destroy_hash_function(&hash_state_seeded);
211     return 0;
212 }
213 
214 /**
215  * Verifies a detached signature and message under a given public key.
216  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify(const uint8_t * sig,size_t siglen,const uint8_t * m,size_t mlen,const uint8_t * pk)217 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify(
218     const uint8_t *sig, size_t siglen,
219     const uint8_t *m, size_t mlen, const uint8_t *pk) {
220     const unsigned char *pub_seed = pk;
221     const unsigned char *pub_root = pk + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
222     unsigned char mhash[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_FORS_MSG_BYTES];
223     unsigned char wots_pk[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_WOTS_BYTES];
224     unsigned char root[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N];
225     unsigned char leaf[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N];
226     unsigned int i;
227     uint64_t tree;
228     uint32_t idx_leaf;
229     uint32_t wots_addr[8] = {0};
230     uint32_t tree_addr[8] = {0};
231     uint32_t wots_pk_addr[8] = {0};
232 
233     hash_state hash_state_seeded;
234 
235     if (siglen != PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES) {
236         return -1;
237     }
238 
239     /* This hook allows the hash function instantiation to do whatever
240        preparation or computation it needs, based on the public seed. */
241     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function(
242         &hash_state_seeded,
243         pub_seed, NULL);
244 
245     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
246         wots_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_WOTS);
247     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
248         tree_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_HASHTREE);
249     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type(
250         wots_pk_addr, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ADDR_TYPE_WOTSPK);
251 
252     /* Derive the message digest and leaf index from R || PK || M. */
253     /* The additional PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N is a result of the hash domain separator. */
254     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message(
255         mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded);
256     sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
257 
258     /* Layer correctly defaults to 0, so no need to set_layer_addr */
259     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree);
260     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr(
261         wots_addr, idx_leaf);
262 
263     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig(
264         root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded);
265     sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_FORS_BYTES;
266 
267     /* For each subtree.. */
268     for (i = 0; i < PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_D; i++) {
269         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i);
270         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree);
271 
272         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr(
273             wots_addr, tree_addr);
274         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr(
275             wots_addr, idx_leaf);
276 
277         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr(
278             wots_pk_addr, wots_addr);
279 
280         /* The WOTS public key is only correct if the signature was correct. */
281         /* Initially, root is the FORS pk, but on subsequent iterations it is
282            the root of the subtree below the currently processed subtree. */
283         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig(
284             wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded);
285         sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_WOTS_BYTES;
286 
287         /* Compute the leaf node using the WOTS public key. */
288         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN(
289             leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded);
290 
291         /* Compute the root node of this subtree. */
292         PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root(
293             root, leaf, idx_leaf, 0, sig, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT,
294             pub_seed, tree_addr, &hash_state_seeded);
295         sig += PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT * PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N;
296 
297         /* Update the indices for the next layer. */
298         idx_leaf = (tree & ((1 << PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT) - 1));
299         tree = tree >> PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_TREE_HEIGHT;
300     }
301 
302     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_destroy_hash_function(&hash_state_seeded);
303     /* Check if the root node equals the root node in the public key. */
304     if (memcmp(root, pub_root, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_N) != 0) {
305         return -1;
306     }
307 
308     return 0;
309 }
310 
311 
312 /**
313  * Returns an array containing the signature followed by the message.
314  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign(uint8_t * sm,size_t * smlen,const uint8_t * m,size_t mlen,const uint8_t * sk)315 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign(
316     uint8_t *sm, size_t *smlen,
317     const uint8_t *m, size_t mlen, const uint8_t *sk) {
318     size_t siglen;
319 
320     PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature(
321         sm, &siglen, m, mlen, sk);
322 
323     memmove(sm + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES, m, mlen);
324     *smlen = siglen + mlen;
325 
326     return 0;
327 }
328 
329 /**
330  * Verifies a given signature-message pair under a given public key.
331  */
PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open(uint8_t * m,size_t * mlen,const uint8_t * sm,size_t smlen,const uint8_t * pk)332 int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open(
333     uint8_t *m, size_t *mlen,
334     const uint8_t *sm, size_t smlen, const uint8_t *pk) {
335     /* The API caller does not necessarily know what size a signature should be
336        but SPHINCS+ signatures are always exactly PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES. */
337     if (smlen < PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES) {
338         memset(m, 0, smlen);
339         *mlen = 0;
340         return -1;
341     }
342 
343     *mlen = smlen - PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES;
344 
345     if (PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify(
346                 sm, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES, sm + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES, *mlen, pk)) {
347         memset(m, 0, smlen);
348         *mlen = 0;
349         return -1;
350     }
351 
352     /* If verification was successful, move the message to the right place. */
353     memmove(m, sm + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_BYTES, *mlen);
354 
355     return 0;
356 }
357