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