Coverage Report

Created: 2026-08-15 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wolfssl-heapmath/wolfcrypt/src/wc_mlkem_poly.c
Line
Count
Source
1
/* wc_mlkem_poly.c
2
 *
3
 * Copyright (C) 2006-2026 wolfSSL Inc.
4
 *
5
 * This file is part of wolfSSL.
6
 *
7
 * wolfSSL is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * wolfSSL is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20
 */
21
22
/* Implementation based on FIPS 203:
23
 *   https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf
24
 *
25
 * Original implementation based on NIST 3rd Round submission package.
26
 * See link at:
27
 *   https://csrc.nist.gov/Projects/post-quantum-cryptography/
28
 *   post-quantum-cryptography-standardization/round-3-submissions
29
 */
30
31
/* Implementation of the functions that operate on polynomials or vectors of
32
 * polynomials.
33
 */
34
35
/* Possible ML-KEM options:
36
 *
37
 * WOLFSSL_HAVE_MLKEM                                         Default: OFF
38
 *   Enables this code, wolfSSL implementation, to be built.
39
 *
40
 * WOLFSSL_WC_ML_KEM_512                                      Default: OFF
41
 *   Enables the ML-KEM 512 parameter implementations.
42
 * WOLFSSL_WC_ML_KEM_768                                      Default: OFF
43
 *   Enables the ML-KEM 768 parameter implementations.
44
 * WOLFSSL_WC_ML_KEM_1024                                     Default: OFF
45
 *   Enables the ML-KEM 1024 parameter implementations.
46
 * WOLFSSL_KYBER512                                           Default: OFF
47
 *   Enables the KYBER512 parameter implementations.
48
 * WOLFSSL_KYBER768                                           Default: OFF
49
 *   Enables the KYBER768 parameter implementations.
50
 * WOLFSSL_KYBER1024                                          Default: OFF
51
 *   Enables the KYBER1024 parameter implementations.
52
 *
53
 * USE_INTEL_SPEEDUP                                          Default: OFF
54
 *   Compiles in Intel x64 specific implementations that are faster.
55
 * WOLFSSL_MLKEM_NO_LARGE_CODE                                Default: OFF
56
 *   Compiles smaller, fast code size with a speed trade-off.
57
 * WOLFSSL_MLKEM_SMALL                                        Default: OFF
58
 *   Compiles to small code size with a speed trade-off.
59
 * WOLFSSL_SMALL_STACK                                        Default: OFF
60
 *   Use less stack by dynamically allocating local variables.
61
 *
62
 * WOLFSSL_MLKEM_NTT_UNROLL                                   Default: OFF
63
 *   Enable an alternative NTT implementation that may be faster on some
64
 *   platforms and is smaller in code size.
65
 * WOLFSSL_MLKEM_INVNTT_UNROLL                                Default: OFF
66
 *   Enables an alternative inverse NTT implementation that may be faster on
67
 *   some platforms and is smaller in code size.
68
 */
69
70
#define WC_FIPS_LL_CRYPTO
71
#define _WC_BUILDING_WC_MLKEM_POLY_C
72
73
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
74
75
#ifdef WC_MLKEM_NO_ASM
76
    #undef USE_INTEL_SPEEDUP
77
    #undef WOLFSSL_ARMASM
78
    #undef WOLFSSL_RISCV_ASM
79
#endif
80
#ifdef WOLFSSL_X86_BUILD
81
    #undef USE_INTEL_SPEEDUP
82
#endif
83
84
#include <wolfssl/wolfcrypt/wc_mlkem.h>
85
#include <wolfssl/wolfcrypt/sha3.h>
86
#include <wolfssl/wolfcrypt/cpuid.h>
87
#include <wolfssl/wolfcrypt/memory.h>
88
89
#ifdef WOLFSSL_HAVE_MLKEM
90
91
#ifdef NO_INLINE
92
    #include <wolfssl/wolfcrypt/misc.h>
93
#else
94
    #define WOLFSSL_MISC_INCLUDED
95
    #include <wolfcrypt/src/misc.c>
96
#endif
97
98
#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
99
    defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
100
static int mlkem_gen_matrix_i(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed,
101
    int i, int transposed);
102
static int mlkem_get_noise_i(MLKEM_PRF_T* prf, int k, sword16* vec2,
103
    byte* seed, int i, int make);
104
static int mlkem_get_noise_eta2_c(MLKEM_PRF_T* prf, sword16* p,
105
    const byte* seed);
106
#endif
107
108
/* Declared in wc_mlkem.c to stop compiler optimizer from simplifying. */
109
extern sword16 wc_mlkem_opt_blocker(void);
110
111
#if defined(USE_INTEL_SPEEDUP) || (defined(__aarch64__) && \
112
    defined(WOLFSSL_ARMASM))
113
static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
114
#endif
115
116
/* Half of Q plus one. Converted message bit value of 1. */
117
0
#define MLKEM_Q_1_HALF      ((MLKEM_Q + 1) / 2)
118
/* Half of Q */
119
0
#define MLKEM_Q_HALF        (MLKEM_Q / 2)
120
121
122
/* q^-1 mod 2^16 (inverse of 3329 mod 65536) */
123
43.7M
#define MLKEM_QINV       62209
124
125
/* Used in Barrett Reduction:
126
 *    r = a mod q
127
 * => r = a - ((V * a) >> 26) * q), as V based on 2^26
128
 * V is the multiplier that gets the quotient after shifting.
129
 */
130
6.97M
#define MLKEM_V          (((1UL << 26) + (MLKEM_Q / 2)) / MLKEM_Q)
131
132
/* Used in converting to Montgomery form.
133
 * f is the normalizer = 2^k % m.
134
 * 16-bit value cast to sword32 in use.
135
 */
136
3.48M
#define MLKEM_F          (((word64)1 << 32) % MLKEM_Q)
137
138
/* Number of bytes in an output block of SHA-3-128 */
139
#define SHA3_128_BYTES   (WC_SHA3_128_COUNT * 8)
140
/* Number of bytes in an output block of SHA-3-256 */
141
#define SHA3_256_BYTES   (WC_SHA3_256_COUNT * 8)
142
143
/* Number of blocks to generate for matrix. */
144
#define GEN_MATRIX_NBLOCKS \
145
84.9k
    ((12 * MLKEM_N / 8 * (1 << 12) / MLKEM_Q + XOF_BLOCK_SIZE) / XOF_BLOCK_SIZE)
146
/* Number of bytes to generate for matrix. */
147
46.7k
#define GEN_MATRIX_SIZE     GEN_MATRIX_NBLOCKS * XOF_BLOCK_SIZE
148
149
150
/* Number of random bytes to generate for ETA3. */
151
#define ETA3_RAND_SIZE     ((3 * MLKEM_N) / 4)
152
/* Number of random bytes to generate for ETA2. */
153
#define ETA2_RAND_SIZE     ((2 * MLKEM_N) / 4)
154
155
156
/* Montgomery reduce a.
157
 *
158
 * @param  [in]  a  32-bit value to be reduced.
159
 * @return  Montgomery reduction result.
160
 */
161
#define MLKEM_MONT_RED(a) \
162
43.7M
    (sword16)(((a) - (sword32)(((sword16)((sword16)(a) * \
163
43.7M
                                (sword16)MLKEM_QINV)) * \
164
43.7M
                               (sword32)MLKEM_Q)) >> 16)
165
166
/* Barrett reduce a. r = a mod q.
167
 *
168
 * Converted division to multiplication.
169
 *
170
 * @param  [in]  a  16-bit value to be reduced to range of q.
171
 * @return  Modulo result.
172
 */
173
#define MLKEM_BARRETT_RED(a) \
174
6.97M
    (sword16)((sword16)(a) - (sword16)((sword16)( \
175
6.97M
        ((sword32)((sword32)MLKEM_V * (sword16)(a))) >> 26) * (word16)MLKEM_Q))
176
177
178
/* Zetas for NTT. */
179
const sword16 zetas[MLKEM_N / 2] = {
180
    2285, 2571, 2970, 1812, 1493, 1422,  287,  202,
181
    3158,  622, 1577,  182,  962, 2127, 1855, 1468,
182
     573, 2004,  264,  383, 2500, 1458, 1727, 3199,
183
    2648, 1017,  732,  608, 1787,  411, 3124, 1758,
184
    1223,  652, 2777, 1015, 2036, 1491, 3047, 1785,
185
     516, 3321, 3009, 2663, 1711, 2167,  126, 1469,
186
    2476, 3239, 3058,  830,  107, 1908, 3082, 2378,
187
    2931,  961, 1821, 2604,  448, 2264,  677, 2054,
188
    2226,  430,  555,  843, 2078,  871, 1550,  105,
189
     422,  587,  177, 3094, 3038, 2869, 1574, 1653,
190
    3083,  778, 1159, 3182, 2552, 1483, 2727, 1119,
191
    1739,  644, 2457,  349,  418,  329, 3173, 3254,
192
     817, 1097,  603,  610, 1322, 2044, 1864,  384,
193
    2114, 3193, 1218, 1994, 2455,  220, 2142, 1670,
194
    2144, 1799, 2051,  794, 1819, 2475, 2459,  478,
195
    3221, 3021,  996,  991,  958, 1869, 1522, 1628
196
};
197
198
199
#if !defined(WOLFSSL_ARMASM)
200
/* Number-Theoretic Transform.
201
 *
202
 * FIPS 203, Algorithm 9: NTT(f)
203
 * Computes the NTT representation f_hat of the given polynomial f element of
204
 * R_q.
205
 *   1: f_hat <- f
206
 *   2: i <- 1
207
 *   3: for (len <- 128; len >= 2; len <- len/2)
208
 *   4:     for (start <- 0; start < 256; start <- start + 2.len)
209
 *   5:         zeta <- zetas^BitRev_7(i) mod q
210
 *   6:         i <- i + 1
211
 *   7:         for (j <- start; j < start + len; j++)
212
 *   8:             t <- zeta.f[j+len]
213
 *   9:             f_hat[j+len] <- f_hat[j] - t
214
 *  10:             f_hat[j] <- f_hat[j] + t
215
 *  11:         end for
216
 *  12:     end for
217
 *  13: end for
218
 *  14: return f_hat
219
 *
220
 * @param  [in, out]  r  Polynomial to transform.
221
 */
222
static void mlkem_ntt(sword16* r)
223
13.6k
{
224
#ifdef WOLFSSL_MLKEM_SMALL
225
    unsigned int len;
226
    unsigned int k;
227
    unsigned int j;
228
229
    /* Step 2 */
230
    k = 1;
231
    /* Step 3 */
232
    for (len = MLKEM_N / 2; len >= 2; len >>= 1) {
233
        unsigned int start;
234
        /* Step 4 */
235
        for (start = 0; start < MLKEM_N; start = j + len) {
236
            /* Step 5, 6*/
237
            sword16 zeta = zetas[k++];
238
            /* Step 7 */
239
            for (j = start; j < start + len; ++j) {
240
                /* Step 8 */
241
                sword32 p = (sword32)zeta * r[j + len];
242
                sword16 t = MLKEM_MONT_RED(p);
243
                sword16 rj = r[j];
244
                /* Step 9 */
245
                r[j + len] = (sword16)(rj - t);
246
                /* Step 10 */
247
                r[j] = (sword16)(rj + t);
248
            }
249
        }
250
    }
251
252
    /* Reduce coefficients with quick algorithm. */
253
    for (j = 0; j < MLKEM_N; ++j) {
254
        r[j] = MLKEM_BARRETT_RED(r[j]);
255
    }
256
#elif defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
257
    /* Take out the first iteration. */
258
    unsigned int len;
259
    unsigned int k = 1;
260
    unsigned int j;
261
    unsigned int start;
262
    sword16 zeta = zetas[k++];
263
264
    for (j = 0; j < MLKEM_N / 2; ++j) {
265
        sword32 p = (sword32)zeta * r[j + MLKEM_N / 2];
266
        sword16 t = MLKEM_MONT_RED(p);
267
        sword16 rj = r[j];
268
        r[j + MLKEM_N / 2] = (sword16)(rj - t);
269
        r[j] = (sword16)(rj + t);
270
    }
271
    for (len = MLKEM_N / 4; len >= 2; len >>= 1) {
272
        for (start = 0; start < MLKEM_N; start = j + len) {
273
            zeta = zetas[k++];
274
            for (j = start; j < start + len; ++j) {
275
                sword32 p = (sword32)zeta * r[j + len];
276
                sword16 t = MLKEM_MONT_RED(p);
277
                sword16 rj = r[j];
278
                r[j + len] = (sword16)(rj - t);
279
                r[j] = (sword16)(rj + t);
280
            }
281
        }
282
    }
283
284
    /* Reduce coefficients with quick algorithm. */
285
    for (j = 0; j < MLKEM_N; ++j) {
286
        r[j] = MLKEM_BARRETT_RED(r[j]);
287
    }
288
#elif defined(WOLFSSL_MLKEM_NTT_UNROLL)
289
    /* Unroll len loop (Step 3). */
290
    unsigned int k = 1;
291
    unsigned int j;
292
    unsigned int start;
293
    sword16 zeta = zetas[k++];
294
295
    /* len = 128 */
296
    for (j = 0; j < MLKEM_N / 2; ++j) {
297
        sword32 p = (sword32)zeta * r[j + MLKEM_N / 2];
298
        sword16 t = MLKEM_MONT_RED(p);
299
        sword16 rj = r[j];
300
        r[j + MLKEM_N / 2] = rj - t;
301
        r[j] = rj + t;
302
    }
303
    /* len = 64 */
304
    for (start = 0; start < MLKEM_N; start += 2 * 64) {
305
        zeta = zetas[k++];
306
        for (j = 0; j < 64; ++j) {
307
            sword32 p = (sword32)zeta * r[start + j + 64];
308
            sword16 t = MLKEM_MONT_RED(p);
309
            sword16 rj = r[start + j];
310
            r[start + j + 64] = rj - t;
311
            r[start + j] = rj + t;
312
        }
313
    }
314
    /* len = 32 */
315
    for (start = 0; start < MLKEM_N; start += 2 * 32) {
316
        zeta = zetas[k++];
317
        for (j = 0; j < 32; ++j) {
318
            sword32 p = (sword32)zeta * r[start + j + 32];
319
            sword16 t = MLKEM_MONT_RED(p);
320
            sword16 rj = r[start + j];
321
            r[start + j + 32] = rj - t;
322
            r[start + j] = rj + t;
323
        }
324
    }
325
    /* len = 16 */
326
    for (start = 0; start < MLKEM_N; start += 2 * 16) {
327
        zeta = zetas[k++];
328
        for (j = 0; j < 16; ++j) {
329
            sword32 p = (sword32)zeta * r[start + j + 16];
330
            sword16 t = MLKEM_MONT_RED(p);
331
            sword16 rj = r[start + j];
332
            r[start + j + 16] = rj - t;
333
            r[start + j] = rj + t;
334
        }
335
    }
336
    /* len = 8 */
337
    for (start = 0; start < MLKEM_N; start += 2 * 8) {
338
        zeta = zetas[k++];
339
        for (j = 0; j < 8; ++j) {
340
            sword32 p = (sword32)zeta * r[start + j + 8];
341
            sword16 t = MLKEM_MONT_RED(p);
342
            sword16 rj = r[start + j];
343
            r[start + j + 8] = rj - t;
344
            r[start + j] = rj + t;
345
        }
346
    }
347
    /* len = 4 */
348
    for (start = 0; start < MLKEM_N; start += 2 * 4) {
349
        zeta = zetas[k++];
350
        for (j = 0; j < 4; ++j) {
351
            sword32 p = (sword32)zeta * r[start + j + 4];
352
            sword16 t = MLKEM_MONT_RED(p);
353
            sword16 rj = r[start + j];
354
            r[start + j + 4] = rj - t;
355
            r[start + j] = rj + t;
356
        }
357
    }
358
    /* len = 2 */
359
    for (start = 0; start < MLKEM_N; start += 2 * 2) {
360
        zeta = zetas[k++];
361
        for (j = 0; j < 2; ++j) {
362
            sword32 p = (sword32)zeta * r[start + j + 2];
363
            sword16 t = MLKEM_MONT_RED(p);
364
            sword16 rj = r[start + j];
365
            r[start + j + 2] = rj - t;
366
            r[start + j] = rj + t;
367
        }
368
    }
369
    /* Reduce coefficients with quick algorithm. */
370
    for (j = 0; j < MLKEM_N; ++j) {
371
        r[j] = MLKEM_BARRETT_RED(r[j]);
372
    }
373
#else
374
    /* Unroll len (2, 3, 2) and start loops. */
375
13.6k
    unsigned int j;
376
13.6k
    sword16 t0;
377
13.6k
    sword16 t1;
378
13.6k
    sword16 t2;
379
13.6k
    sword16 t3;
380
381
    /* len = 128,64 */
382
13.6k
    sword16 zeta128 = zetas[1];
383
13.6k
    sword16 zeta64_0 = zetas[2];
384
13.6k
    sword16 zeta64_1 = zetas[3];
385
449k
    for (j = 0; j < MLKEM_N / 8; j++) {
386
435k
        sword16 r0 = r[j +   0];
387
435k
        sword16 r1 = r[j +  32];
388
435k
        sword16 r2 = r[j +  64];
389
435k
        sword16 r3 = r[j +  96];
390
435k
        sword16 r4 = r[j + 128];
391
435k
        sword16 r5 = r[j + 160];
392
435k
        sword16 r6 = r[j + 192];
393
435k
        sword16 r7 = r[j + 224];
394
395
435k
        t0 = MLKEM_MONT_RED((sword32)zeta128 * r4);
396
435k
        t1 = MLKEM_MONT_RED((sword32)zeta128 * r5);
397
435k
        t2 = MLKEM_MONT_RED((sword32)zeta128 * r6);
398
435k
        t3 = MLKEM_MONT_RED((sword32)zeta128 * r7);
399
435k
        r4 = (sword16)(r0 - t0);
400
435k
        r5 = (sword16)(r1 - t1);
401
435k
        r6 = (sword16)(r2 - t2);
402
435k
        r7 = (sword16)(r3 - t3);
403
435k
        r0 = (sword16)(r0 + t0);
404
435k
        r1 = (sword16)(r1 + t1);
405
435k
        r2 = (sword16)(r2 + t2);
406
435k
        r3 = (sword16)(r3 + t3);
407
408
435k
        t0 = MLKEM_MONT_RED((sword32)zeta64_0 * r2);
409
435k
        t1 = MLKEM_MONT_RED((sword32)zeta64_0 * r3);
410
435k
        t2 = MLKEM_MONT_RED((sword32)zeta64_1 * r6);
411
435k
        t3 = MLKEM_MONT_RED((sword32)zeta64_1 * r7);
412
435k
        r2 = (sword16)(r0 - t0);
413
435k
        r3 = (sword16)(r1 - t1);
414
435k
        r6 = (sword16)(r4 - t2);
415
435k
        r7 = (sword16)(r5 - t3);
416
435k
        r0 = (sword16)(r0 + t0);
417
435k
        r1 = (sword16)(r1 + t1);
418
435k
        r4 = (sword16)(r4 + t2);
419
435k
        r5 = (sword16)(r5 + t3);
420
421
435k
        r[j +   0] = r0;
422
435k
        r[j +  32] = r1;
423
435k
        r[j +  64] = r2;
424
435k
        r[j +  96] = r3;
425
435k
        r[j + 128] = r4;
426
435k
        r[j + 160] = r5;
427
435k
        r[j + 192] = r6;
428
435k
        r[j + 224] = r7;
429
435k
    }
430
431
    /* len = 32,16,8 */
432
68.0k
    for (j = 0; j < MLKEM_N; j += 64) {
433
54.4k
        unsigned int i;
434
54.4k
        sword16 zeta32   = zetas[ 4 + j / 64 + 0];
435
54.4k
        sword16 zeta16_0 = zetas[ 8 + j / 32 + 0];
436
54.4k
        sword16 zeta16_1 = zetas[ 8 + j / 32 + 1];
437
54.4k
        sword16 zeta8_0  = zetas[16 + j / 16 + 0];
438
54.4k
        sword16 zeta8_1  = zetas[16 + j / 16 + 1];
439
54.4k
        sword16 zeta8_2  = zetas[16 + j / 16 + 2];
440
54.4k
        sword16 zeta8_3  = zetas[16 + j / 16 + 3];
441
490k
        for (i = 0; i < 8; i++) {
442
435k
            sword16 r0 = r[j + i +  0];
443
435k
            sword16 r1 = r[j + i +  8];
444
435k
            sword16 r2 = r[j + i + 16];
445
435k
            sword16 r3 = r[j + i + 24];
446
435k
            sword16 r4 = r[j + i + 32];
447
435k
            sword16 r5 = r[j + i + 40];
448
435k
            sword16 r6 = r[j + i + 48];
449
435k
            sword16 r7 = r[j + i + 56];
450
451
435k
            t0 = MLKEM_MONT_RED((sword32)zeta32 * r4);
452
435k
            t1 = MLKEM_MONT_RED((sword32)zeta32 * r5);
453
435k
            t2 = MLKEM_MONT_RED((sword32)zeta32 * r6);
454
435k
            t3 = MLKEM_MONT_RED((sword32)zeta32 * r7);
455
435k
            r4 = (sword16)(r0 - t0);
456
435k
            r5 = (sword16)(r1 - t1);
457
435k
            r6 = (sword16)(r2 - t2);
458
435k
            r7 = (sword16)(r3 - t3);
459
435k
            r0 = (sword16)(r0 + t0);
460
435k
            r1 = (sword16)(r1 + t1);
461
435k
            r2 = (sword16)(r2 + t2);
462
435k
            r3 = (sword16)(r3 + t3);
463
464
435k
            t0 = MLKEM_MONT_RED((sword32)zeta16_0 * r2);
465
435k
            t1 = MLKEM_MONT_RED((sword32)zeta16_0 * r3);
466
435k
            t2 = MLKEM_MONT_RED((sword32)zeta16_1 * r6);
467
435k
            t3 = MLKEM_MONT_RED((sword32)zeta16_1 * r7);
468
435k
            r2 = (sword16)(r0 - t0);
469
435k
            r3 = (sword16)(r1 - t1);
470
435k
            r6 = (sword16)(r4 - t2);
471
435k
            r7 = (sword16)(r5 - t3);
472
435k
            r0 = (sword16)(r0 + t0);
473
435k
            r1 = (sword16)(r1 + t1);
474
435k
            r4 = (sword16)(r4 + t2);
475
435k
            r5 = (sword16)(r5 + t3);
476
477
435k
            t0 = MLKEM_MONT_RED((sword32)zeta8_0 * r1);
478
435k
            t1 = MLKEM_MONT_RED((sword32)zeta8_1 * r3);
479
435k
            t2 = MLKEM_MONT_RED((sword32)zeta8_2 * r5);
480
435k
            t3 = MLKEM_MONT_RED((sword32)zeta8_3 * r7);
481
435k
            r1 = (sword16)(r0 - t0);
482
435k
            r3 = (sword16)(r2 - t1);
483
435k
            r5 = (sword16)(r4 - t2);
484
435k
            r7 = (sword16)(r6 - t3);
485
435k
            r0 = (sword16)(r0 + t0);
486
435k
            r2 = (sword16)(r2 + t1);
487
435k
            r4 = (sword16)(r4 + t2);
488
435k
            r6 = (sword16)(r6 + t3);
489
490
435k
            r[j + i +  0] = r0;
491
435k
            r[j + i +  8] = r1;
492
435k
            r[j + i + 16] = r2;
493
435k
            r[j + i + 24] = r3;
494
435k
            r[j + i + 32] = r4;
495
435k
            r[j + i + 40] = r5;
496
435k
            r[j + i + 48] = r6;
497
435k
            r[j + i + 56] = r7;
498
435k
        }
499
54.4k
    }
500
501
    /* len = 4,2 and Final reduction */
502
449k
    for (j = 0; j < MLKEM_N; j += 8) {
503
435k
        sword16 zeta4  = zetas[32 + j / 8 + 0];
504
435k
        sword16 zeta2_0 = zetas[64 + j / 4 + 0];
505
435k
        sword16 zeta2_1 = zetas[64 + j / 4 + 1];
506
435k
        sword16 r0 = r[j + 0];
507
435k
        sword16 r1 = r[j + 1];
508
435k
        sword16 r2 = r[j + 2];
509
435k
        sword16 r3 = r[j + 3];
510
435k
        sword16 r4 = r[j + 4];
511
435k
        sword16 r5 = r[j + 5];
512
435k
        sword16 r6 = r[j + 6];
513
435k
        sword16 r7 = r[j + 7];
514
515
435k
        t0 = MLKEM_MONT_RED((sword32)zeta4 * r4);
516
435k
        t1 = MLKEM_MONT_RED((sword32)zeta4 * r5);
517
435k
        t2 = MLKEM_MONT_RED((sword32)zeta4 * r6);
518
435k
        t3 = MLKEM_MONT_RED((sword32)zeta4 * r7);
519
435k
        r4 = (sword16)(r0 - t0);
520
435k
        r5 = (sword16)(r1 - t1);
521
435k
        r6 = (sword16)(r2 - t2);
522
435k
        r7 = (sword16)(r3 - t3);
523
435k
        r0 = (sword16)(r0 + t0);
524
435k
        r1 = (sword16)(r1 + t1);
525
435k
        r2 = (sword16)(r2 + t2);
526
435k
        r3 = (sword16)(r3 + t3);
527
528
435k
        t0 = MLKEM_MONT_RED((sword32)zeta2_0 * r2);
529
435k
        t1 = MLKEM_MONT_RED((sword32)zeta2_0 * r3);
530
435k
        t2 = MLKEM_MONT_RED((sword32)zeta2_1 * r6);
531
435k
        t3 = MLKEM_MONT_RED((sword32)zeta2_1 * r7);
532
435k
        r2 = (sword16)(r0 - t0);
533
435k
        r3 = (sword16)(r1 - t1);
534
435k
        r6 = (sword16)(r4 - t2);
535
435k
        r7 = (sword16)(r5 - t3);
536
435k
        r0 = (sword16)(r0 + t0);
537
435k
        r1 = (sword16)(r1 + t1);
538
435k
        r4 = (sword16)(r4 + t2);
539
435k
        r5 = (sword16)(r5 + t3);
540
541
435k
        r[j + 0] = MLKEM_BARRETT_RED(r0);
542
435k
        r[j + 1] = MLKEM_BARRETT_RED(r1);
543
435k
        r[j + 2] = MLKEM_BARRETT_RED(r2);
544
435k
        r[j + 3] = MLKEM_BARRETT_RED(r3);
545
435k
        r[j + 4] = MLKEM_BARRETT_RED(r4);
546
435k
        r[j + 5] = MLKEM_BARRETT_RED(r5);
547
435k
        r[j + 6] = MLKEM_BARRETT_RED(r6);
548
435k
        r[j + 7] = MLKEM_BARRETT_RED(r7);
549
435k
    }
550
13.6k
#endif
551
13.6k
}
552
553
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
554
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
555
/* Zetas for inverse NTT. */
556
const sword16 zetas_inv[MLKEM_N / 2] = {
557
    1701, 1807, 1460, 2371, 2338, 2333,  308,  108,
558
    2851,  870,  854, 1510, 2535, 1278, 1530, 1185,
559
    1659, 1187, 3109,  874, 1335, 2111,  136, 1215,
560
    2945, 1465, 1285, 2007, 2719, 2726, 2232, 2512,
561
      75,  156, 3000, 2911, 2980,  872, 2685, 1590,
562
    2210,  602, 1846,  777,  147, 2170, 2551,  246,
563
    1676, 1755,  460,  291,  235, 3152, 2742, 2907,
564
    3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103,
565
    1275, 2652, 1065, 2881,  725, 1508, 2368,  398,
566
     951,  247, 1421, 3222, 2499,  271,   90,  853,
567
    1860, 3203, 1162, 1618,  666,  320,    8, 2813,
568
    1544,  282, 1838, 1293, 2314,  552, 2677, 2106,
569
    1571,  205, 2918, 1542, 2721, 2597, 2312,  681,
570
     130, 1602, 1871,  829, 2946, 3065, 1325, 2756,
571
    1861, 1474, 1202, 2367, 3147, 1752, 2707,  171,
572
    3127, 3042, 1907, 1836, 1517,  359,  758, 1441
573
};
574
575
/* Inverse Number-Theoretic Transform.
576
 *
577
 * FIPS 203, Algorithm 10: NTT^-1(f_hat)
578
 * Computes the polynomial f element of R_q that corresponds to the given NTT
579
 * representation f element of T_q.
580
 *   1: f <- f_hat
581
 *   2: i <- 127
582
 *   3: for (len <- 2; len <= 128 ; len <- 2.len)
583
 *   4:     for (start <- 0; start < 256; start <- start + 2.len)
584
 *   5:         zeta <- zetas^BitRev_7(i) mod q
585
 *   6:         i <- i - 1
586
 *   7:         for (j <- start; j < start + len; j++)
587
 *   8:             t <- f[j]
588
 *   9:             f[j] <- t + f[j + len]
589
 *  10:             f[j + len] <- zeta.(f[j+len] - t)
590
 *  11:         end for
591
 *  12:     end for
592
 *  13: end for
593
 *  14: f <- f.3303 mod q
594
 *  15: return f
595
 *
596
 * @param  [in, out]  r  Polynomial to transform.
597
 */
598
static void mlkem_invntt(sword16* r)
599
0
{
600
#ifdef WOLFSSL_MLKEM_SMALL
601
    unsigned int len;
602
    unsigned int k;
603
    unsigned int j;
604
    sword16 zeta;
605
606
    /* Step 2 - table reversed */
607
    k = 0;
608
    /* Step 3 */
609
    for (len = 2; len <= MLKEM_N / 2; len <<= 1) {
610
        unsigned int start;
611
        /* Step 4 */
612
        for (start = 0; start < MLKEM_N; start = j + len) {
613
            /* Step 5, 6 */
614
            zeta = zetas_inv[k++];
615
            /* Step 7 */
616
            for (j = start; j < start + len; ++j) {
617
                sword32 p;
618
                /* Step 8 */
619
                sword16 rj = r[j];
620
                sword16 rjl = r[j + len];
621
                /* Step 9 */
622
                sword16 t = (sword16)(rj + rjl);
623
                r[j] = MLKEM_BARRETT_RED(t);
624
                /* Step 10 */
625
                rjl = (sword16)(rj - rjl);
626
                p = (sword32)zeta * rjl;
627
                r[j + len] = MLKEM_MONT_RED(p);
628
            }
629
        }
630
    }
631
632
    /* Step 14 */
633
    zeta = zetas_inv[127];
634
    for (j = 0; j < MLKEM_N; ++j) {
635
        sword32 p = (sword32)zeta * r[j];
636
        r[j] = MLKEM_MONT_RED(p);
637
    }
638
#elif defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
639
    /* Take out last iteration. */
640
    unsigned int len;
641
    unsigned int k;
642
    unsigned int j;
643
    sword16 zeta;
644
    sword16 zeta2;
645
646
    k = 0;
647
    for (len = 2; len <= MLKEM_N / 4; len <<= 1) {
648
        unsigned int start;
649
        for (start = 0; start < MLKEM_N; start = j + len) {
650
            zeta = zetas_inv[k++];
651
            for (j = start; j < start + len; ++j) {
652
                sword32 p;
653
                sword16 rj = r[j];
654
                sword16 rjl = r[j + len];
655
                sword16 t = (sword16)(rj + rjl);
656
                r[j] = MLKEM_BARRETT_RED(t);
657
                rjl = (sword16)(rj - rjl);
658
                p = (sword32)zeta * rjl;
659
                r[j + len] = MLKEM_MONT_RED(p);
660
            }
661
        }
662
    }
663
664
    zeta = zetas_inv[126];
665
    zeta2 = zetas_inv[127];
666
    for (j = 0; j < MLKEM_N / 2; ++j) {
667
        sword32 p;
668
        sword16 rj = r[j];
669
        sword16 rjl = r[j + MLKEM_N / 2];
670
        sword16 t = (sword16)(rj + rjl);
671
        rjl = (sword16)(rj - rjl);
672
        p = (sword32)zeta * rjl;
673
        r[j] = (sword16)t;
674
        r[j + MLKEM_N / 2] = MLKEM_MONT_RED(p);
675
676
        p = (sword32)zeta2 * r[j];
677
        r[j] = MLKEM_MONT_RED(p);
678
        p = (sword32)zeta2 * r[j + MLKEM_N / 2];
679
        r[j + MLKEM_N / 2] = MLKEM_MONT_RED(p);
680
    }
681
#elif defined(WOLFSSL_MLKEM_INVNTT_UNROLL)
682
    /* Unroll len loop (Step 3). */
683
    unsigned int k;
684
    unsigned int j;
685
    unsigned int start;
686
    sword16 zeta;
687
    sword16 zeta2;
688
689
    k = 0;
690
    /* len = 2 */
691
    for (start = 0; start < MLKEM_N; start += 2 * 2) {
692
        zeta = zetas_inv[k++];
693
        for (j = 0; j < 2; ++j) {
694
            sword32 p;
695
            sword16 rj = r[start + j];
696
            sword16 rjl = r[start + j + 2];
697
            sword16 t = rj + rjl;
698
            r[start + j] = t;
699
            rjl = rj - rjl;
700
            p = (sword32)zeta * rjl;
701
            r[start + j + 2] = MLKEM_MONT_RED(p);
702
        }
703
    }
704
    /* len = 4 */
705
    for (start = 0; start < MLKEM_N; start += 2 * 4) {
706
        zeta = zetas_inv[k++];
707
        for (j = 0; j < 4; ++j) {
708
            sword32 p;
709
            sword16 rj = r[start + j];
710
            sword16 rjl = r[start + j + 4];
711
            sword16 t = rj + rjl;
712
            r[start + j] = t;
713
            rjl = rj - rjl;
714
            p = (sword32)zeta * rjl;
715
            r[start + j + 4] = MLKEM_MONT_RED(p);
716
        }
717
    }
718
    /* len = 8 */
719
    for (start = 0; start < MLKEM_N; start += 2 * 8) {
720
        zeta = zetas_inv[k++];
721
        for (j = 0; j < 8; ++j) {
722
            sword32 p;
723
            sword16 rj = r[start + j];
724
            sword16 rjl = r[start + j + 8];
725
            sword16 t = rj + rjl;
726
            /* Reduce. */
727
            r[start + j] = MLKEM_BARRETT_RED(t);
728
            rjl = rj - rjl;
729
            p = (sword32)zeta * rjl;
730
            r[start + j + 8] = MLKEM_MONT_RED(p);
731
        }
732
    }
733
    /* len = 16 */
734
    for (start = 0; start < MLKEM_N; start += 2 * 16) {
735
        zeta = zetas_inv[k++];
736
        for (j = 0; j < 16; ++j) {
737
            sword32 p;
738
            sword16 rj = r[start + j];
739
            sword16 rjl = r[start + j + 16];
740
            sword16 t = rj + rjl;
741
            r[start + j] = t;
742
            rjl = rj - rjl;
743
            p = (sword32)zeta * rjl;
744
            r[start + j + 16] = MLKEM_MONT_RED(p);
745
        }
746
    }
747
    /* len = 32 */
748
    for (start = 0; start < MLKEM_N; start += 2 * 32) {
749
        zeta = zetas_inv[k++];
750
        for (j = 0; j < 32; ++j) {
751
            sword32 p;
752
            sword16 rj = r[start + j];
753
            sword16 rjl = r[start + j + 32];
754
            sword16 t = rj + rjl;
755
            r[start + j] = t;
756
            rjl = rj - rjl;
757
            p = (sword32)zeta * rjl;
758
            r[start + j + 32] = MLKEM_MONT_RED(p);
759
        }
760
    }
761
    /* len = 64 */
762
    for (start = 0; start < MLKEM_N; start += 2 * 64) {
763
        zeta = zetas_inv[k++];
764
        for (j = 0; j < 64; ++j) {
765
            sword32 p;
766
            sword16 rj = r[start + j];
767
            sword16 rjl = r[start + j + 64];
768
            sword16 t = rj + rjl;
769
            /* Reduce. */
770
            r[start + j] = MLKEM_BARRETT_RED(t);
771
            rjl = rj - rjl;
772
            p = (sword32)zeta * rjl;
773
            r[start + j + 64] = MLKEM_MONT_RED(p);
774
        }
775
    }
776
    /* len = 128, 256 */
777
    zeta = zetas_inv[126];
778
    zeta2 = zetas_inv[127];
779
    for (j = 0; j < MLKEM_N / 2; ++j) {
780
        sword32 p;
781
        sword16 rj = r[j];
782
        sword16 rjl = r[j + MLKEM_N / 2];
783
        sword16 t = rj + rjl;
784
        rjl = rj - rjl;
785
        p = (sword32)zeta * rjl;
786
        r[j] = t;
787
        r[j + MLKEM_N / 2] = MLKEM_MONT_RED(p);
788
789
        p = (sword32)zeta2 * r[j];
790
        r[j] = MLKEM_MONT_RED(p);
791
        p = (sword32)zeta2 * r[j + MLKEM_N / 2];
792
        r[j + MLKEM_N / 2] = MLKEM_MONT_RED(p);
793
    }
794
#else
795
    /* Unroll len (2, 3, 3) and start loops. */
796
0
    unsigned int j;
797
0
    sword16 t0;
798
0
    sword16 t1;
799
0
    sword16 t2;
800
0
    sword16 t3;
801
0
    sword16 zeta64_0;
802
0
    sword16 zeta64_1;
803
0
    sword16 zeta128;
804
0
    sword16 zeta256;
805
0
    sword32 p;
806
807
0
    for (j = 0; j < MLKEM_N; j += 8) {
808
0
        sword16 zeta2_0 = zetas_inv[ 0 + j / 4 + 0];
809
0
        sword16 zeta2_1 = zetas_inv[ 0 + j / 4 + 1];
810
0
        sword16 zeta4   = zetas_inv[64 + j / 8 + 0];
811
0
        sword16 r0 = r[j + 0];
812
0
        sword16 r1 = r[j + 1];
813
0
        sword16 r2 = r[j + 2];
814
0
        sword16 r3 = r[j + 3];
815
0
        sword16 r4 = r[j + 4];
816
0
        sword16 r5 = r[j + 5];
817
0
        sword16 r6 = r[j + 6];
818
0
        sword16 r7 = r[j + 7];
819
820
0
        p = (sword32)zeta2_0 * (sword16)(r0 - r2);
821
0
        t0 = MLKEM_MONT_RED(p);
822
0
        p = (sword32)zeta2_0 * (sword16)(r1 - r3);
823
0
        t1 = MLKEM_MONT_RED(p);
824
0
        p = (sword32)zeta2_1 * (sword16)(r4 - r6);
825
0
        t2 = MLKEM_MONT_RED(p);
826
0
        p = (sword32)zeta2_1 * (sword16)(r5 - r7);
827
0
        t3 = MLKEM_MONT_RED(p);
828
0
        r0 = (sword16)(r0 + r2);
829
0
        r1 = (sword16)(r1 + r3);
830
0
        r4 = (sword16)(r4 + r6);
831
0
        r5 = (sword16)(r5 + r7);
832
0
        r2 = t0;
833
0
        r3 = t1;
834
0
        r6 = t2;
835
0
        r7 = t3;
836
837
0
        p = (sword32)zeta4 * (sword16)(r0 - r4);
838
0
        t0 = MLKEM_MONT_RED(p);
839
0
        p = (sword32)zeta4 * (sword16)(r1 - r5);
840
0
        t1 = MLKEM_MONT_RED(p);
841
0
        p = (sword32)zeta4 * (sword16)(r2 - r6);
842
0
        t2 = MLKEM_MONT_RED(p);
843
0
        p = (sword32)zeta4 * (sword16)(r3 - r7);
844
0
        t3 = MLKEM_MONT_RED(p);
845
0
        r0 = (sword16)(r0 + r4);
846
0
        r1 = (sword16)(r1 + r5);
847
0
        r2 = (sword16)(r2 + r6);
848
0
        r3 = (sword16)(r3 + r7);
849
0
        r4 = t0;
850
0
        r5 = t1;
851
0
        r6 = t2;
852
0
        r7 = t3;
853
854
0
        r[j + 0] = r0;
855
0
        r[j + 1] = r1;
856
0
        r[j + 2] = r2;
857
0
        r[j + 3] = r3;
858
0
        r[j + 4] = r4;
859
0
        r[j + 5] = r5;
860
0
        r[j + 6] = r6;
861
0
        r[j + 7] = r7;
862
0
    }
863
864
0
    for (j = 0; j < MLKEM_N; j += 64) {
865
0
        unsigned int i;
866
0
        sword16 zeta8_0  = zetas_inv[ 96 + j / 16 + 0];
867
0
        sword16 zeta8_1  = zetas_inv[ 96 + j / 16 + 1];
868
0
        sword16 zeta8_2  = zetas_inv[ 96 + j / 16 + 2];
869
0
        sword16 zeta8_3  = zetas_inv[ 96 + j / 16 + 3];
870
0
        sword16 zeta16_0 = zetas_inv[112 + j / 32 + 0];
871
0
        sword16 zeta16_1 = zetas_inv[112 + j / 32 + 1];
872
0
        sword16 zeta32   = zetas_inv[120 + j / 64 + 0];
873
0
        for (i = 0; i < 8; i++) {
874
0
            sword16 r0 = r[j + i +  0];
875
0
            sword16 r1 = r[j + i +  8];
876
0
            sword16 r2 = r[j + i + 16];
877
0
            sword16 r3 = r[j + i + 24];
878
0
            sword16 r4 = r[j + i + 32];
879
0
            sword16 r5 = r[j + i + 40];
880
0
            sword16 r6 = r[j + i + 48];
881
0
            sword16 r7 = r[j + i + 56];
882
883
0
            p = (sword32)zeta8_0 * (sword16)(r0 - r1);
884
0
            t0 = MLKEM_MONT_RED(p);
885
0
            p = (sword32)zeta8_1 * (sword16)(r2 - r3);
886
0
            t1 = MLKEM_MONT_RED(p);
887
0
            p = (sword32)zeta8_2 * (sword16)(r4 - r5);
888
0
            t2 = MLKEM_MONT_RED(p);
889
0
            p = (sword32)zeta8_3 * (sword16)(r6 - r7);
890
0
            t3 = MLKEM_MONT_RED(p);
891
0
            r0 = MLKEM_BARRETT_RED(r0 + r1);
892
0
            r2 = MLKEM_BARRETT_RED(r2 + r3);
893
0
            r4 = MLKEM_BARRETT_RED(r4 + r5);
894
0
            r6 = MLKEM_BARRETT_RED(r6 + r7);
895
0
            r1 = t0;
896
0
            r3 = t1;
897
0
            r5 = t2;
898
0
            r7 = t3;
899
900
0
            p = (sword32)zeta16_0 * (sword16)(r0 - r2);
901
0
            t0 = MLKEM_MONT_RED(p);
902
0
            p = (sword32)zeta16_0 * (sword16)(r1 - r3);
903
0
            t1 = MLKEM_MONT_RED(p);
904
0
            p = (sword32)zeta16_1 * (sword16)(r4 - r6);
905
0
            t2 = MLKEM_MONT_RED(p);
906
0
            p = (sword32)zeta16_1 * (sword16)(r5 - r7);
907
0
            t3 = MLKEM_MONT_RED(p);
908
0
            r0 = (sword16)(r0 + r2);
909
0
            r1 = (sword16)(r1 + r3);
910
0
            r4 = (sword16)(r4 + r6);
911
0
            r5 = (sword16)(r5 + r7);
912
0
            r2 = t0;
913
0
            r3 = t1;
914
0
            r6 = t2;
915
0
            r7 = t3;
916
917
0
            p = (sword32)zeta32 * (sword16)(r0 - r4);
918
0
            t0 = MLKEM_MONT_RED(p);
919
0
            p = (sword32)zeta32 * (sword16)(r1 - r5);
920
0
            t1 = MLKEM_MONT_RED(p);
921
0
            p = (sword32)zeta32 * (sword16)(r2 - r6);
922
0
            t2 = MLKEM_MONT_RED(p);
923
0
            p = (sword32)zeta32 * (sword16)(r3 - r7);
924
0
            t3 = MLKEM_MONT_RED(p);
925
0
            r0 = (sword16)(r0 + r4);
926
0
            r1 = (sword16)(r1 + r5);
927
0
            r2 = (sword16)(r2 + r6);
928
0
            r3 = (sword16)(r3 + r7);
929
0
            r4 = t0;
930
0
            r5 = t1;
931
0
            r6 = t2;
932
0
            r7 = t3;
933
934
0
            r[j + i +  0] = r0;
935
0
            r[j + i +  8] = r1;
936
0
            r[j + i + 16] = r2;
937
0
            r[j + i + 24] = r3;
938
0
            r[j + i + 32] = r4;
939
0
            r[j + i + 40] = r5;
940
0
            r[j + i + 48] = r6;
941
0
            r[j + i + 56] = r7;
942
0
        }
943
0
    }
944
945
0
    zeta64_0 = zetas_inv[124];
946
0
    zeta64_1 = zetas_inv[125];
947
0
    zeta128  = zetas_inv[126];
948
0
    zeta256  = zetas_inv[127];
949
0
    for (j = 0; j < MLKEM_N / 8; j++) {
950
0
        sword16 r0 = r[j +   0];
951
0
        sword16 r1 = r[j +  32];
952
0
        sword16 r2 = r[j +  64];
953
0
        sword16 r3 = r[j +  96];
954
0
        sword16 r4 = r[j + 128];
955
0
        sword16 r5 = r[j + 160];
956
0
        sword16 r6 = r[j + 192];
957
0
        sword16 r7 = r[j + 224];
958
959
0
        p = (sword32)zeta64_0 * (sword16)(r0 - r2);
960
0
        t0 = MLKEM_MONT_RED(p);
961
0
        p = (sword32)zeta64_0 * (sword16)(r1 - r3);
962
0
        t1 = MLKEM_MONT_RED(p);
963
0
        p = (sword32)zeta64_1 * (sword16)(r4 - r6);
964
0
        t2 = MLKEM_MONT_RED(p);
965
0
        p = (sword32)zeta64_1 * (sword16)(r5 - r7);
966
0
        t3 = MLKEM_MONT_RED(p);
967
0
        r0 = MLKEM_BARRETT_RED(r0 + r2);
968
0
        r1 = MLKEM_BARRETT_RED(r1 + r3);
969
0
        r4 = MLKEM_BARRETT_RED(r4 + r6);
970
0
        r5 = MLKEM_BARRETT_RED(r5 + r7);
971
0
        r2 = t0;
972
0
        r3 = t1;
973
0
        r6 = t2;
974
0
        r7 = t3;
975
976
0
        p = (sword32)zeta128 * (sword16)(r0 - r4);
977
0
        t0 = MLKEM_MONT_RED(p);
978
0
        p = (sword32)zeta128 * (sword16)(r1 - r5);
979
0
        t1 = MLKEM_MONT_RED(p);
980
0
        p = (sword32)zeta128 * (sword16)(r2 - r6);
981
0
        t2 = MLKEM_MONT_RED(p);
982
0
        p = (sword32)zeta128 * (sword16)(r3 - r7);
983
0
        t3 = MLKEM_MONT_RED(p);
984
0
        r0 = (sword16)(r0 + r4);
985
0
        r1 = (sword16)(r1 + r5);
986
0
        r2 = (sword16)(r2 + r6);
987
0
        r3 = (sword16)(r3 + r7);
988
0
        r4 = t0;
989
0
        r5 = t1;
990
0
        r6 = t2;
991
0
        r7 = t3;
992
993
0
        p = (sword32)zeta256 * r0;
994
0
        r0 = MLKEM_MONT_RED(p);
995
0
        p = (sword32)zeta256 * r1;
996
0
        r1 = MLKEM_MONT_RED(p);
997
0
        p = (sword32)zeta256 * r2;
998
0
        r2 = MLKEM_MONT_RED(p);
999
0
        p = (sword32)zeta256 * r3;
1000
0
        r3 = MLKEM_MONT_RED(p);
1001
0
        p = (sword32)zeta256 * r4;
1002
0
        r4 = MLKEM_MONT_RED(p);
1003
0
        p = (sword32)zeta256 * r5;
1004
0
        r5 = MLKEM_MONT_RED(p);
1005
0
        p = (sword32)zeta256 * r6;
1006
0
        r6 = MLKEM_MONT_RED(p);
1007
0
        p = (sword32)zeta256 * r7;
1008
0
        r7 = MLKEM_MONT_RED(p);
1009
1010
0
        r[j +   0] = r0;
1011
0
        r[j +  32] = r1;
1012
0
        r[j +  64] = r2;
1013
0
        r[j +  96] = r3;
1014
0
        r[j + 128] = r4;
1015
0
        r[j + 160] = r5;
1016
0
        r[j + 192] = r6;
1017
0
        r[j + 224] = r7;
1018
0
    }
1019
0
#endif
1020
0
}
1021
#endif
1022
1023
/* Multiplication of polynomials in Zq[X]/(X^2-zeta).
1024
 *
1025
 * Used for multiplication of elements in Rq in NTT domain.
1026
 *
1027
 * FIPS 203, Algorithm 12: BaseCaseMultiply(a0, a1, b0, b1, zeta)
1028
 * Computes the product of two degree-one polynomials with respect to a
1029
 * quadratic modulus.
1030
 *   1: c0 <- a0.b0 + a1.b1.zeta
1031
 *   2: c1 <- a0.b1 + a1.b0
1032
 *   3: return (c0, c1)
1033
 *
1034
 * @param  [out]  r     Result polynomial.
1035
 * @param  [in]   a     First factor.
1036
 * @param  [in]   b     Second factor.
1037
 * @param  [in]   zeta  Integer defining the reduction polynomial.
1038
 */
1039
static void mlkem_basemul(sword16* r, const sword16* a, const sword16* b,
1040
    sword16 zeta)
1041
5.30M
{
1042
5.30M
    sword16 r0;
1043
5.30M
    sword16 a0 = a[0];
1044
5.30M
    sword16 a1 = a[1];
1045
5.30M
    sword16 b0 = b[0];
1046
5.30M
    sword16 b1 = b[1];
1047
5.30M
    sword32 p1;
1048
5.30M
    sword32 p2;
1049
1050
    /* Step 1 */
1051
5.30M
    p1   = (sword32)a0 * b0;
1052
5.30M
    p2   = (sword32)a1 * b1;
1053
5.30M
    r0   = MLKEM_MONT_RED(p2);
1054
5.30M
    p2   = (sword32)zeta * r0;
1055
5.30M
    p2  += p1;
1056
5.30M
    r[0] = MLKEM_MONT_RED(p2);
1057
1058
    /* Step 2 */
1059
5.30M
    p1   = (sword32)a0 * b1;
1060
5.30M
    p2   = (sword32)a1 * b0;
1061
5.30M
    p1  += p2;
1062
5.30M
    r[1] = MLKEM_MONT_RED(p1);
1063
5.30M
}
1064
1065
/* Multiply two polynomials in NTT domain. r = a * b.
1066
 *
1067
 * FIPS 203, Algorithm 11: MultiplyNTTs(f_hat, g_hat)
1068
 * Computes the product (in the ring T_q) of two NTT representations.
1069
 *   1: for (i <- 0; i < 128; i++)
1070
 *   2:     (h_hat[2i],h_hat[2i+1]) <-
1071
 *              BaseCaseMultiply(f_hat[2i],f_hat[2i+1],g_hat[2i],g_hat[2i+1],
1072
 *                               zetas^(BitRev_7(i)+1))
1073
 *   3: end for
1074
 *   4: return h_hat
1075
 *
1076
 * @param  [out]  r  Result polynomial.
1077
 * @param  [in]   a  First polynomial multiplier.
1078
 * @param  [in]   b  Second polynomial multiplier.
1079
 */
1080
static void mlkem_basemul_mont(sword16* r, const sword16* a, const sword16* b)
1081
13.6k
{
1082
13.6k
    const sword16* zeta = zetas + 64;
1083
1084
#if defined(WOLFSSL_MLKEM_SMALL)
1085
    /* Two multiplications per loop. */
1086
    unsigned int i;
1087
    /* Step 1 */
1088
    for (i = 0; i < MLKEM_N; i += 4, zeta++) {
1089
        /* Step 2 */
1090
        mlkem_basemul(r + i + 0, a + i + 0, b + i + 0, zeta[0]);
1091
        mlkem_basemul(r + i + 2, a + i + 2, b + i + 2, (sword16)(-zeta[0]));
1092
    }
1093
#elif defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
1094
    /* Four multiplications per loop. */
1095
    unsigned int i;
1096
    for (i = 0; i < MLKEM_N; i += 8, zeta += 2) {
1097
        mlkem_basemul(r + i + 0, a + i + 0, b + i + 0, zeta[0]);
1098
        mlkem_basemul(r + i + 2, a + i + 2, b + i + 2, (sword16)(-zeta[0]));
1099
        mlkem_basemul(r + i + 4, a + i + 4, b + i + 4, zeta[1]);
1100
        mlkem_basemul(r + i + 6, a + i + 6, b + i + 6, (sword16)(-zeta[1]));
1101
    }
1102
#else
1103
    /* Eight multiplications per loop. */
1104
13.6k
    unsigned int i;
1105
231k
    for (i = 0; i < MLKEM_N; i += 16, zeta += 4) {
1106
217k
        mlkem_basemul(r + i +  0, a + i +  0, b + i +  0, zeta[0]);
1107
217k
        mlkem_basemul(r + i +  2, a + i +  2, b + i +  2, (sword16)(-zeta[0]));
1108
217k
        mlkem_basemul(r + i +  4, a + i +  4, b + i +  4, zeta[1]);
1109
217k
        mlkem_basemul(r + i +  6, a + i +  6, b + i +  6, (sword16)(-zeta[1]));
1110
217k
        mlkem_basemul(r + i +  8, a + i +  8, b + i +  8, zeta[2]);
1111
217k
        mlkem_basemul(r + i + 10, a + i + 10, b + i + 10, (sword16)(-zeta[2]));
1112
217k
        mlkem_basemul(r + i + 12, a + i + 12, b + i + 12, zeta[3]);
1113
217k
        mlkem_basemul(r + i + 14, a + i + 14, b + i + 14, (sword16)(-zeta[3]));
1114
217k
    }
1115
13.6k
#endif
1116
13.6k
}
1117
1118
/* Multiply two polynomials in NTT domain and add to result. r += a * b.
1119
 *
1120
 * FIPS 203, Algorithm 11: MultiplyNTTs(f_hat, g_hat)
1121
 * Computes the product (in the ring T_q) of two NTT representations.
1122
 *   1: for (i <- 0; i < 128; i++)
1123
 *   2:     (h_hat[2i],h_hat[2i+1]) <-
1124
 *              BaseCaseMultiply(f_hat[2i],f_hat[2i+1],g_hat[2i],g_hat[2i+1],
1125
 *                               zetas^(BitRev_7(i)+1))
1126
 *   3: end for
1127
 *   4: return h_hat
1128
 * Add h_hat to r.
1129
 *
1130
 * @param  [in, out]  r  Result polynomial.
1131
 * @param  [in]       a  First polynomial multiplier.
1132
 * @param  [in]       b  Second polynomial multiplier.
1133
 */
1134
static void mlkem_basemul_mont_add(sword16* r, const sword16* a,
1135
    const sword16* b)
1136
27.8k
{
1137
27.8k
    const sword16* zeta = zetas + 64;
1138
1139
#if defined(WOLFSSL_MLKEM_SMALL)
1140
    /* Two multiplications per loop. */
1141
    unsigned int i;
1142
    for (i = 0; i < MLKEM_N; i += 4, zeta++) {
1143
        sword16 t0[2];
1144
        sword16 t2[2];
1145
1146
        mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]);
1147
        mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0]));
1148
1149
        r[i + 0] = (sword16)(r[i + 0] + t0[0]);
1150
        r[i + 1] = (sword16)(r[i + 1] + t0[1]);
1151
        r[i + 2] = (sword16)(r[i + 2] + t2[0]);
1152
        r[i + 3] = (sword16)(r[i + 3] + t2[1]);
1153
    }
1154
#elif defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
1155
    /* Four multiplications per loop. */
1156
    unsigned int i;
1157
    for (i = 0; i < MLKEM_N; i += 8, zeta += 2) {
1158
        sword16 t0[2];
1159
        sword16 t2[2];
1160
        sword16 t4[2];
1161
        sword16 t6[2];
1162
1163
        mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]);
1164
        mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0]));
1165
        mlkem_basemul(t4, a + i + 4, b + i + 4, zeta[1]);
1166
        mlkem_basemul(t6, a + i + 6, b + i + 6, (sword16)(-zeta[1]));
1167
1168
        r[i + 0] = (sword16)(r[i + 0] + t0[0]);
1169
        r[i + 1] = (sword16)(r[i + 1] + t0[1]);
1170
        r[i + 2] = (sword16)(r[i + 2] + t2[0]);
1171
        r[i + 3] = (sword16)(r[i + 3] + t2[1]);
1172
        r[i + 4] = (sword16)(r[i + 4] + t4[0]);
1173
        r[i + 5] = (sword16)(r[i + 5] + t4[1]);
1174
        r[i + 6] = (sword16)(r[i + 6] + t6[0]);
1175
        r[i + 7] = (sword16)(r[i + 7] + t6[1]);
1176
    }
1177
#else
1178
    /* Eight multiplications per loop. */
1179
27.8k
    unsigned int i;
1180
472k
    for (i = 0; i < MLKEM_N; i += 16, zeta += 4) {
1181
444k
        sword16 t0[2];
1182
444k
        sword16 t2[2];
1183
444k
        sword16 t4[2];
1184
444k
        sword16 t6[2];
1185
444k
        sword16 t8[2];
1186
444k
        sword16 t10[2];
1187
444k
        sword16 t12[2];
1188
444k
        sword16 t14[2];
1189
1190
444k
        mlkem_basemul(t0, a + i + 0, b + i + 0, zeta[0]);
1191
444k
        mlkem_basemul(t2, a + i + 2, b + i + 2, (sword16)(-zeta[0]));
1192
444k
        mlkem_basemul(t4, a + i + 4, b + i + 4, zeta[1]);
1193
444k
        mlkem_basemul(t6, a + i + 6, b + i + 6, (sword16)(-zeta[1]));
1194
444k
        mlkem_basemul(t8, a + i + 8, b + i + 8, zeta[2]);
1195
444k
        mlkem_basemul(t10, a + i + 10, b + i + 10, (sword16)(-zeta[2]));
1196
444k
        mlkem_basemul(t12, a + i + 12, b + i + 12, zeta[3]);
1197
444k
        mlkem_basemul(t14, a + i + 14, b + i + 14, (sword16)(-zeta[3]));
1198
1199
444k
        r[i + 0] = (sword16)(r[i + 0] + t0[0]);
1200
444k
        r[i + 1] = (sword16)(r[i + 1] + t0[1]);
1201
444k
        r[i + 2] = (sword16)(r[i + 2] + t2[0]);
1202
444k
        r[i + 3] = (sword16)(r[i + 3] + t2[1]);
1203
444k
        r[i + 4] = (sword16)(r[i + 4] + t4[0]);
1204
444k
        r[i + 5] = (sword16)(r[i + 5] + t4[1]);
1205
444k
        r[i + 6] = (sword16)(r[i + 6] + t6[0]);
1206
444k
        r[i + 7] = (sword16)(r[i + 7] + t6[1]);
1207
444k
        r[i + 8] = (sword16)(r[i + 8] + t8[0]);
1208
444k
        r[i + 9] = (sword16)(r[i + 9] + t8[1]);
1209
444k
        r[i + 10] = (sword16)(r[i + 10] + t10[0]);
1210
444k
        r[i + 11] = (sword16)(r[i + 11] + t10[1]);
1211
444k
        r[i + 12] = (sword16)(r[i + 12] + t12[0]);
1212
444k
        r[i + 13] = (sword16)(r[i + 13] + t12[1]);
1213
444k
        r[i + 14] = (sword16)(r[i + 14] + t14[0]);
1214
444k
        r[i + 15] = (sword16)(r[i + 15] + t14[1]);
1215
444k
    }
1216
27.8k
#endif
1217
27.8k
}
1218
#endif
1219
1220
/* Pointwise multiply elements of a and b, into r, and multiply by 2^-16.
1221
 *
1222
 * @param  [out]  r  Result polynomial.
1223
 * @param  [in]   a  First vector polynomial to multiply with.
1224
 * @param  [in]   b  Second vector polynomial to multiply with.
1225
 * @param  [in]   k  Number of polynomials in vector.
1226
 */
1227
static void mlkem_pointwise_acc_mont(sword16* r, const sword16* a,
1228
    const sword16* b, unsigned int k)
1229
13.6k
{
1230
13.6k
    unsigned int i;
1231
1232
13.6k
    mlkem_basemul_mont(r, a, b);
1233
#ifdef WOLFSSL_MLKEM_SMALL
1234
    for (i = 1; i < k; ++i) {
1235
        mlkem_basemul_mont_add(r, a + i * MLKEM_N, b + i * MLKEM_N);
1236
    }
1237
#else
1238
27.8k
    for (i = 1; i < k - 1; ++i) {
1239
14.1k
        mlkem_basemul_mont_add(r, a + i * MLKEM_N, b + i * MLKEM_N);
1240
14.1k
    }
1241
13.6k
    mlkem_basemul_mont_add(r, a + (k - 1) * MLKEM_N, b + (k - 1) * MLKEM_N);
1242
13.6k
#endif
1243
13.6k
}
1244
1245
/******************************************************************************/
1246
1247
/* Initialize ML-KEM implementation.
1248
 */
1249
void mlkem_init(void)
1250
4.54k
{
1251
#if defined(USE_INTEL_SPEEDUP) || (defined(__aarch64__) && \
1252
    defined(WOLFSSL_ARMASM))
1253
    cpuid_get_flags_ex(&cpuid_flags);
1254
#endif
1255
4.54k
}
1256
1257
/******************************************************************************/
1258
1259
#if defined(__aarch64__) && defined(WOLFSSL_ARMASM)
1260
1261
/* The three-way Keccak helpers have a NEON implementation and one using the
1262
 * SHA-3 crypto extension instructions (EOR3/RAX1/XAR/BCAX). Those instructions
1263
 * are OPTIONAL in ARMv8.2 and are absent on Cortex-A55 parts such as the NXP
1264
 * i.MX95, so the choice has to be made from the CPU ID flags at run time -- the
1265
 * same way sha3.c selects between BlockSha3_crypto and BlockSha3_base.
1266
 * Selecting at build time made ML-KEM abort with SIGILL on any aarch64 CPU
1267
 * without FEAT_SHA3 whenever wolfSSL was configured
1268
 * --enable-armasm=sha3-crypto, even though SHA-3 itself fell back correctly.
1269
 */
1270
#ifdef WOLFSSL_ARMASM_CRYPTO_SHA3
1271
1272
static void mlkem_sha3_blocksx3(word64* state)
1273
{
1274
    if (IS_AARCH64_SHA3(cpuid_flags)) {
1275
        mlkem_sha3_blocksx3_crypto(state);
1276
    }
1277
    else {
1278
        mlkem_sha3_blocksx3_neon(state);
1279
    }
1280
}
1281
1282
static void mlkem_shake128_blocksx3_seed(word64* state, byte* seed)
1283
{
1284
    if (IS_AARCH64_SHA3(cpuid_flags)) {
1285
        mlkem_shake128_blocksx3_seed_crypto(state, seed);
1286
    }
1287
    else {
1288
        mlkem_shake128_blocksx3_seed_neon(state, seed);
1289
    }
1290
}
1291
1292
static void mlkem_shake256_blocksx3_seed(word64* state, byte* seed)
1293
{
1294
    if (IS_AARCH64_SHA3(cpuid_flags)) {
1295
        mlkem_shake256_blocksx3_seed_crypto(state, seed);
1296
    }
1297
    else {
1298
        mlkem_shake256_blocksx3_seed_neon(state, seed);
1299
    }
1300
}
1301
1302
#else
1303
1304
#define mlkem_sha3_blocksx3             mlkem_sha3_blocksx3_neon
1305
#define mlkem_shake128_blocksx3_seed    mlkem_shake128_blocksx3_seed_neon
1306
#define mlkem_shake256_blocksx3_seed    mlkem_shake256_blocksx3_seed_neon
1307
1308
#endif /* WOLFSSL_ARMASM_CRYPTO_SHA3 */
1309
1310
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
1311
/* Generate a public-private key pair from randomly generated data.
1312
 *
1313
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
1314
 *   ...
1315
 *   16: s_hat <- NTT(s)
1316
 *   17: e_hat <- NTT(e)
1317
 *   18: t_hat <- A_hat o s_hat + e_hat
1318
 *   ...
1319
 *
1320
 * @param  [in, out]  s  Private key vector of polynomials.
1321
 * @param  [out]      t  Public key vector of polynomials.
1322
 * @param  [in, out]  e  Error values as a vector of polynomials. Modified.
1323
 * @param  [in]       a  Random values in an array of vectors of polynomials.
1324
 * @param  [in]       k  Number of polynomials in vector.
1325
 */
1326
void mlkem_keygen(sword16* s, sword16* t, sword16* e, const sword16* a, int k)
1327
{
1328
    int i;
1329
1330
#ifndef WOLFSSL_AARCH64_NO_SQRDMLSH
1331
    if (IS_AARCH64_RDM(cpuid_flags)) {
1332
        /* Transform private key. All of result used in public key calculation.
1333
         * Step 16: s_hat = NTT(s) */
1334
        for (i = 0; i < k; ++i) {
1335
            mlkem_ntt_sqrdmlsh(s + i * MLKEM_N);
1336
        }
1337
1338
        /* For each polynomial in the vectors.
1339
         * Step 17, Step 18: Calculate public from A_hat, s_hat and e_hat. */
1340
        for (i = 0; i < k; ++i) {
1341
            /* Multiply a by private into public polynomial.
1342
             * Step 18: ... A_hat o s_hat ... */
1343
            mlkem_pointwise_acc_mont(t + i * MLKEM_N, a + i * k * MLKEM_N, s,
1344
                (unsigned int)k);
1345
            /* Convert public polynomial to Montgomery form.
1346
             * Step 18: ... MontRed(A_hat o s_hat) ... */
1347
            mlkem_to_mont_sqrdmlsh(t + i * MLKEM_N);
1348
            /* Transform error values polynomial.
1349
             * Step 17: e_hat = NTT(e) */
1350
            mlkem_ntt_sqrdmlsh(e + i * MLKEM_N);
1351
            /* Add errors to public key and reduce.
1352
             * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
1353
            mlkem_add_reduce(t + i * MLKEM_N, e + i * MLKEM_N);
1354
        }
1355
    }
1356
    else
1357
#endif
1358
    {
1359
        /* Transform private key. All of result used in public key calculation.
1360
         * Step 16: s_hat = NTT(s) */
1361
        for (i = 0; i < k; ++i) {
1362
            mlkem_ntt(s + i * MLKEM_N);
1363
        }
1364
1365
        /* For each polynomial in the vectors.
1366
         * Step 17, Step 18: Calculate public from A_hat, s_hat and e_hat. */
1367
        for (i = 0; i < k; ++i) {
1368
            /* Multiply a by private into public polynomial.
1369
             * Step 18: ... A_hat o s_hat ... */
1370
            mlkem_pointwise_acc_mont(t + i * MLKEM_N, a + i * k * MLKEM_N, s,
1371
                (unsigned int)k);
1372
            /* Convert public polynomial to Montgomery form.
1373
             * Step 18: ... MontRed(A_hat o s_hat) ... */
1374
            mlkem_to_mont(t + i * MLKEM_N);
1375
            /* Transform error values polynomial.
1376
             * Step 17: e_hat = NTT(e) */
1377
            mlkem_ntt(e + i * MLKEM_N);
1378
            /* Add errors to public key and reduce.
1379
             * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
1380
            mlkem_add_reduce(t + i * MLKEM_N, e + i * MLKEM_N);
1381
        }
1382
    }
1383
}
1384
#endif /* WOLFSSL_MLKEM_NO_MAKE_KEY */
1385
1386
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
1387
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
1388
/* Encapsulate message.
1389
 *
1390
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE, m, r)
1391
 *   ...
1392
 *   Step 18: y_hat <- NTT(y)
1393
 *   Step 19: u <- InvNTT(A_hat_trans o y_hat) + e_1
1394
 *   ...
1395
 *   Step 21: v <- InvNTT(t_hat_trans o y_hat) + e_2 + mu
1396
 *   ...
1397
 *
1398
 * @param  [in]       t   Public key vector of polynomials.
1399
 * @param  [out]      u   Vector of polynomials.
1400
 * @param  [out]      v   Polynomial.
1401
 * @param  [in]       a   Array of vector of polynomials.
1402
 * @param  [in, out]  y   Vector of polynomials.
1403
 * @param  [in]       e1  Error Vector of polynomials.
1404
 * @param  [in]       e2  Error polynomial.
1405
 * @param  [in]       m   Message polynomial.
1406
 * @param  [in]       k   Number of polynomials in vector.
1407
 */
1408
void mlkem_encapsulate(const sword16* t, sword16* u, sword16* v,
1409
    const sword16* a, sword16* y, const sword16* e1, const sword16* e2,
1410
    const sword16* m, int k)
1411
{
1412
    int i;
1413
1414
#ifndef WOLFSSL_AARCH64_NO_SQRDMLSH
1415
    if (IS_AARCH64_RDM(cpuid_flags)) {
1416
        /* Transform y. All of result used in calculation of u and v.
1417
         * Step 18: y_hat <- NTT(y) */
1418
        for (i = 0; i < k; ++i) {
1419
            mlkem_ntt_sqrdmlsh(y + i * MLKEM_N);
1420
        }
1421
1422
        /* For each polynomial in the vectors.
1423
         * Step 19: u <- InvNTT(A_hat_trans o y_hat) + e_1 */
1424
        for (i = 0; i < k; ++i) {
1425
            /* Multiply at by y into u polynomial.
1426
             * Step 19: ... A_hat_trans o y_hat ... */
1427
            mlkem_pointwise_acc_mont(u + i * MLKEM_N, a + i * k * MLKEM_N, y,
1428
                (unsigned int)k);
1429
            /* Inverse transform u polynomial.
1430
             * Step 19: ... InvNTT(A_hat_trans o y_hat) ... */
1431
            mlkem_invntt_sqrdmlsh(u + i * MLKEM_N);
1432
            /* Add errors to u and reduce.
1433
             * Step 19: u <- InvNTT(A_hat_trans o y_hat) + e_1 */
1434
            mlkem_add_reduce(u + i * MLKEM_N, e1 + i * MLKEM_N);
1435
        }
1436
1437
        /* Multiply public key by y into v polynomial.
1438
         * Step 21: ... t_hat_trans o y_hat ... */
1439
        mlkem_pointwise_acc_mont(v, t, y, (unsigned int)k);
1440
        /* Inverse transform v.
1441
         * Step 21: ... InvNTT(t_hat_trans o y_hat) ... */
1442
        mlkem_invntt_sqrdmlsh(v);
1443
    }
1444
    else
1445
#endif
1446
    {
1447
        /* Transform y. All of result used in calculation of u and v.
1448
         * Step 18: y_hat <- NTT(y) */
1449
        for (i = 0; i < k; ++i) {
1450
            mlkem_ntt(y + i * MLKEM_N);
1451
        }
1452
1453
        /* For each polynomial in the vectors.
1454
         * Step 19: u <- InvNTT(A_hat_trans o y_hat) + e_1 */
1455
        for (i = 0; i < k; ++i) {
1456
            /* Multiply at by y into u polynomial.
1457
             * Step 19: ... A_hat_trans o y_hat ... */
1458
            mlkem_pointwise_acc_mont(u + i * MLKEM_N, a + i * k * MLKEM_N, y,
1459
                (unsigned int)k);
1460
            /* Inverse transform u polynomial.
1461
             * Step 19: ... InvNTT(A_hat_trans o y_hat) ... */
1462
            mlkem_invntt(u + i * MLKEM_N);
1463
            /* Add errors to u and reduce.
1464
             * Step 19: u <- InvNTT(A_hat_trans o y_hat) + e_1 */
1465
            mlkem_add_reduce(u + i * MLKEM_N, e1 + i * MLKEM_N);
1466
        }
1467
1468
        /* Multiply public key by y into v polynomial.
1469
         * Step 21: ... t_hat_trans o y_hat ... */
1470
        mlkem_pointwise_acc_mont(v, t, y, (unsigned int)k);
1471
        /* Inverse transform v.
1472
         * Step 21: ... InvNTT(t_hat_trans o y_hat) ... */
1473
        mlkem_invntt(v);
1474
    }
1475
    /* Add errors and message to v and reduce.
1476
     * Step 21: v <- InvNTT(t_hat_trans o y_hat) + e_2 + mu */
1477
    mlkem_add3_reduce(v, e2, m);
1478
}
1479
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE || !WOLFSSL_MLKEM_NO_DECAPSULATE */
1480
1481
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
1482
/* Decapsulate message.
1483
 *
1484
 * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c)
1485
 * Uses the decryption key to decrypt a ciphertext.
1486
 *   ...
1487
 *   6: w <- v' - InvNTT(s_hat_trans o NTT(u'))
1488
 *   ...
1489
 *
1490
 * @param  [in]       s  Decryption key as vector of polynomials.
1491
 * @param  [out]      w  Message polynomial.
1492
 * @param  [in, out]  u  Vector of polynomials containing error.
1493
 * @param  [in]       v  Encapsulated message polynomial.
1494
 * @param  [in]       k  Number of polynomials in vector.
1495
 */
1496
void mlkem_decapsulate(const sword16* s, sword16* w, sword16* u,
1497
    const sword16* v, int k)
1498
{
1499
    int i;
1500
1501
#ifndef WOLFSSL_AARCH64_NO_SQRDMLSH
1502
    if (IS_AARCH64_RDM(cpuid_flags)) {
1503
        /* Transform u. All of result used in calculation of w.
1504
         * Step 6: ... NTT(u') */
1505
        for (i = 0; i < k; ++i) {
1506
            mlkem_ntt_sqrdmlsh(u + i * MLKEM_N);
1507
        }
1508
1509
        /* Multiply private key by u into w polynomial.
1510
         * Step 6: ... s_hat_trans o NTT(u') */
1511
        mlkem_pointwise_acc_mont(w, s, u, (unsigned int)k);
1512
        /* Inverse transform w.
1513
         * Step 6: ... InvNTT(s_hat_trans o NTT(u')) */
1514
        mlkem_invntt_sqrdmlsh(w);
1515
    }
1516
    else
1517
#endif
1518
    {
1519
        /* Transform u. All of result used in calculation of w.
1520
         * Step 6: ... NTT(u') */
1521
        for (i = 0; i < k; ++i) {
1522
            mlkem_ntt(u + i * MLKEM_N);
1523
        }
1524
1525
        /* Multiply private key by u into w polynomial.
1526
         * Step 6: ... s_hat_trans o NTT(u') */
1527
        mlkem_pointwise_acc_mont(w, s, u, (unsigned int)k);
1528
        /* Inverse transform w.
1529
         * Step 6: ... InvNTT(s_hat_trans o NTT(u')) */
1530
        mlkem_invntt(w);
1531
    }
1532
    /* Subtract errors (in w) out of v and reduce into w.
1533
     * Step 6: w <- v' - InvNTT(s_hat_trans o NTT(u')) */
1534
    mlkem_rsub_reduce(w, v);
1535
}
1536
#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */
1537
1538
#else
1539
1540
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
1541
1542
#if !defined(WOLFSSL_MLKEM_SMALL) && !defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
1543
/* Number-Theoretic Transform.
1544
 *
1545
 * FIPS 203, Algorithm 9: NTT(f)
1546
 * Computes the NTT representation f_hat of the given polynomial f element of
1547
 * R_q.
1548
 *   1: f_hat <- f
1549
 *   2: i <- 1
1550
 *   3: for (len <- 128; len >= 2; len <- len/2)
1551
 *   4:     for (start <- 0; start < 256; start <- start + 2.len)
1552
 *   5:         zeta <- zetas^BitRev_7(i) mod q
1553
 *   6:         i <- i + 1
1554
 *   7:         for (j <- start; j < start + len; j++)
1555
 *   8:             t <- zeta.f[j+len]
1556
 *   9:             f_hat[j+len] <- f_hat[j] - t
1557
 *  10:             f_hat[j] <- f_hat[j] + t
1558
 *  11:         end for
1559
 *  12:     end for
1560
 *  13: end for
1561
 *  14: return f_hat
1562
 *
1563
 * @param [in, out]  r  Polynomial to transform.
1564
 * @param [in, out]  a  Polynomial to add NTT result to.
1565
 */
1566
static void mlkem_ntt_add_to(sword16* r, sword16* a)
1567
13.6k
{
1568
#if defined(WOLFSSL_MLKEM_NTT_UNROLL)
1569
    /* Unroll len loop (Step 3). */
1570
    unsigned int k = 1;
1571
    unsigned int j;
1572
    unsigned int start;
1573
    sword16 zeta = zetas[k++];
1574
1575
    /* len = 128 */
1576
    for (j = 0; j < MLKEM_N / 2; ++j) {
1577
        sword32 p = (sword32)zeta * r[j + MLKEM_N / 2];
1578
        sword16 t = MLKEM_MONT_RED(p);
1579
        sword16 rj = r[j];
1580
        r[j + MLKEM_N / 2] = rj - t;
1581
        r[j] = rj + t;
1582
    }
1583
    /* len = 64 */
1584
    for (start = 0; start < MLKEM_N; start += 2 * 64) {
1585
        zeta = zetas[k++];
1586
        for (j = 0; j < 64; ++j) {
1587
            sword32 p = (sword32)zeta * r[start + j + 64];
1588
            sword16 t = MLKEM_MONT_RED(p);
1589
            sword16 rj = r[start + j];
1590
            r[start + j + 64] = rj - t;
1591
            r[start + j] = rj + t;
1592
        }
1593
    }
1594
    /* len = 32 */
1595
    for (start = 0; start < MLKEM_N; start += 2 * 32) {
1596
        zeta = zetas[k++];
1597
        for (j = 0; j < 32; ++j) {
1598
            sword32 p = (sword32)zeta * r[start + j + 32];
1599
            sword16 t = MLKEM_MONT_RED(p);
1600
            sword16 rj = r[start + j];
1601
            r[start + j + 32] = rj - t;
1602
            r[start + j] = rj + t;
1603
        }
1604
    }
1605
    /* len = 16 */
1606
    for (start = 0; start < MLKEM_N; start += 2 * 16) {
1607
        zeta = zetas[k++];
1608
        for (j = 0; j < 16; ++j) {
1609
            sword32 p = (sword32)zeta * r[start + j + 16];
1610
            sword16 t = MLKEM_MONT_RED(p);
1611
            sword16 rj = r[start + j];
1612
            r[start + j + 16] = rj - t;
1613
            r[start + j] = rj + t;
1614
        }
1615
    }
1616
    /* len = 8 */
1617
    for (start = 0; start < MLKEM_N; start += 2 * 8) {
1618
        zeta = zetas[k++];
1619
        for (j = 0; j < 8; ++j) {
1620
            sword32 p = (sword32)zeta * r[start + j + 8];
1621
            sword16 t = MLKEM_MONT_RED(p);
1622
            sword16 rj = r[start + j];
1623
            r[start + j + 8] = rj - t;
1624
            r[start + j] = rj + t;
1625
        }
1626
    }
1627
    /* len = 4 */
1628
    for (start = 0; start < MLKEM_N; start += 2 * 4) {
1629
        zeta = zetas[k++];
1630
        for (j = 0; j < 4; ++j) {
1631
            sword32 p = (sword32)zeta * r[start + j + 4];
1632
            sword16 t = MLKEM_MONT_RED(p);
1633
            sword16 rj = r[start + j];
1634
            r[start + j + 4] = rj - t;
1635
            r[start + j] = rj + t;
1636
        }
1637
    }
1638
    /* len = 2 */
1639
    for (start = 0; start < MLKEM_N; start += 2 * 2) {
1640
        zeta = zetas[k++];
1641
        for (j = 0; j < 2; ++j) {
1642
            sword32 p = (sword32)zeta * r[start + j + 2];
1643
            sword16 t = MLKEM_MONT_RED(p);
1644
            sword16 rj = r[start + j];
1645
            r[start + j + 2] = rj - t;
1646
            r[start + j] = rj + t;
1647
        }
1648
    }
1649
    /* Reduce coefficients with quick algorithm. */
1650
    for (j = 0; j < MLKEM_N; ++j) {
1651
        sword16 t = a[j] + r[j];
1652
        a[j] = MLKEM_BARRETT_RED(t);
1653
    }
1654
#else /* !WOLFSSL_MLKEM_NTT_UNROLL */
1655
    /* Unroll len (2, 3, 2) and start loops. */
1656
13.6k
    unsigned int j;
1657
13.6k
    sword16 t0;
1658
13.6k
    sword16 t1;
1659
13.6k
    sword16 t2;
1660
13.6k
    sword16 t3;
1661
1662
    /* len = 128,64 */
1663
13.6k
    sword16 zeta128 = zetas[1];
1664
13.6k
    sword16 zeta64_0 = zetas[2];
1665
13.6k
    sword16 zeta64_1 = zetas[3];
1666
449k
    for (j = 0; j < MLKEM_N / 8; j++) {
1667
435k
        sword16 r0 = r[j +   0];
1668
435k
        sword16 r1 = r[j +  32];
1669
435k
        sword16 r2 = r[j +  64];
1670
435k
        sword16 r3 = r[j +  96];
1671
435k
        sword16 r4 = r[j + 128];
1672
435k
        sword16 r5 = r[j + 160];
1673
435k
        sword16 r6 = r[j + 192];
1674
435k
        sword16 r7 = r[j + 224];
1675
1676
435k
        t0 = MLKEM_MONT_RED((sword32)zeta128 * r4);
1677
435k
        t1 = MLKEM_MONT_RED((sword32)zeta128 * r5);
1678
435k
        t2 = MLKEM_MONT_RED((sword32)zeta128 * r6);
1679
435k
        t3 = MLKEM_MONT_RED((sword32)zeta128 * r7);
1680
435k
        r4 = (sword16)(r0 - t0);
1681
435k
        r5 = (sword16)(r1 - t1);
1682
435k
        r6 = (sword16)(r2 - t2);
1683
435k
        r7 = (sword16)(r3 - t3);
1684
435k
        r0 = (sword16)(r0 + t0);
1685
435k
        r1 = (sword16)(r1 + t1);
1686
435k
        r2 = (sword16)(r2 + t2);
1687
435k
        r3 = (sword16)(r3 + t3);
1688
1689
435k
        t0 = MLKEM_MONT_RED((sword32)zeta64_0 * r2);
1690
435k
        t1 = MLKEM_MONT_RED((sword32)zeta64_0 * r3);
1691
435k
        t2 = MLKEM_MONT_RED((sword32)zeta64_1 * r6);
1692
435k
        t3 = MLKEM_MONT_RED((sword32)zeta64_1 * r7);
1693
435k
        r2 = (sword16)(r0 - t0);
1694
435k
        r3 = (sword16)(r1 - t1);
1695
435k
        r6 = (sword16)(r4 - t2);
1696
435k
        r7 = (sword16)(r5 - t3);
1697
435k
        r0 = (sword16)(r0 + t0);
1698
435k
        r1 = (sword16)(r1 + t1);
1699
435k
        r4 = (sword16)(r4 + t2);
1700
435k
        r5 = (sword16)(r5 + t3);
1701
1702
435k
        r[j +   0] = r0;
1703
435k
        r[j +  32] = r1;
1704
435k
        r[j +  64] = r2;
1705
435k
        r[j +  96] = r3;
1706
435k
        r[j + 128] = r4;
1707
435k
        r[j + 160] = r5;
1708
435k
        r[j + 192] = r6;
1709
435k
        r[j + 224] = r7;
1710
435k
    }
1711
1712
    /* len = 32,16,8 */
1713
68.0k
    for (j = 0; j < MLKEM_N; j += 64) {
1714
54.4k
        unsigned int i;
1715
54.4k
        sword16 zeta32   = zetas[ 4 + j / 64 + 0];
1716
54.4k
        sword16 zeta16_0 = zetas[ 8 + j / 32 + 0];
1717
54.4k
        sword16 zeta16_1 = zetas[ 8 + j / 32 + 1];
1718
54.4k
        sword16 zeta8_0  = zetas[16 + j / 16 + 0];
1719
54.4k
        sword16 zeta8_1  = zetas[16 + j / 16 + 1];
1720
54.4k
        sword16 zeta8_2  = zetas[16 + j / 16 + 2];
1721
54.4k
        sword16 zeta8_3  = zetas[16 + j / 16 + 3];
1722
490k
        for (i = 0; i < 8; i++) {
1723
435k
            sword16 r0 = r[j + i +  0];
1724
435k
            sword16 r1 = r[j + i +  8];
1725
435k
            sword16 r2 = r[j + i + 16];
1726
435k
            sword16 r3 = r[j + i + 24];
1727
435k
            sword16 r4 = r[j + i + 32];
1728
435k
            sword16 r5 = r[j + i + 40];
1729
435k
            sword16 r6 = r[j + i + 48];
1730
435k
            sword16 r7 = r[j + i + 56];
1731
1732
435k
            t0 = MLKEM_MONT_RED((sword32)zeta32 * r4);
1733
435k
            t1 = MLKEM_MONT_RED((sword32)zeta32 * r5);
1734
435k
            t2 = MLKEM_MONT_RED((sword32)zeta32 * r6);
1735
435k
            t3 = MLKEM_MONT_RED((sword32)zeta32 * r7);
1736
435k
            r4 = (sword16)(r0 - t0);
1737
435k
            r5 = (sword16)(r1 - t1);
1738
435k
            r6 = (sword16)(r2 - t2);
1739
435k
            r7 = (sword16)(r3 - t3);
1740
435k
            r0 = (sword16)(r0 + t0);
1741
435k
            r1 = (sword16)(r1 + t1);
1742
435k
            r2 = (sword16)(r2 + t2);
1743
435k
            r3 = (sword16)(r3 + t3);
1744
1745
435k
            t0 = MLKEM_MONT_RED((sword32)zeta16_0 * r2);
1746
435k
            t1 = MLKEM_MONT_RED((sword32)zeta16_0 * r3);
1747
435k
            t2 = MLKEM_MONT_RED((sword32)zeta16_1 * r6);
1748
435k
            t3 = MLKEM_MONT_RED((sword32)zeta16_1 * r7);
1749
435k
            r2 = (sword16)(r0 - t0);
1750
435k
            r3 = (sword16)(r1 - t1);
1751
435k
            r6 = (sword16)(r4 - t2);
1752
435k
            r7 = (sword16)(r5 - t3);
1753
435k
            r0 = (sword16)(r0 + t0);
1754
435k
            r1 = (sword16)(r1 + t1);
1755
435k
            r4 = (sword16)(r4 + t2);
1756
435k
            r5 = (sword16)(r5 + t3);
1757
1758
435k
            t0 = MLKEM_MONT_RED((sword32)zeta8_0 * r1);
1759
435k
            t1 = MLKEM_MONT_RED((sword32)zeta8_1 * r3);
1760
435k
            t2 = MLKEM_MONT_RED((sword32)zeta8_2 * r5);
1761
435k
            t3 = MLKEM_MONT_RED((sword32)zeta8_3 * r7);
1762
435k
            r1 = (sword16)(r0 - t0);
1763
435k
            r3 = (sword16)(r2 - t1);
1764
435k
            r5 = (sword16)(r4 - t2);
1765
435k
            r7 = (sword16)(r6 - t3);
1766
435k
            r0 = (sword16)(r0 + t0);
1767
435k
            r2 = (sword16)(r2 + t1);
1768
435k
            r4 = (sword16)(r4 + t2);
1769
435k
            r6 = (sword16)(r6 + t3);
1770
1771
435k
            r[j + i +  0] = r0;
1772
435k
            r[j + i +  8] = r1;
1773
435k
            r[j + i + 16] = r2;
1774
435k
            r[j + i + 24] = r3;
1775
435k
            r[j + i + 32] = r4;
1776
435k
            r[j + i + 40] = r5;
1777
435k
            r[j + i + 48] = r6;
1778
435k
            r[j + i + 56] = r7;
1779
435k
        }
1780
54.4k
    }
1781
1782
    /* len = 4,2 and Final reduction */
1783
449k
    for (j = 0; j < MLKEM_N; j += 8) {
1784
435k
        sword16 zeta4  = zetas[32 + j / 8 + 0];
1785
435k
        sword16 zeta2_0 = zetas[64 + j / 4 + 0];
1786
435k
        sword16 zeta2_1 = zetas[64 + j / 4 + 1];
1787
435k
        sword16 r0 = r[j + 0];
1788
435k
        sword16 r1 = r[j + 1];
1789
435k
        sword16 r2 = r[j + 2];
1790
435k
        sword16 r3 = r[j + 3];
1791
435k
        sword16 r4 = r[j + 4];
1792
435k
        sword16 r5 = r[j + 5];
1793
435k
        sword16 r6 = r[j + 6];
1794
435k
        sword16 r7 = r[j + 7];
1795
1796
435k
        t0 = MLKEM_MONT_RED((sword32)zeta4 * r4);
1797
435k
        t1 = MLKEM_MONT_RED((sword32)zeta4 * r5);
1798
435k
        t2 = MLKEM_MONT_RED((sword32)zeta4 * r6);
1799
435k
        t3 = MLKEM_MONT_RED((sword32)zeta4 * r7);
1800
435k
        r4 = (sword16)(r0 - t0);
1801
435k
        r5 = (sword16)(r1 - t1);
1802
435k
        r6 = (sword16)(r2 - t2);
1803
435k
        r7 = (sword16)(r3 - t3);
1804
435k
        r0 = (sword16)(r0 + t0);
1805
435k
        r1 = (sword16)(r1 + t1);
1806
435k
        r2 = (sword16)(r2 + t2);
1807
435k
        r3 = (sword16)(r3 + t3);
1808
1809
435k
        t0 = MLKEM_MONT_RED((sword32)zeta2_0 * r2);
1810
435k
        t1 = MLKEM_MONT_RED((sword32)zeta2_0 * r3);
1811
435k
        t2 = MLKEM_MONT_RED((sword32)zeta2_1 * r6);
1812
435k
        t3 = MLKEM_MONT_RED((sword32)zeta2_1 * r7);
1813
435k
        r2 = (sword16)(r0 - t0);
1814
435k
        r3 = (sword16)(r1 - t1);
1815
435k
        r6 = (sword16)(r4 - t2);
1816
435k
        r7 = (sword16)(r5 - t3);
1817
435k
        r0 = (sword16)(r0 + t0);
1818
435k
        r1 = (sword16)(r1 + t1);
1819
435k
        r4 = (sword16)(r4 + t2);
1820
435k
        r5 = (sword16)(r5 + t3);
1821
1822
435k
        r0 = (sword16)(r0 + a[j + 0]);
1823
435k
        r1 = (sword16)(r1 + a[j + 1]);
1824
435k
        r2 = (sword16)(r2 + a[j + 2]);
1825
435k
        r3 = (sword16)(r3 + a[j + 3]);
1826
435k
        r4 = (sword16)(r4 + a[j + 4]);
1827
435k
        r5 = (sword16)(r5 + a[j + 5]);
1828
435k
        r6 = (sword16)(r6 + a[j + 6]);
1829
435k
        r7 = (sword16)(r7 + a[j + 7]);
1830
1831
435k
        a[j + 0] = MLKEM_BARRETT_RED(r0);
1832
435k
        a[j + 1] = MLKEM_BARRETT_RED(r1);
1833
435k
        a[j + 2] = MLKEM_BARRETT_RED(r2);
1834
435k
        a[j + 3] = MLKEM_BARRETT_RED(r3);
1835
435k
        a[j + 4] = MLKEM_BARRETT_RED(r4);
1836
435k
        a[j + 5] = MLKEM_BARRETT_RED(r5);
1837
435k
        a[j + 6] = MLKEM_BARRETT_RED(r6);
1838
435k
        a[j + 7] = MLKEM_BARRETT_RED(r7);
1839
435k
    }
1840
13.6k
#endif /* !WOLFSSL_MLKEM_NTT_UNROLL */
1841
13.6k
}
1842
#endif /* !WOLFSSL_MLKEM_SMALL && !WOLFSSL_MLKEM_NO_LARGE_CODE */
1843
1844
#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM
1845
/* Generate a public-private key pair from randomly generated data.
1846
 *
1847
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
1848
 *   ...
1849
 *   16: s_hat <- NTT(s)
1850
 *   17: e_hat <- NTT(e)
1851
 *   18: t_hat <- A_hat o s_hat + e_hat
1852
 *   ...
1853
 *
1854
 * @param  [in, out]  s  Private key vector of polynomials.
1855
 * @param  [out]      t  Public key vector of polynomials.
1856
 * @param  [in, out]  e  Error values as a vector of polynomials. Modified.
1857
 * @param  [in]       a  Random values in an array of vectors of polynomials.
1858
 * @param  [in]       k  Number of polynomials in vector.
1859
 */
1860
static void mlkem_keygen_c(sword16* s, sword16* t, sword16* e, const sword16* a,
1861
    int k)
1862
4.49k
{
1863
4.49k
    int i;
1864
1865
    /* Transform private key. All of result used in public key calculation
1866
     * Step 16: s_hat = NTT(s) */
1867
18.1k
    for (i = 0; i < k; ++i) {
1868
13.6k
        mlkem_ntt(s + i * MLKEM_N);
1869
13.6k
    }
1870
1871
    /* For each polynomial in the vectors.
1872
     * Step 17, Step 18: Calculate public from A_hat, s_hat and e_hat. */
1873
18.1k
    for (i = 0; i < k; ++i) {
1874
13.6k
        int j;
1875
1876
        /* Multiply a by private into public polynomial.
1877
         * Step 18: ... A_hat o s_hat ... */
1878
13.6k
        mlkem_pointwise_acc_mont(t + i * MLKEM_N, a + i * k * MLKEM_N, s,
1879
13.6k
            (unsigned int)k);
1880
        /* Convert public polynomial to Montgomery form.
1881
         * Step 18: ... MontRed(A_hat o s_hat) ... */
1882
3.49M
        for (j = 0; j < MLKEM_N; ++j) {
1883
3.48M
            sword32 n = t[i * MLKEM_N + j] * (sword32)MLKEM_F;
1884
3.48M
            t[i * MLKEM_N + j] = MLKEM_MONT_RED(n);
1885
3.48M
        }
1886
        /* Transform error values polynomial.
1887
         * Step 17: e_hat = NTT(e) */
1888
#if defined(WOLFSSL_MLKEM_SMALL) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
1889
        mlkem_ntt(e + i * MLKEM_N);
1890
        /* Add errors to public key and reduce.
1891
         * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
1892
        for (j = 0; j < MLKEM_N; ++j) {
1893
            sword16 n = (sword16)(t[i * MLKEM_N + j] + e[i * MLKEM_N + j]);
1894
            t[i * MLKEM_N + j] = MLKEM_BARRETT_RED(n);
1895
        }
1896
#else
1897
        /* Add errors to public key and reduce.
1898
         * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
1899
13.6k
        mlkem_ntt_add_to(e + i * MLKEM_N, t + i * MLKEM_N);
1900
13.6k
#endif
1901
13.6k
    }
1902
4.49k
}
1903
1904
/* Generate a public-private key pair from randomly generated data.
1905
 *
1906
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
1907
 *   ...
1908
 *   16: s_hat <- NTT(s)
1909
 *   17: e_hat <- NTT(e)
1910
 *   18: t_hat <- A_hat o s_hat + e_hat
1911
 *   ...
1912
 *
1913
 * @param  [in, out]  s  Private key vector of polynomials.
1914
 * @param  [out]      t  Public key vector of polynomials.
1915
 * @param  [in, out]  e  Error values as a vector of polynomials. Modified.
1916
 * @param  [in]       a  Random values in an array of vectors of polynomials.
1917
 * @param  [in]       k  Number of polynomials in vector.
1918
 */
1919
void mlkem_keygen(sword16* s, sword16* t, sword16* e, const sword16* a, int k)
1920
4.49k
{
1921
#ifdef USE_INTEL_SPEEDUP
1922
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
1923
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
1924
        /* Alg 13: Steps 16-18 */
1925
        mlkem_keygen_avx512(s, t, e, a, k);
1926
        RESTORE_VECTOR_REGISTERS();
1927
    }
1928
    else
1929
#endif
1930
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
1931
        /* Alg 13: Steps 16-18 */
1932
        mlkem_keygen_avx2(s, t, e, a, k);
1933
        RESTORE_VECTOR_REGISTERS();
1934
    }
1935
    else
1936
#endif
1937
4.49k
    {
1938
        /* Alg 13: Steps 16-18 */
1939
4.49k
        mlkem_keygen_c(s, t, e, a, k);
1940
4.49k
    }
1941
4.49k
}
1942
1943
#else /* WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM */
1944
1945
/* Generate a public-private key pair from randomly generated data.
1946
 *
1947
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
1948
 *   3: for (i <- 0; i < k; i++)                         > generate matrix A_hat
1949
 *   ... (generate A[i])
1950
 *   7: end for
1951
 *   ...
1952
 *  13:      e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
1953
 *   ...
1954
 *  16: s_hat <- NTT(s)
1955
 *  17: e_hat <- NTT(e)
1956
 *  18: t_hat <- A_hat o s_hat + e_hat
1957
 *   ...
1958
 *
1959
 * @param  [in, out]  s      Private key vector of polynomials.
1960
 * @param  [out]      t      Public key vector of polynomials.
1961
 * @param  [in, out]  prf    XOF object.
1962
 * @param  [in]       tv     Temporary vector of polynomials.
1963
 * @param  [in]       k      Number of polynomials in vector.
1964
 * @param  [in]       rho    Random seed to generate matrix A from.
1965
 * @param  [in, out]  sigma  Random seed to generate noise from.
1966
 * @return  0 on success.
1967
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
1968
 *          WOLFSSL_SMALL_STACK is defined.
1969
 * @return  Other negative value when a hash error occurred.
1970
 */
1971
int mlkem_keygen_seeds(sword16* s, sword16* t, MLKEM_PRF_T* prf,
1972
    sword16* tv, int k, byte* rho, byte* sigma)
1973
{
1974
    int i;
1975
    int ret = 0;
1976
    sword16* ai = tv;
1977
    sword16* e = tv;
1978
1979
    /* Transform private key. All of result used in public key calculation
1980
     * Step 16: s_hat = NTT(s) */
1981
    for (i = 0; i < k; ++i) {
1982
        mlkem_ntt(s + i * MLKEM_N);
1983
    }
1984
1985
    /* For each polynomial in the vectors.
1986
     * Step 17, Step 18: Calculate public from A_hat, s_hat and e_hat. */
1987
    for (i = 0; i < k; ++i) {
1988
        int j;
1989
1990
        /* Generate a vector of matrix A.
1991
         * Steps 4-6: generate A[i] */
1992
        ret = mlkem_gen_matrix_i(prf, ai, k, rho, i, 0);
1993
        if (ret != 0) {
1994
           break;
1995
        }
1996
1997
        /* Multiply a by private into public polynomial.
1998
         * Step 18: ... A_hat o s_hat ... */
1999
        mlkem_pointwise_acc_mont(t + i * MLKEM_N, ai, s, (unsigned int)k);
2000
        /* Convert public polynomial to Montgomery form.
2001
         * Step 18: ... MontRed(A_hat o s_hat) ... */
2002
        for (j = 0; j < MLKEM_N; ++j) {
2003
            sword32 n = t[i * MLKEM_N + j] * (sword32)MLKEM_F;
2004
            t[i * MLKEM_N + j] = MLKEM_MONT_RED(n);
2005
        }
2006
2007
        /* Generate noise using PRF.
2008
         * Step 13: e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N)) */
2009
        ret = mlkem_get_noise_i(prf, k, e, sigma, i, 1);
2010
        if (ret != 0) {
2011
           break;
2012
        }
2013
        /* Transform error values polynomial.
2014
         * Step 17: e_hat = NTT(e) */
2015
#if defined(WOLFSSL_MLKEM_SMALL) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
2016
        mlkem_ntt(e);
2017
        /* Add errors to public key and reduce.
2018
         * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
2019
        for (j = 0; j < MLKEM_N; ++j) {
2020
            sword16 n = (sword16)(t[i * MLKEM_N + j] + e[j]);
2021
            t[i * MLKEM_N + j] = MLKEM_BARRETT_RED(n);
2022
        }
2023
#else
2024
        /* Add errors to public key and reduce.
2025
         * Step 18: t_hat = BarrettRed(MontRed(A_hat o s_hat) + e_hat) */
2026
        mlkem_ntt_add_to(e, t + i * MLKEM_N);
2027
#endif
2028
    }
2029
2030
    return ret;
2031
}
2032
2033
#endif /* WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM */
2034
#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY */
2035
2036
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
2037
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
2038
#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM
2039
/* Encapsulate message.
2040
 *
2041
 * @param  [in]       pub  Public key vector of polynomials.
2042
 * @param  [out]      u    Vector of polynomials.
2043
 * @param  [out]      v    Polynomial.
2044
 * @param  [in]       a    Array of vector of polynomials.
2045
 * @param  [in, out]  y    Vector of polynomials.
2046
 * @param  [in]       e1   Error Vector of polynomials.
2047
 * @param  [in]       e2   Error polynomial.
2048
 * @param  [in]       m    Message polynomial.
2049
 * @param  [in]       k    Number of polynomials in vector.
2050
 */
2051
static void mlkem_encapsulate_c(const sword16* pub, sword16* u, sword16* v,
2052
    const sword16* a, sword16* y, const sword16* e1, const sword16* e2,
2053
    const sword16* m, int k)
2054
0
{
2055
0
    int i;
2056
2057
    /* Transform y. All of result used in calculation of u and v. */
2058
0
    for (i = 0; i < k; ++i) {
2059
0
        mlkem_ntt(y + i * MLKEM_N);
2060
0
    }
2061
2062
    /* For each polynomial in the vectors. */
2063
0
    for (i = 0; i < k; ++i) {
2064
0
        int j;
2065
2066
        /* Multiply at by y into u polynomial. */
2067
0
        mlkem_pointwise_acc_mont(u + i * MLKEM_N, a + i * k * MLKEM_N, y,
2068
0
            (unsigned int)k);
2069
        /* Inverse transform u polynomial. */
2070
0
        mlkem_invntt(u + i * MLKEM_N);
2071
        /* Add errors to u and reduce. */
2072
#if defined(WOLFSSL_MLKEM_SMALL) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
2073
        for (j = 0; j < MLKEM_N; ++j) {
2074
            sword16 t = (sword16)(u[i * MLKEM_N + j] + e1[i * MLKEM_N + j]);
2075
            u[i * MLKEM_N + j] = MLKEM_BARRETT_RED(t);
2076
        }
2077
#else
2078
0
        for (j = 0; j < MLKEM_N; j += 8) {
2079
0
            sword16 t0 = (sword16)(u[i * MLKEM_N + j + 0] +
2080
0
                                   e1[i * MLKEM_N + j + 0]);
2081
0
            sword16 t1 = (sword16)(u[i * MLKEM_N + j + 1] +
2082
0
                                   e1[i * MLKEM_N + j + 1]);
2083
0
            sword16 t2 = (sword16)(u[i * MLKEM_N + j + 2] +
2084
0
                                   e1[i * MLKEM_N + j + 2]);
2085
0
            sword16 t3 = (sword16)(u[i * MLKEM_N + j + 3] +
2086
0
                                   e1[i * MLKEM_N + j + 3]);
2087
0
            sword16 t4 = (sword16)(u[i * MLKEM_N + j + 4] +
2088
0
                                   e1[i * MLKEM_N + j + 4]);
2089
0
            sword16 t5 = (sword16)(u[i * MLKEM_N + j + 5] +
2090
0
                                   e1[i * MLKEM_N + j + 5]);
2091
0
            sword16 t6 = (sword16)(u[i * MLKEM_N + j + 6] +
2092
0
                                   e1[i * MLKEM_N + j + 6]);
2093
0
            sword16 t7 = (sword16)(u[i * MLKEM_N + j + 7] +
2094
0
                                   e1[i * MLKEM_N + j + 7]);
2095
0
            u[i * MLKEM_N + j + 0] = MLKEM_BARRETT_RED(t0);
2096
0
            u[i * MLKEM_N + j + 1] = MLKEM_BARRETT_RED(t1);
2097
0
            u[i * MLKEM_N + j + 2] = MLKEM_BARRETT_RED(t2);
2098
0
            u[i * MLKEM_N + j + 3] = MLKEM_BARRETT_RED(t3);
2099
0
            u[i * MLKEM_N + j + 4] = MLKEM_BARRETT_RED(t4);
2100
0
            u[i * MLKEM_N + j + 5] = MLKEM_BARRETT_RED(t5);
2101
0
            u[i * MLKEM_N + j + 6] = MLKEM_BARRETT_RED(t6);
2102
0
            u[i * MLKEM_N + j + 7] = MLKEM_BARRETT_RED(t7);
2103
0
        }
2104
0
#endif
2105
0
    }
2106
2107
    /* Multiply public key by y into v polynomial. */
2108
0
    mlkem_pointwise_acc_mont(v, pub, y, (unsigned int)k);
2109
    /* Inverse transform v. */
2110
0
    mlkem_invntt(v);
2111
    /* Add errors and message to v and reduce. */
2112
0
    for (i = 0; i < MLKEM_N; ++i) {
2113
0
        sword16 t = (sword16)(v[i] + e2[i] + m[i]);
2114
0
        v[i] = MLKEM_BARRETT_RED(t);
2115
0
    }
2116
0
}
2117
2118
/* Encapsulate message.
2119
 *
2120
 * @param  [in]       pub  Public key vector of polynomials.
2121
 * @param  [out]      u    Vector of polynomials.
2122
 * @param  [out]      v    Polynomial.
2123
 * @param  [in]       a    Array of vector of polynomials.
2124
 * @param  [in, out]  y    Vector of polynomials.
2125
 * @param  [in]       e1   Error Vector of polynomials.
2126
 * @param  [in]       e2   Error polynomial.
2127
 * @param  [in]       m    Message polynomial.
2128
 * @param  [in]       k    Number of polynomials in vector.
2129
 */
2130
void mlkem_encapsulate(const sword16* pub, sword16* u, sword16* v,
2131
    const sword16* a, sword16* y, const sword16* e1, const sword16* e2,
2132
    const sword16* m, int k)
2133
0
{
2134
#ifdef USE_INTEL_SPEEDUP
2135
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2136
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
2137
        mlkem_encapsulate_avx512(pub, u, v, a, y, e1, e2, m, k);
2138
        RESTORE_VECTOR_REGISTERS();
2139
    }
2140
    else
2141
#endif
2142
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
2143
        mlkem_encapsulate_avx2(pub, u, v, a, y, e1, e2, m, k);
2144
        RESTORE_VECTOR_REGISTERS();
2145
    }
2146
    else
2147
#endif
2148
0
    {
2149
0
        mlkem_encapsulate_c(pub, u, v, a, y, e1, e2, m, k);
2150
0
    }
2151
0
}
2152
2153
#else
2154
2155
/* Encapsulate message.
2156
 *
2157
 * @param  [in]       pub    Public key vector of polynomials.
2158
 * @param  [in, out]  prf    XOF object.
2159
 * @param  [out]      u      Vector of polynomials.
2160
 * @param  [in, out]  tp     Polynomial.
2161
 * @param  [in, out]  y      Vector of polynomials.
2162
 * @param  [in]       k      Number of polynomials in vector.
2163
 * @param  [in]       msg    Message to encapsulate.
2164
 * @param  [in]       seed   Random seed to generate matrix A from.
2165
 * @param  [in, out]  coins  Random seed to generate noise from.
2166
 * @return  0 on success.
2167
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2168
 *          WOLFSSL_SMALL_STACK is defined.
2169
 * @return  Other negative value when a hash error occurred.
2170
 */
2171
int mlkem_encapsulate_seeds(const sword16* pub, MLKEM_PRF_T* prf, sword16* u,
2172
    sword16* tp, sword16* y, int k, const byte* msg, byte* seed, byte* coins)
2173
{
2174
    int ret = 0;
2175
    int i;
2176
    sword16* a = tp;
2177
    sword16* e1 = tp;
2178
    sword16* v = tp;
2179
    sword16* e2 = tp + MLKEM_N;
2180
    sword16* m = y;
2181
2182
    /* Transform y. All of result used in calculation of u and v. */
2183
    for (i = 0; i < k; ++i) {
2184
        mlkem_ntt(y + i * MLKEM_N);
2185
    }
2186
2187
    /* For each polynomial in the vectors. */
2188
    for (i = 0; i < k; ++i) {
2189
        int j;
2190
2191
        /* Generate a vector of matrix A. */
2192
        ret = mlkem_gen_matrix_i(prf, a, k, seed, i, 1);
2193
        if (ret != 0) {
2194
           break;
2195
        }
2196
2197
        /* Multiply at by y into u polynomial. */
2198
        mlkem_pointwise_acc_mont(u + i * MLKEM_N, a, y, (unsigned int)k);
2199
        /* Inverse transform u polynomial. */
2200
        mlkem_invntt(u + i * MLKEM_N);
2201
2202
        /* Generate noise using PRF. */
2203
        ret = mlkem_get_noise_i(prf, k, e1, coins, i, 0);
2204
        if (ret != 0) {
2205
           break;
2206
        }
2207
        /* Add errors to u and reduce. */
2208
#if defined(WOLFSSL_MLKEM_SMALL) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
2209
        for (j = 0; j < MLKEM_N; ++j) {
2210
            sword16 t = (sword16)(u[i * MLKEM_N + j] + e1[j]);
2211
            u[i * MLKEM_N + j] = MLKEM_BARRETT_RED(t);
2212
        }
2213
#else
2214
        for (j = 0; j < MLKEM_N; j += 8) {
2215
            sword16 t0 = (sword16)(u[i * MLKEM_N + j + 0] + e1[j + 0]);
2216
            sword16 t1 = (sword16)(u[i * MLKEM_N + j + 1] + e1[j + 1]);
2217
            sword16 t2 = (sword16)(u[i * MLKEM_N + j + 2] + e1[j + 2]);
2218
            sword16 t3 = (sword16)(u[i * MLKEM_N + j + 3] + e1[j + 3]);
2219
            sword16 t4 = (sword16)(u[i * MLKEM_N + j + 4] + e1[j + 4]);
2220
            sword16 t5 = (sword16)(u[i * MLKEM_N + j + 5] + e1[j + 5]);
2221
            sword16 t6 = (sword16)(u[i * MLKEM_N + j + 6] + e1[j + 6]);
2222
            sword16 t7 = (sword16)(u[i * MLKEM_N + j + 7] + e1[j + 7]);
2223
            u[i * MLKEM_N + j + 0] = MLKEM_BARRETT_RED(t0);
2224
            u[i * MLKEM_N + j + 1] = MLKEM_BARRETT_RED(t1);
2225
            u[i * MLKEM_N + j + 2] = MLKEM_BARRETT_RED(t2);
2226
            u[i * MLKEM_N + j + 3] = MLKEM_BARRETT_RED(t3);
2227
            u[i * MLKEM_N + j + 4] = MLKEM_BARRETT_RED(t4);
2228
            u[i * MLKEM_N + j + 5] = MLKEM_BARRETT_RED(t5);
2229
            u[i * MLKEM_N + j + 6] = MLKEM_BARRETT_RED(t6);
2230
            u[i * MLKEM_N + j + 7] = MLKEM_BARRETT_RED(t7);
2231
        }
2232
#endif
2233
    }
2234
2235
    /* Multiply public key by y into v polynomial. */
2236
    mlkem_pointwise_acc_mont(v, pub, y, (unsigned int)k);
2237
    /* Inverse transform v. */
2238
    mlkem_invntt(v);
2239
2240
    mlkem_from_msg(m, msg);
2241
2242
    /* Generate noise using PRF. */
2243
    coins[WC_ML_KEM_SYM_SZ] = WC_OCTET(2 * k);
2244
    ret = mlkem_get_noise_eta2_c(prf, e2, coins);
2245
    if (ret == 0) {
2246
        /* Add errors and message to v and reduce. */
2247
    #if defined(WOLFSSL_MLKEM_SMALL) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE)
2248
        for (i = 0; i < MLKEM_N; ++i) {
2249
            sword16 t = (sword16)(v[i] + e2[i] + m[i]);
2250
            v[i] = MLKEM_BARRETT_RED(t);
2251
        }
2252
    #else
2253
        for (i = 0; i < MLKEM_N; i += 8) {
2254
            sword16 t0 = (sword16)(v[i + 0] + e2[i + 0] + m[i + 0]);
2255
            sword16 t1 = (sword16)(v[i + 1] + e2[i + 1] + m[i + 1]);
2256
            sword16 t2 = (sword16)(v[i + 2] + e2[i + 2] + m[i + 2]);
2257
            sword16 t3 = (sword16)(v[i + 3] + e2[i + 3] + m[i + 3]);
2258
            sword16 t4 = (sword16)(v[i + 4] + e2[i + 4] + m[i + 4]);
2259
            sword16 t5 = (sword16)(v[i + 5] + e2[i + 5] + m[i + 5]);
2260
            sword16 t6 = (sword16)(v[i + 6] + e2[i + 6] + m[i + 6]);
2261
            sword16 t7 = (sword16)(v[i + 7] + e2[i + 7] + m[i + 7]);
2262
            v[i + 0] = MLKEM_BARRETT_RED(t0);
2263
            v[i + 1] = MLKEM_BARRETT_RED(t1);
2264
            v[i + 2] = MLKEM_BARRETT_RED(t2);
2265
            v[i + 3] = MLKEM_BARRETT_RED(t3);
2266
            v[i + 4] = MLKEM_BARRETT_RED(t4);
2267
            v[i + 5] = MLKEM_BARRETT_RED(t5);
2268
            v[i + 6] = MLKEM_BARRETT_RED(t6);
2269
            v[i + 7] = MLKEM_BARRETT_RED(t7);
2270
        }
2271
    #endif
2272
    }
2273
2274
    return ret;
2275
}
2276
#endif
2277
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE || !WOLFSSL_MLKEM_NO_DECAPSULATE */
2278
2279
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
2280
2281
/* Decapsulate message.
2282
 *
2283
 * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c)
2284
 * Uses the decryption key to decrypt a ciphertext.
2285
 *   ...
2286
 *   6: w <- v' - InvNTT(s_hat_trans o NTT(u'))
2287
 *   ...
2288
 *
2289
 * @param  [in]       s  Private key vector of polynomials.
2290
 * @param  [out]      w  Message polynomial.
2291
 * @param  [in, out]  u  Vector of polynomials containing error.
2292
 * @param  [in]       v  Encapsulated message polynomial.
2293
 * @param  [in]       k  Number of polynomials in vector.
2294
 */
2295
static void mlkem_decapsulate_c(const sword16* s, sword16* w, sword16* u,
2296
    const sword16* v, int k)
2297
0
{
2298
0
    int i;
2299
2300
    /* Transform u. All of result used in calculation of w.
2301
     * Step 6: ... NTT(u') */
2302
0
    for (i = 0; i < k; ++i) {
2303
0
        mlkem_ntt(u + i * MLKEM_N);
2304
0
    }
2305
2306
    /* Multiply private key by u into w polynomial.
2307
     * Step 6: ... s_hat_trans o NTT(u') */
2308
0
    mlkem_pointwise_acc_mont(w, s, u, (unsigned int)k);
2309
    /* Inverse transform w.
2310
     * Step 6: ... InvNTT(s_hat_trans o NTT(u')) */
2311
0
    mlkem_invntt(w);
2312
    /* Subtract errors (in w) out of v and reduce into w.
2313
     * Step 6: w <- v' - InvNTT(s_hat_trans o NTT(u')) */
2314
0
    for (i = 0; i < MLKEM_N; ++i) {
2315
0
        sword16 t = (sword16)(v[i] - w[i]);
2316
0
        w[i] = MLKEM_BARRETT_RED(t);
2317
0
    }
2318
0
}
2319
2320
/* Decapsulate message.
2321
 *
2322
 * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c)
2323
 * Uses the decryption key to decrypt a ciphertext.
2324
 *   ...
2325
 *   6: w <- v' - InvNTT(s_hat_trans o NTT(u'))
2326
 *   ...
2327
 *
2328
 * @param  [in]       s  Private key vector of polynomials.
2329
 * @param  [out]      w  Message polynomial.
2330
 * @param  [in, out]  u  Vector of polynomials containing error.
2331
 * @param  [in]       v  Encapsulated message polynomial.
2332
 * @param  [in]       k  Number of polynomials in vector.
2333
 */
2334
void mlkem_decapsulate(const sword16* s, sword16* w, sword16* u,
2335
    const sword16* v, int k)
2336
0
{
2337
#ifdef USE_INTEL_SPEEDUP
2338
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2339
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
2340
        mlkem_decapsulate_avx512(s, w, u, v, k);
2341
        RESTORE_VECTOR_REGISTERS();
2342
    }
2343
    else
2344
#endif
2345
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
2346
        mlkem_decapsulate_avx2(s, w, u, v, k);
2347
        RESTORE_VECTOR_REGISTERS();
2348
    }
2349
    else
2350
#endif
2351
0
    {
2352
0
        mlkem_decapsulate_c(s, w, u, v, k);
2353
0
    }
2354
0
}
2355
2356
#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */
2357
#endif
2358
2359
/******************************************************************************/
2360
2361
#if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
2362
2363
/* Rejection sampling used by the matrix generators. Dispatches to the fastest
2364
 * available left-pack variant, all producing identical output for the
2365
 * multiple-of-3 input lengths the matrix generator uses:
2366
 *   VBMI + VBMI2 : vpermb decode + vpcompressw   (_avx512_vbmi_vbmi2)
2367
 *   VBMI2        : vpermd decode + vpcompressw    (_avx512_vbmi2)
2368
 *   VBMI         : vpermb decode + vpcompressd    (_avx512_vbmi)
2369
 *   AVX512F/BW   : vpermd decode + vpcompressd    (_avx512)
2370
 *   otherwise    : the AVX2 sampler
2371
 * Only vpcompressw is AVX512-VBMI2; parts without it (e.g. Skylake-X, Cascade
2372
 * Lake) still get an AVX512F/BW sampler rather than falling back to AVX2. */
2373
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2374
static WC_INLINE unsigned int mlkem_rej_uniform_n_ins(sword16* p,
2375
    unsigned int len, const byte* r, unsigned int rLen)
2376
{
2377
    if (USE_INTEL_AVX512(cpuid_flags)) {
2378
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI2
2379
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
2380
        if (IS_INTEL_AVX512_VBMI(cpuid_flags) &&
2381
                IS_INTEL_AVX512_VBMI2(cpuid_flags)) {
2382
            return mlkem_rej_uniform_n_avx512_vbmi_vbmi2(p, len, r, rLen);
2383
        }
2384
#endif
2385
        if (IS_INTEL_AVX512_VBMI2(cpuid_flags)) {
2386
            return mlkem_rej_uniform_n_avx512_vbmi2(p, len, r, rLen);
2387
        }
2388
#endif
2389
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
2390
        if (IS_INTEL_AVX512_VBMI(cpuid_flags)) {
2391
            return mlkem_rej_uniform_n_avx512_vbmi(p, len, r, rLen);
2392
        }
2393
#endif
2394
        return mlkem_rej_uniform_n_avx512(p, len, r, rLen);
2395
    }
2396
    return mlkem_rej_uniform_n_avx2(p, len, r, rLen);
2397
}
2398
static WC_INLINE unsigned int mlkem_rej_uniform_ins(sword16* p,
2399
    unsigned int len, const byte* r, unsigned int rLen)
2400
{
2401
    if (USE_INTEL_AVX512(cpuid_flags)) {
2402
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI2
2403
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
2404
        if (IS_INTEL_AVX512_VBMI(cpuid_flags) &&
2405
                IS_INTEL_AVX512_VBMI2(cpuid_flags)) {
2406
            return mlkem_rej_uniform_avx512_vbmi_vbmi2(p, len, r, rLen);
2407
        }
2408
#endif
2409
        if (IS_INTEL_AVX512_VBMI2(cpuid_flags)) {
2410
            return mlkem_rej_uniform_avx512_vbmi2(p, len, r, rLen);
2411
        }
2412
#endif
2413
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
2414
        if (IS_INTEL_AVX512_VBMI(cpuid_flags)) {
2415
            return mlkem_rej_uniform_avx512_vbmi(p, len, r, rLen);
2416
        }
2417
#endif
2418
        return mlkem_rej_uniform_avx512(p, len, r, rLen);
2419
    }
2420
    return mlkem_rej_uniform_avx2(p, len, r, rLen);
2421
}
2422
#else
2423
#define mlkem_rej_uniform_n_ins  mlkem_rej_uniform_n_avx2
2424
#define mlkem_rej_uniform_ins    mlkem_rej_uniform_avx2
2425
#endif
2426
2427
/* Keccak-x4 output redistribution: dispatch to AVX512 when available. */
2428
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2429
#define MLKEM_REDIST_INS(N)                                                    \
2430
static WC_INLINE void mlkem_redistribute_##N##_rand_ins(const word64* s,       \
2431
    byte* r0, byte* r1, byte* r2, byte* r3)                                    \
2432
{                                                                              \
2433
    if (USE_INTEL_AVX512(cpuid_flags)) {                                       \
2434
        mlkem_redistribute_##N##_rand_avx512(s, r0, r1, r2, r3);               \
2435
        return;                                                                \
2436
    }                                                                          \
2437
    mlkem_redistribute_##N##_rand_avx2(s, r0, r1, r2, r3);                     \
2438
}
2439
MLKEM_REDIST_INS(8)
2440
MLKEM_REDIST_INS(16)
2441
MLKEM_REDIST_INS(17)
2442
MLKEM_REDIST_INS(21)
2443
#else
2444
#define mlkem_redistribute_8_rand_ins   mlkem_redistribute_8_rand_avx2
2445
#define mlkem_redistribute_16_rand_ins  mlkem_redistribute_16_rand_avx2
2446
#define mlkem_redistribute_17_rand_ins  mlkem_redistribute_17_rand_avx2
2447
#define mlkem_redistribute_21_rand_ins  mlkem_redistribute_21_rand_avx2
2448
#endif
2449
2450
/* CBD noise sampling: dispatch to AVX512 when available. */
2451
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2452
static WC_INLINE void mlkem_cbd_eta2_ins(sword16* p, const byte* r)
2453
{
2454
    if (USE_INTEL_AVX512(cpuid_flags)) {
2455
        mlkem_cbd_eta2_avx512(p, r);
2456
        return;
2457
    }
2458
    mlkem_cbd_eta2_avx2(p, r);
2459
}
2460
static WC_INLINE void mlkem_cbd_eta3_ins(sword16* p, const byte* r)
2461
{
2462
    if (USE_INTEL_AVX512(cpuid_flags)) {
2463
        mlkem_cbd_eta3_avx512(p, r);
2464
        return;
2465
    }
2466
    mlkem_cbd_eta3_avx2(p, r);
2467
}
2468
#else
2469
#define mlkem_cbd_eta2_ins  mlkem_cbd_eta2_avx2
2470
#define mlkem_cbd_eta3_ins  mlkem_cbd_eta3_avx2
2471
#endif
2472
2473
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
2474
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
2475
 *
2476
 * Seed used with XOF to generate random bytes.
2477
 *
2478
 * @param  [out]  a           Matrix of uniform integers.
2479
 * @param  [in]   seed        Bytes to seed XOF generation.
2480
 * @param  [in]   transposed  Whether A or A^T is generated.
2481
 * @return  0 on success.
2482
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2483
 *          WOLFSSL_SMALL_STACK is defined.
2484
 */
2485
static int mlkem_gen_matrix_k2_avx2(sword16* a, byte* seed, int transposed)
2486
{
2487
    int i;
2488
#ifdef WOLFSSL_SMALL_STACK
2489
    byte *rand = NULL;
2490
    word64 *state = NULL;
2491
#else
2492
    byte rand[4 * GEN_MATRIX_SIZE + 4];
2493
    word64 state[25 * 4];
2494
#endif
2495
    unsigned int ctr0;
2496
    unsigned int ctr1;
2497
    unsigned int ctr2;
2498
    unsigned int ctr3;
2499
    byte* p;
2500
2501
#ifdef WOLFSSL_SMALL_STACK
2502
    rand = (byte*)XMALLOC(4 * GEN_MATRIX_SIZE + 4, NULL,
2503
                          DYNAMIC_TYPE_TMP_BUFFER);
2504
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 4, NULL,
2505
                          DYNAMIC_TYPE_TMP_BUFFER);
2506
    if ((rand == NULL) || (state == NULL)) {
2507
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2508
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2509
        return MEMORY_E;
2510
    }
2511
#endif
2512
2513
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
2514
    rand[4 * GEN_MATRIX_SIZE + 0] = 0xff;
2515
    rand[4 * GEN_MATRIX_SIZE + 1] = 0xff;
2516
    rand[4 * GEN_MATRIX_SIZE + 2] = 0xff;
2517
    rand[4 * GEN_MATRIX_SIZE + 3] = 0xff;
2518
2519
    if (!transposed) {
2520
        state[4*4 + 0] = 0x1f0000 + 0x000;
2521
        state[4*4 + 1] = 0x1f0000 + 0x001;
2522
        state[4*4 + 2] = 0x1f0000 + 0x100;
2523
        state[4*4 + 3] = 0x1f0000 + 0x101;
2524
    }
2525
    else {
2526
        state[4*4 + 0] = 0x1f0000 + 0x000;
2527
        state[4*4 + 1] = 0x1f0000 + 0x100;
2528
        state[4*4 + 2] = 0x1f0000 + 0x001;
2529
        state[4*4 + 3] = 0x1f0000 + 0x101;
2530
    }
2531
2532
    sha3_128_blocksx4_seed_avx2(state, seed);
2533
    mlkem_redistribute_21_rand_ins(state, rand + 0 * GEN_MATRIX_SIZE,
2534
        rand + 1 * GEN_MATRIX_SIZE, rand + 2 * GEN_MATRIX_SIZE,
2535
        rand + 3 * GEN_MATRIX_SIZE);
2536
    for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2537
        sha3_blocksx4_avx2(state);
2538
        mlkem_redistribute_21_rand_ins(state, rand + i + 0 * GEN_MATRIX_SIZE,
2539
            rand + i + 1 * GEN_MATRIX_SIZE, rand + i + 2 * GEN_MATRIX_SIZE,
2540
            rand + i + 3 * GEN_MATRIX_SIZE);
2541
    }
2542
2543
    /* Sample random bytes to create a polynomial. */
2544
    p = rand;
2545
    ctr0 = mlkem_rej_uniform_n_ins(a + 0 * MLKEM_N, MLKEM_N, p,
2546
        GEN_MATRIX_SIZE);
2547
    p += GEN_MATRIX_SIZE;
2548
    ctr1 = mlkem_rej_uniform_n_ins(a + 1 * MLKEM_N, MLKEM_N, p,
2549
        GEN_MATRIX_SIZE);
2550
    p += GEN_MATRIX_SIZE;
2551
    ctr2 = mlkem_rej_uniform_n_ins(a + 2 * MLKEM_N, MLKEM_N, p,
2552
        GEN_MATRIX_SIZE);
2553
    p += GEN_MATRIX_SIZE;
2554
    ctr3 = mlkem_rej_uniform_n_ins(a + 3 * MLKEM_N, MLKEM_N, p,
2555
        GEN_MATRIX_SIZE);
2556
    /* Create more blocks if too many rejected. */
2557
    while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N) ||
2558
           (ctr3 < MLKEM_N)) {
2559
        sha3_blocksx4_avx2(state);
2560
        mlkem_redistribute_21_rand_ins(state, rand + 0 * GEN_MATRIX_SIZE,
2561
            rand + 1 * GEN_MATRIX_SIZE, rand + 2 * GEN_MATRIX_SIZE,
2562
            rand + 3 * GEN_MATRIX_SIZE);
2563
2564
        p = rand;
2565
        ctr0 += mlkem_rej_uniform_ins(a + 0 * MLKEM_N + ctr0, MLKEM_N - ctr0,
2566
            p, XOF_BLOCK_SIZE);
2567
        p += GEN_MATRIX_SIZE;
2568
        ctr1 += mlkem_rej_uniform_ins(a + 1 * MLKEM_N + ctr1, MLKEM_N - ctr1,
2569
            p, XOF_BLOCK_SIZE);
2570
        p += GEN_MATRIX_SIZE;
2571
        ctr2 += mlkem_rej_uniform_ins(a + 2 * MLKEM_N + ctr2, MLKEM_N - ctr2,
2572
            p, XOF_BLOCK_SIZE);
2573
        p += GEN_MATRIX_SIZE;
2574
        ctr3 += mlkem_rej_uniform_ins(a + 3 * MLKEM_N + ctr3, MLKEM_N - ctr3,
2575
            p, XOF_BLOCK_SIZE);
2576
    }
2577
2578
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2579
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2580
2581
    return 0;
2582
}
2583
2584
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2585
/* Deterministically generate a 2x2 matrix (or transpose) of uniform integers
2586
 * mod q using the eight-way AVX-512 SHA3 core. Only four of the eight lanes
2587
 * are used - the register-resident eight-way permutation is still faster than
2588
 * the memory-based four-way one, so wasting four lanes is a net win.
2589
 *
2590
 * @param  [out]  a           Matrix of uniform integers.
2591
 * @param  [in]   seed        Bytes to seed XOF generation.
2592
 * @param  [in]   transposed  Whether A or A^T is generated.
2593
 * @return  0 on success.
2594
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2595
 *          WOLFSSL_SMALL_STACK is defined.
2596
 */
2597
static int mlkem_gen_matrix_k2_avx512(sword16* a, byte* seed, int transposed)
2598
{
2599
    int i;
2600
#ifdef WOLFSSL_SMALL_STACK
2601
    byte *rand = NULL;
2602
    word64 *state = NULL;
2603
#else
2604
    byte rand[8 * GEN_MATRIX_SIZE + 4];
2605
    word64 state[25 * 8];
2606
#endif
2607
    unsigned int ctr[4];
2608
2609
#ifdef WOLFSSL_SMALL_STACK
2610
    rand = (byte*)XMALLOC(8 * GEN_MATRIX_SIZE + 4, NULL,
2611
                          DYNAMIC_TYPE_TMP_BUFFER);
2612
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 8, NULL,
2613
                          DYNAMIC_TYPE_TMP_BUFFER);
2614
    if ((rand == NULL) || (state == NULL)) {
2615
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2616
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2617
        return MEMORY_E;
2618
    }
2619
#endif
2620
2621
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
2622
    rand[8 * GEN_MATRIX_SIZE + 0] = 0xff;
2623
    rand[8 * GEN_MATRIX_SIZE + 1] = 0xff;
2624
    rand[8 * GEN_MATRIX_SIZE + 2] = 0xff;
2625
    rand[8 * GEN_MATRIX_SIZE + 3] = 0xff;
2626
2627
    /* Four used lanes hold the 2x2 matrix; lanes 4..7 are unused. */
2628
    for (i = 0; i < 4; i++) {
2629
        int row = i / 2;
2630
        int col = i % 2;
2631
        if (!transposed) {
2632
            state[4*8 + i] = (word32)(0x1f0000 + (row << 8) + col);
2633
        }
2634
        else {
2635
            state[4*8 + i] = (word32)(0x1f0000 + (col << 8) + row);
2636
        }
2637
    }
2638
    for (i = 4; i < 8; i++) {
2639
        state[4*8 + i] = 0x1f0000;
2640
    }
2641
2642
    sha3_128_blocksx8_seed_avx512(state, seed);
2643
    mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
2644
    for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2645
        sha3_blocksx8_avx512(state);
2646
        mlkem_redistribute_21_rand_x8_avx512(state, rand + i, GEN_MATRIX_SIZE);
2647
    }
2648
2649
    for (i = 0; i < 4; i++) {
2650
        ctr[i] = mlkem_rej_uniform_n_ins(a + i * MLKEM_N, MLKEM_N,
2651
            rand + i * GEN_MATRIX_SIZE, GEN_MATRIX_SIZE);
2652
    }
2653
    while ((ctr[0] < MLKEM_N) || (ctr[1] < MLKEM_N) || (ctr[2] < MLKEM_N) ||
2654
           (ctr[3] < MLKEM_N)) {
2655
        sha3_blocksx8_avx512(state);
2656
        mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
2657
        for (i = 0; i < 4; i++) {
2658
            ctr[i] += mlkem_rej_uniform_ins(a + i * MLKEM_N + ctr[i],
2659
                MLKEM_N - ctr[i], rand + i * GEN_MATRIX_SIZE, XOF_BLOCK_SIZE);
2660
        }
2661
    }
2662
2663
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2664
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2665
2666
    return 0;
2667
}
2668
#endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */
2669
#endif
2670
2671
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
2672
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
2673
 *
2674
 * Seed used with XOF to generate random bytes.
2675
 *
2676
 * @param  [out]  a           Matrix of uniform integers.
2677
 * @param  [in]   seed        Bytes to seed XOF generation.
2678
 * @param  [in]   transposed  Whether A or A^T is generated.
2679
 * @return  0 on success.
2680
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2681
 *          WOLFSSL_SMALL_STACK is defined.
2682
 */
2683
static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed)
2684
{
2685
    int i;
2686
    int k;
2687
#ifdef WOLFSSL_SMALL_STACK
2688
    byte *rand = NULL;
2689
    word64 *state = NULL;
2690
#else
2691
    byte rand[4 * GEN_MATRIX_SIZE + 4];
2692
    word64 state[25 * 4];
2693
#endif
2694
    unsigned int ctr0;
2695
    unsigned int ctr1;
2696
    unsigned int ctr2;
2697
    unsigned int ctr3;
2698
    byte* p;
2699
2700
#ifdef WOLFSSL_SMALL_STACK
2701
    rand = (byte*)XMALLOC(4 * GEN_MATRIX_SIZE + 4, NULL,
2702
                          DYNAMIC_TYPE_TMP_BUFFER);
2703
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 4, NULL,
2704
                          DYNAMIC_TYPE_TMP_BUFFER);
2705
    if ((rand == NULL) || (state == NULL)) {
2706
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2707
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2708
        return MEMORY_E;
2709
    }
2710
#endif
2711
2712
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
2713
    rand[4 * GEN_MATRIX_SIZE + 0] = 0xff;
2714
    rand[4 * GEN_MATRIX_SIZE + 1] = 0xff;
2715
    rand[4 * GEN_MATRIX_SIZE + 2] = 0xff;
2716
    rand[4 * GEN_MATRIX_SIZE + 3] = 0xff;
2717
2718
    for (k = 0; k < 2; k++) {
2719
        for (i = 0; i < 4; i++) {
2720
            if (!transposed) {
2721
                state[4*4 + i] = (word32)(0x1f0000 + (((k*4+i)/3) << 8) +
2722
                                          ((k*4+i)%3));
2723
            }
2724
            else {
2725
                state[4*4 + i] = (word32)(0x1f0000 + (((k*4+i)%3) << 8) +
2726
                                          ((k*4+i)/3));
2727
2728
            }
2729
        }
2730
2731
        sha3_128_blocksx4_seed_avx2(state, seed);
2732
        mlkem_redistribute_21_rand_ins(state,
2733
            rand + 0 * GEN_MATRIX_SIZE, rand + 1 * GEN_MATRIX_SIZE,
2734
            rand + 2 * GEN_MATRIX_SIZE, rand + 3 * GEN_MATRIX_SIZE);
2735
        for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2736
            sha3_blocksx4_avx2(state);
2737
            mlkem_redistribute_21_rand_ins(state,
2738
                rand + i + 0 * GEN_MATRIX_SIZE, rand + i + 1 * GEN_MATRIX_SIZE,
2739
                rand + i + 2 * GEN_MATRIX_SIZE, rand + i + 3 * GEN_MATRIX_SIZE);
2740
        }
2741
2742
        /* Sample random bytes to create a polynomial. */
2743
        p = rand;
2744
        ctr0 = mlkem_rej_uniform_n_ins(a + 0 * MLKEM_N, MLKEM_N, p,
2745
            GEN_MATRIX_SIZE);
2746
        p += GEN_MATRIX_SIZE;
2747
        ctr1 = mlkem_rej_uniform_n_ins(a + 1 * MLKEM_N, MLKEM_N, p,
2748
            GEN_MATRIX_SIZE);
2749
        p += GEN_MATRIX_SIZE;
2750
        ctr2 = mlkem_rej_uniform_n_ins(a + 2 * MLKEM_N, MLKEM_N, p,
2751
            GEN_MATRIX_SIZE);
2752
        p += GEN_MATRIX_SIZE;
2753
        ctr3 = mlkem_rej_uniform_n_ins(a + 3 * MLKEM_N, MLKEM_N, p,
2754
            GEN_MATRIX_SIZE);
2755
        /* Create more blocks if too many rejected. */
2756
        while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N) ||
2757
               (ctr3 < MLKEM_N)) {
2758
            sha3_blocksx4_avx2(state);
2759
            mlkem_redistribute_21_rand_ins(state, rand + 0 * GEN_MATRIX_SIZE,
2760
                rand + 1 * GEN_MATRIX_SIZE, rand + 2 * GEN_MATRIX_SIZE,
2761
                rand + 3 * GEN_MATRIX_SIZE);
2762
2763
            p = rand;
2764
            ctr0 += mlkem_rej_uniform_ins(a + 0 * MLKEM_N + ctr0,
2765
                MLKEM_N - ctr0, p, XOF_BLOCK_SIZE);
2766
            p += GEN_MATRIX_SIZE;
2767
            ctr1 += mlkem_rej_uniform_ins(a + 1 * MLKEM_N + ctr1,
2768
                MLKEM_N - ctr1, p, XOF_BLOCK_SIZE);
2769
            p += GEN_MATRIX_SIZE;
2770
            ctr2 += mlkem_rej_uniform_ins(a + 2 * MLKEM_N + ctr2,
2771
                MLKEM_N - ctr2, p, XOF_BLOCK_SIZE);
2772
            p += GEN_MATRIX_SIZE;
2773
            ctr3 += mlkem_rej_uniform_ins(a + 3 * MLKEM_N + ctr3,
2774
                MLKEM_N - ctr3, p, XOF_BLOCK_SIZE);
2775
        }
2776
2777
        a += 4 * MLKEM_N;
2778
    }
2779
2780
    readUnalignedWords64(state, seed, 4);
2781
    /* Transposed value same as not. */
2782
    state[4] = 0x1f0000 + (2 << 8) + 2;
2783
    XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5));
2784
    state[20] = W64LIT(0x8000000000000000);
2785
    for (i = 0; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2786
#ifndef WC_SHA3_NO_ASM
2787
        if (IS_INTEL_BMI2(cpuid_flags)) {
2788
            sha3_block_bmi2(state);
2789
        }
2790
        else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0))
2791
        {
2792
            sha3_block_avx2(state);
2793
            RESTORE_VECTOR_REGISTERS();
2794
        }
2795
        else
2796
#endif /* !WC_SHA3_NO_ASM */
2797
        {
2798
            BlockSha3(state);
2799
        }
2800
        XMEMCPY(rand + i, state, SHA3_128_BYTES);
2801
    }
2802
    ctr0 = mlkem_rej_uniform_n_ins(a, MLKEM_N, rand, GEN_MATRIX_SIZE);
2803
    while (ctr0 < MLKEM_N) {
2804
#ifndef WC_SHA3_NO_ASM
2805
        if (IS_INTEL_BMI2(cpuid_flags)) {
2806
            sha3_block_bmi2(state);
2807
        }
2808
        else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0))
2809
        {
2810
            sha3_block_avx2(state);
2811
            RESTORE_VECTOR_REGISTERS();
2812
        }
2813
        else
2814
#endif /* !WC_SHA3_NO_ASM */
2815
        {
2816
            BlockSha3(state);
2817
        }
2818
        XMEMCPY(rand, state, SHA3_128_BYTES);
2819
        ctr0 += mlkem_rej_uniform_ins(a + ctr0, MLKEM_N - ctr0, rand,
2820
            XOF_BLOCK_SIZE);
2821
    }
2822
2823
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2824
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2825
2826
    return 0;
2827
}
2828
2829
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
2830
/* Deterministically generate a 3x3 matrix (or transpose) of uniform integers
2831
 * mod q using eight-way AVX-512 SHA3. The first eight polynomials are produced
2832
 * in one eight-way batch; the ninth uses a single SHA3 state.
2833
 *
2834
 * @param  [out]  a           Matrix of uniform integers.
2835
 * @param  [in]   seed        Bytes to seed XOF generation.
2836
 * @param  [in]   transposed  Whether A or A^T is generated.
2837
 * @return  0 on success.
2838
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2839
 *          WOLFSSL_SMALL_STACK is defined.
2840
 */
2841
static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed)
2842
{
2843
    int i;
2844
#ifdef WOLFSSL_SMALL_STACK
2845
    byte *rand = NULL;
2846
    word64 *state = NULL;
2847
#else
2848
    byte rand[8 * GEN_MATRIX_SIZE + 4];
2849
    word64 state[25 * 8];
2850
#endif
2851
    unsigned int ctr[8];
2852
2853
#ifdef WOLFSSL_SMALL_STACK
2854
    rand = (byte*)XMALLOC(8 * GEN_MATRIX_SIZE + 4, NULL,
2855
                          DYNAMIC_TYPE_TMP_BUFFER);
2856
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 8, NULL,
2857
                          DYNAMIC_TYPE_TMP_BUFFER);
2858
    if ((rand == NULL) || (state == NULL)) {
2859
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2860
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2861
        return MEMORY_E;
2862
    }
2863
#endif
2864
2865
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
2866
    rand[8 * GEN_MATRIX_SIZE + 0] = 0xff;
2867
    rand[8 * GEN_MATRIX_SIZE + 1] = 0xff;
2868
    rand[8 * GEN_MATRIX_SIZE + 2] = 0xff;
2869
    rand[8 * GEN_MATRIX_SIZE + 3] = 0xff;
2870
2871
    /* First eight polynomials - row-major indices 0..7 of the 3x3 matrix
2872
     * (through (2,1)) - in one eight-way batch. */
2873
    for (i = 0; i < 8; i++) {
2874
        int row = i / 3;
2875
        int col = i % 3;
2876
        if (!transposed) {
2877
            state[4*8 + i] = (word32)(0x1f0000 + (row << 8) + col);
2878
        }
2879
        else {
2880
            state[4*8 + i] = (word32)(0x1f0000 + (col << 8) + row);
2881
        }
2882
    }
2883
2884
    sha3_128_blocksx8_seed_avx512(state, seed);
2885
    mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
2886
    for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2887
        sha3_blocksx8_avx512(state);
2888
        mlkem_redistribute_21_rand_x8_avx512(state, rand + i, GEN_MATRIX_SIZE);
2889
    }
2890
2891
    for (i = 0; i < 8; i++) {
2892
        ctr[i] = mlkem_rej_uniform_n_ins(a + i * MLKEM_N, MLKEM_N,
2893
            rand + i * GEN_MATRIX_SIZE, GEN_MATRIX_SIZE);
2894
    }
2895
    while ((ctr[0] < MLKEM_N) || (ctr[1] < MLKEM_N) || (ctr[2] < MLKEM_N) ||
2896
           (ctr[3] < MLKEM_N) || (ctr[4] < MLKEM_N) || (ctr[5] < MLKEM_N) ||
2897
           (ctr[6] < MLKEM_N) || (ctr[7] < MLKEM_N)) {
2898
        sha3_blocksx8_avx512(state);
2899
        mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
2900
        for (i = 0; i < 8; i++) {
2901
            ctr[i] += mlkem_rej_uniform_ins(a + i * MLKEM_N + ctr[i],
2902
                MLKEM_N - ctr[i], rand + i * GEN_MATRIX_SIZE, XOF_BLOCK_SIZE);
2903
        }
2904
    }
2905
    a += 8 * MLKEM_N;
2906
2907
    /* Ninth polynomial (row 2, column 2) - single SHA3 state. Transposed
2908
     * value same as not. */
2909
    readUnalignedWords64(state, seed, 4);
2910
    state[4] = 0x1f0000 + (2 << 8) + 2;
2911
    XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5));
2912
    state[20] = W64LIT(0x8000000000000000);
2913
    for (i = 0; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
2914
#ifndef WC_SHA3_NO_ASM
2915
        if (IS_INTEL_BMI2(cpuid_flags)) {
2916
            sha3_block_bmi2(state);
2917
        }
2918
        else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0))
2919
        {
2920
            sha3_block_avx2(state);
2921
            RESTORE_VECTOR_REGISTERS();
2922
        }
2923
        else
2924
#endif /* !WC_SHA3_NO_ASM */
2925
        {
2926
            BlockSha3(state);
2927
        }
2928
        XMEMCPY(rand + i, state, SHA3_128_BYTES);
2929
    }
2930
    ctr[0] = mlkem_rej_uniform_n_ins(a, MLKEM_N, rand, GEN_MATRIX_SIZE);
2931
    while (ctr[0] < MLKEM_N) {
2932
#ifndef WC_SHA3_NO_ASM
2933
        if (IS_INTEL_BMI2(cpuid_flags)) {
2934
            sha3_block_bmi2(state);
2935
        }
2936
        else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0))
2937
        {
2938
            sha3_block_avx2(state);
2939
            RESTORE_VECTOR_REGISTERS();
2940
        }
2941
        else
2942
#endif /* !WC_SHA3_NO_ASM */
2943
        {
2944
            BlockSha3(state);
2945
        }
2946
        XMEMCPY(rand, state, SHA3_128_BYTES);
2947
        ctr[0] += mlkem_rej_uniform_ins(a + ctr[0], MLKEM_N - ctr[0], rand,
2948
            XOF_BLOCK_SIZE);
2949
    }
2950
2951
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2952
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2953
2954
    return 0;
2955
}
2956
#endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */
2957
#endif
2958
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
2959
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
2960
 *
2961
 * Seed used with XOF to generate random bytes.
2962
 *
2963
 * @param  [out]  a           Matrix of uniform integers.
2964
 * @param  [in]   seed        Bytes to seed XOF generation.
2965
 * @param  [in]   transposed  Whether A or A^T is generated.
2966
 * @return  0 on success.
2967
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
2968
 *          WOLFSSL_SMALL_STACK is defined.
2969
 */
2970
static int mlkem_gen_matrix_k4_avx2(sword16* a, byte* seed, int transposed)
2971
{
2972
    int i;
2973
    int k;
2974
#ifdef WOLFSSL_SMALL_STACK
2975
    byte *rand = NULL;
2976
    word64 *state = NULL;
2977
#else
2978
    byte rand[4 * GEN_MATRIX_SIZE + 4];
2979
    word64 state[25 * 4];
2980
#endif
2981
    unsigned int ctr0;
2982
    unsigned int ctr1;
2983
    unsigned int ctr2;
2984
    unsigned int ctr3;
2985
    byte* p;
2986
2987
#ifdef WOLFSSL_SMALL_STACK
2988
    rand = (byte*)XMALLOC(4 * GEN_MATRIX_SIZE + 4, NULL,
2989
                          DYNAMIC_TYPE_TMP_BUFFER);
2990
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 4, NULL,
2991
                          DYNAMIC_TYPE_TMP_BUFFER);
2992
    if ((rand == NULL) || (state == NULL)) {
2993
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2994
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
2995
        return MEMORY_E;
2996
    }
2997
#endif
2998
2999
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
3000
    rand[4 * GEN_MATRIX_SIZE + 0] = 0xff;
3001
    rand[4 * GEN_MATRIX_SIZE + 1] = 0xff;
3002
    rand[4 * GEN_MATRIX_SIZE + 2] = 0xff;
3003
    rand[4 * GEN_MATRIX_SIZE + 3] = 0xff;
3004
3005
    for (k = 0; k < 4; k++) {
3006
        for (i = 0; i < 4; i++) {
3007
            if (!transposed) {
3008
                state[4*4 + i] = (word32)(0x1f0000 + (k << 8) + i);
3009
            }
3010
            else {
3011
                state[4*4 + i] = (word32)(0x1f0000 + (i << 8) + k);
3012
            }
3013
        }
3014
3015
        sha3_128_blocksx4_seed_avx2(state, seed);
3016
        mlkem_redistribute_21_rand_ins(state,
3017
            rand + 0 * GEN_MATRIX_SIZE, rand + 1 * GEN_MATRIX_SIZE,
3018
            rand + 2 * GEN_MATRIX_SIZE, rand + 3 * GEN_MATRIX_SIZE);
3019
        for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
3020
            sha3_blocksx4_avx2(state);
3021
            mlkem_redistribute_21_rand_ins(state,
3022
                rand + i + 0 * GEN_MATRIX_SIZE, rand + i + 1 * GEN_MATRIX_SIZE,
3023
                rand + i + 2 * GEN_MATRIX_SIZE, rand + i + 3 * GEN_MATRIX_SIZE);
3024
        }
3025
3026
        /* Sample random bytes to create a polynomial. */
3027
        p = rand;
3028
        ctr0 = mlkem_rej_uniform_n_ins(a + 0 * MLKEM_N, MLKEM_N, p,
3029
            GEN_MATRIX_SIZE);
3030
        p += GEN_MATRIX_SIZE;
3031
        ctr1 = mlkem_rej_uniform_n_ins(a + 1 * MLKEM_N, MLKEM_N, p,
3032
            GEN_MATRIX_SIZE);
3033
        p += GEN_MATRIX_SIZE;
3034
        ctr2 = mlkem_rej_uniform_n_ins(a + 2 * MLKEM_N, MLKEM_N, p,
3035
            GEN_MATRIX_SIZE);
3036
        p += GEN_MATRIX_SIZE;
3037
        ctr3 = mlkem_rej_uniform_n_ins(a + 3 * MLKEM_N, MLKEM_N, p,
3038
            GEN_MATRIX_SIZE);
3039
        /* Create more blocks if too many rejected. */
3040
        while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N) ||
3041
               (ctr3 < MLKEM_N)) {
3042
            sha3_blocksx4_avx2(state);
3043
            mlkem_redistribute_21_rand_ins(state, rand + 0 * GEN_MATRIX_SIZE,
3044
                rand + 1 * GEN_MATRIX_SIZE, rand + 2 * GEN_MATRIX_SIZE,
3045
                rand + 3 * GEN_MATRIX_SIZE);
3046
3047
            p = rand;
3048
            ctr0 += mlkem_rej_uniform_ins(a + 0 * MLKEM_N + ctr0,
3049
                MLKEM_N - ctr0, p, XOF_BLOCK_SIZE);
3050
            p += GEN_MATRIX_SIZE;
3051
            ctr1 += mlkem_rej_uniform_ins(a + 1 * MLKEM_N + ctr1,
3052
                MLKEM_N - ctr1, p, XOF_BLOCK_SIZE);
3053
            p += GEN_MATRIX_SIZE;
3054
            ctr2 += mlkem_rej_uniform_ins(a + 2 * MLKEM_N + ctr2,
3055
                MLKEM_N - ctr2, p, XOF_BLOCK_SIZE);
3056
            p += GEN_MATRIX_SIZE;
3057
            ctr3 += mlkem_rej_uniform_ins(a + 3 * MLKEM_N + ctr3,
3058
                MLKEM_N - ctr3, p, XOF_BLOCK_SIZE);
3059
        }
3060
3061
        a += 4 * MLKEM_N;
3062
    }
3063
3064
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3065
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3066
3067
    return 0;
3068
}
3069
3070
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
3071
/* Deterministically generate a 4x4 matrix (or transpose) of uniform integers
3072
 * mod q using eight-way AVX-512 SHA3. The 16 polynomials are produced in two
3073
 * batches of eight.
3074
 *
3075
 * @param  [out]  a           Matrix of uniform integers.
3076
 * @param  [in]   seed        Bytes to seed XOF generation.
3077
 * @param  [in]   transposed  Whether A or A^T is generated.
3078
 * @return  0 on success.
3079
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
3080
 *          WOLFSSL_SMALL_STACK is defined.
3081
 */
3082
static int mlkem_gen_matrix_k4_avx512(sword16* a, byte* seed, int transposed)
3083
{
3084
    int i;
3085
    int b;
3086
#ifdef WOLFSSL_SMALL_STACK
3087
    byte *rand = NULL;
3088
    word64 *state = NULL;
3089
#else
3090
    byte rand[8 * GEN_MATRIX_SIZE + 4];
3091
    word64 state[25 * 8];
3092
#endif
3093
    unsigned int ctr[8];
3094
3095
#ifdef WOLFSSL_SMALL_STACK
3096
    rand = (byte*)XMALLOC(8 * GEN_MATRIX_SIZE + 4, NULL,
3097
                          DYNAMIC_TYPE_TMP_BUFFER);
3098
    state = (word64*)XMALLOC(sizeof(word64) * 25 * 8, NULL,
3099
                          DYNAMIC_TYPE_TMP_BUFFER);
3100
    if ((rand == NULL) || (state == NULL)) {
3101
        XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3102
        XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3103
        return MEMORY_E;
3104
    }
3105
#endif
3106
3107
    /* Loading 64 bits, only using 48 bits. Loading 4 bytes more than used. */
3108
    rand[8 * GEN_MATRIX_SIZE + 0] = 0xff;
3109
    rand[8 * GEN_MATRIX_SIZE + 1] = 0xff;
3110
    rand[8 * GEN_MATRIX_SIZE + 2] = 0xff;
3111
    rand[8 * GEN_MATRIX_SIZE + 3] = 0xff;
3112
3113
    for (b = 0; b < 2; b++) {
3114
        for (i = 0; i < 8; i++) {
3115
            int row = (b * 8 + i) / 4;
3116
            int col = (b * 8 + i) % 4;
3117
            if (!transposed) {
3118
                state[4*8 + i] = (word32)(0x1f0000 + (row << 8) + col);
3119
            }
3120
            else {
3121
                state[4*8 + i] = (word32)(0x1f0000 + (col << 8) + row);
3122
            }
3123
        }
3124
3125
        sha3_128_blocksx8_seed_avx512(state, seed);
3126
        mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
3127
        for (i = SHA3_128_BYTES; i < GEN_MATRIX_SIZE; i += SHA3_128_BYTES) {
3128
            sha3_blocksx8_avx512(state);
3129
            mlkem_redistribute_21_rand_x8_avx512(state, rand + i,
3130
                GEN_MATRIX_SIZE);
3131
        }
3132
3133
        /* Sample random bytes to create the polynomials. */
3134
        for (i = 0; i < 8; i++) {
3135
            ctr[i] = mlkem_rej_uniform_n_ins(a + i * MLKEM_N, MLKEM_N,
3136
                rand + i * GEN_MATRIX_SIZE, GEN_MATRIX_SIZE);
3137
        }
3138
        /* Create more blocks if too many rejected. */
3139
        while ((ctr[0] < MLKEM_N) || (ctr[1] < MLKEM_N) || (ctr[2] < MLKEM_N) ||
3140
               (ctr[3] < MLKEM_N) || (ctr[4] < MLKEM_N) || (ctr[5] < MLKEM_N) ||
3141
               (ctr[6] < MLKEM_N) || (ctr[7] < MLKEM_N)) {
3142
            sha3_blocksx8_avx512(state);
3143
            mlkem_redistribute_21_rand_x8_avx512(state, rand, GEN_MATRIX_SIZE);
3144
            for (i = 0; i < 8; i++) {
3145
                ctr[i] += mlkem_rej_uniform_ins(a + i * MLKEM_N + ctr[i],
3146
                    MLKEM_N - ctr[i], rand + i * GEN_MATRIX_SIZE,
3147
                    XOF_BLOCK_SIZE);
3148
            }
3149
        }
3150
3151
        a += 8 * MLKEM_N;
3152
    }
3153
3154
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3155
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3156
3157
    return 0;
3158
}
3159
#endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */
3160
#endif /* WOLFSSL_KYBER1024 || WOLFSSL_WC_ML_KEM_1024 */
3161
#elif defined(WOLFSSL_ARMASM) && defined(__aarch64__)
3162
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
3163
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
3164
 *
3165
 * Seed used with XOF to generate random bytes.
3166
 *
3167
 * @param  [out]  a           Matrix of uniform integers.
3168
 * @param  [in]   seed        Bytes to seed XOF generation.
3169
 * @param  [in]   transposed  Whether A or A^T is generated.
3170
 * @return  0 on success.
3171
 */
3172
static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed)
3173
{
3174
    word64 state[3 * 25];
3175
    word64* st = (word64*)state;
3176
    unsigned int ctr0;
3177
    unsigned int ctr1;
3178
    unsigned int ctr2;
3179
    byte* p;
3180
3181
    if (!transposed) {
3182
        state[0*25 + 4] = 0x1f0000 + (0 << 8) + 0;
3183
        state[1*25 + 4] = 0x1f0000 + (0 << 8) + 1;
3184
        state[2*25 + 4] = 0x1f0000 + (1 << 8) + 0;
3185
    }
3186
    else {
3187
        state[0*25 + 4] = 0x1f0000 + (0 << 8) + 0;
3188
        state[1*25 + 4] = 0x1f0000 + (1 << 8) + 0;
3189
        state[2*25 + 4] = 0x1f0000 + (0 << 8) + 1;
3190
    }
3191
3192
    mlkem_shake128_blocksx3_seed(state, seed);
3193
    /* Sample random bytes to create a polynomial. */
3194
    p = (byte*)st;
3195
    ctr0 = mlkem_rej_uniform_neon(a + 0 * MLKEM_N, MLKEM_N, p, XOF_BLOCK_SIZE);
3196
    p += 25 * 8;
3197
    ctr1 = mlkem_rej_uniform_neon(a + 1 * MLKEM_N, MLKEM_N, p, XOF_BLOCK_SIZE);
3198
    p += 25 * 8;
3199
    ctr2 = mlkem_rej_uniform_neon(a + 2 * MLKEM_N, MLKEM_N, p, XOF_BLOCK_SIZE);
3200
    while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N)) {
3201
        mlkem_sha3_blocksx3(st);
3202
3203
        p = (byte*)st;
3204
        ctr0 += mlkem_rej_uniform_neon(a + 0 * MLKEM_N + ctr0, MLKEM_N - ctr0,
3205
            p, XOF_BLOCK_SIZE);
3206
        p += 25 * 8;
3207
        ctr1 += mlkem_rej_uniform_neon(a + 1 * MLKEM_N + ctr1, MLKEM_N - ctr1,
3208
            p, XOF_BLOCK_SIZE);
3209
        p += 25 * 8;
3210
        ctr2 += mlkem_rej_uniform_neon(a + 2 * MLKEM_N + ctr2, MLKEM_N - ctr2,
3211
            p, XOF_BLOCK_SIZE);
3212
    }
3213
3214
    a += 3 * MLKEM_N;
3215
3216
    readUnalignedWords64(state, seed, 4);
3217
    /* Transposed value same as not. */
3218
    state[4] = 0x1f0000 + (1 << 8) + 1;
3219
    XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5));
3220
    state[20] = W64LIT(0x8000000000000000);
3221
    BlockSha3(state);
3222
    p = (byte*)state;
3223
    ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE);
3224
    while (ctr0 < MLKEM_N) {
3225
        BlockSha3(state);
3226
        ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p,
3227
            XOF_BLOCK_SIZE);
3228
    }
3229
3230
    return 0;
3231
}
3232
#endif
3233
3234
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
3235
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
3236
 *
3237
 * Seed used with XOF to generate random bytes.
3238
 *
3239
 * @param  [out]  a           Matrix of uniform integers.
3240
 * @param  [in]   seed        Bytes to seed XOF generation.
3241
 * @param  [in]   transposed  Whether A or A^T is generated.
3242
 * @return  0 on success.
3243
 */
3244
static int mlkem_gen_matrix_k3_aarch64(sword16* a, byte* seed, int transposed)
3245
{
3246
    int i;
3247
    int k;
3248
    word64 state[3 * 25];
3249
    word64* st = (word64*)state;
3250
    unsigned int ctr0;
3251
    unsigned int ctr1;
3252
    unsigned int ctr2;
3253
    byte* p;
3254
3255
    for (k = 0; k < 3; k++) {
3256
        for (i = 0; i < 3; i++) {
3257
            if (!transposed) {
3258
                state[i*25 + 4] = 0x1f0000 + ((k << 8) + i);
3259
            }
3260
            else {
3261
                state[i*25 + 4] = 0x1f0000 + ((i << 8) + k);
3262
            }
3263
        }
3264
3265
        mlkem_shake128_blocksx3_seed(state, seed);
3266
        /* Sample random bytes to create a polynomial. */
3267
        p = (byte*)st;
3268
        ctr0 = mlkem_rej_uniform_neon(a + 0 * MLKEM_N, MLKEM_N, p,
3269
            XOF_BLOCK_SIZE);
3270
        p += 25 * 8;
3271
        ctr1 = mlkem_rej_uniform_neon(a + 1 * MLKEM_N, MLKEM_N, p,
3272
            XOF_BLOCK_SIZE);
3273
        p += 25 * 8;
3274
        ctr2 = mlkem_rej_uniform_neon(a + 2 * MLKEM_N, MLKEM_N, p,
3275
            XOF_BLOCK_SIZE);
3276
        /* Create more blocks if too many rejected. */
3277
        while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N)) {
3278
            mlkem_sha3_blocksx3(st);
3279
3280
            p = (byte*)st;
3281
            ctr0 += mlkem_rej_uniform_neon(a + 0 * MLKEM_N + ctr0,
3282
                MLKEM_N - ctr0, p, XOF_BLOCK_SIZE);
3283
            p += 25 * 8;
3284
            ctr1 += mlkem_rej_uniform_neon(a + 1 * MLKEM_N + ctr1,
3285
                MLKEM_N - ctr1, p, XOF_BLOCK_SIZE);
3286
            p += 25 * 8;
3287
            ctr2 += mlkem_rej_uniform_neon(a + 2 * MLKEM_N + ctr2,
3288
                MLKEM_N - ctr2, p, XOF_BLOCK_SIZE);
3289
        }
3290
3291
        a += 3 * MLKEM_N;
3292
    }
3293
3294
    return 0;
3295
}
3296
#endif
3297
3298
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
3299
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
3300
 *
3301
 * Seed used with XOF to generate random bytes.
3302
 *
3303
 * @param  [out]  a           Matrix of uniform integers.
3304
 * @param  [in]   seed        Bytes to seed XOF generation.
3305
 * @param  [in]   transposed  Whether A or A^T is generated.
3306
 * @return  0 on success.
3307
 */
3308
static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed)
3309
{
3310
    int i;
3311
    int k;
3312
    word64 state[3 * 25];
3313
    word64* st = (word64*)state;
3314
    unsigned int ctr0;
3315
    unsigned int ctr1;
3316
    unsigned int ctr2;
3317
    byte* p;
3318
3319
    for (k = 0; k < 5; k++) {
3320
        for (i = 0; i < 3; i++) {
3321
            byte bi = ((k * 3) + i) / 4;
3322
            byte bj = ((k * 3) + i) % 4;
3323
            if (!transposed) {
3324
                state[i*25 + 4] = 0x1f0000 + (bi << 8) + bj;
3325
            }
3326
            else {
3327
                state[i*25 + 4] = 0x1f0000 + (bj << 8) + bi;
3328
            }
3329
        }
3330
3331
        mlkem_shake128_blocksx3_seed(state, seed);
3332
        /* Sample random bytes to create a polynomial. */
3333
        p = (byte*)st;
3334
        ctr0 = mlkem_rej_uniform_neon(a + 0 * MLKEM_N, MLKEM_N, p,
3335
            XOF_BLOCK_SIZE);
3336
        p += 25 * 8;
3337
        ctr1 = mlkem_rej_uniform_neon(a + 1 * MLKEM_N, MLKEM_N, p,
3338
            XOF_BLOCK_SIZE);
3339
        p += 25 * 8;
3340
        ctr2 = mlkem_rej_uniform_neon(a + 2 * MLKEM_N, MLKEM_N, p,
3341
            XOF_BLOCK_SIZE);
3342
        /* Create more blocks if too many rejected. */
3343
        while ((ctr0 < MLKEM_N) || (ctr1 < MLKEM_N) || (ctr2 < MLKEM_N)) {
3344
            mlkem_sha3_blocksx3(st);
3345
3346
            p = (byte*)st;
3347
            ctr0 += mlkem_rej_uniform_neon(a + 0 * MLKEM_N + ctr0,
3348
                MLKEM_N - ctr0, p, XOF_BLOCK_SIZE);
3349
            p += 25 * 8;
3350
            ctr1 += mlkem_rej_uniform_neon(a + 1 * MLKEM_N + ctr1,
3351
                MLKEM_N - ctr1, p, XOF_BLOCK_SIZE);
3352
            p += 25 * 8;
3353
            ctr2 += mlkem_rej_uniform_neon(a + 2 * MLKEM_N + ctr2,
3354
                MLKEM_N - ctr2, p, XOF_BLOCK_SIZE);
3355
        }
3356
3357
        a += 3 * MLKEM_N;
3358
    }
3359
3360
    readUnalignedWords64(state, seed, 4);
3361
    /* Transposed value same as not. */
3362
    state[4] = 0x1f0000 + (3 << 8) + 3;
3363
    XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5));
3364
    state[20] = W64LIT(0x8000000000000000);
3365
    BlockSha3(state);
3366
    p = (byte*)state;
3367
    ctr0 = mlkem_rej_uniform_neon(a, MLKEM_N, p, XOF_BLOCK_SIZE);
3368
    while (ctr0 < MLKEM_N) {
3369
        BlockSha3(state);
3370
        ctr0 += mlkem_rej_uniform_neon(a + ctr0, MLKEM_N - ctr0, p,
3371
            XOF_BLOCK_SIZE);
3372
    }
3373
3374
    return 0;
3375
}
3376
#endif
3377
#endif /* USE_INTEL_SPEEDUP */
3378
3379
#if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__))
3380
/* Absorb the seed data for squeezing out pseudo-random data.
3381
 *
3382
 * FIPS 203, Section 4.1:
3383
 * 1. XOF.init() = SHAKE128.Init().
3384
 * 2. XOF.Absorb(ctx,str) = SHAKE128.Absorb(ctx,str).
3385
 *
3386
 * @param  [in, out]  shake128  SHAKE-128 object.
3387
 * @param  [in]       seed      Data to absorb.
3388
 * @param  [in]       len       Length of data to absorb in bytes.
3389
 * @return  0 on success always.
3390
 */
3391
static int mlkem_xof_absorb(wc_Shake* shake128, const byte* seed, int len)
3392
41.4k
{
3393
41.4k
    int ret;
3394
3395
41.4k
    ret = wc_InitShake128(shake128, NULL, INVALID_DEVID);
3396
41.4k
    if (ret == 0) {
3397
41.4k
        ret = wc_Shake128_Absorb(shake128, seed, (word32)len);
3398
41.4k
    }
3399
3400
41.4k
    return ret;
3401
41.4k
}
3402
3403
/* Squeeze the state to produce pseudo-random data.
3404
 *
3405
 * FIPS 203, Section 4.1:
3406
 * 3. XOF.Squeeze(ctx,l) = SHAKE128.Squeeze(ctx,8.l).
3407
 *
3408
 * @param  [in, out]  shake128  SHAKE-128 object.
3409
 * @param  [out]      out       Buffer to write to.
3410
 * @param  [in]       blocks    Number of blocks to write.
3411
 * @return  0 on success always.
3412
 */
3413
static int mlkem_xof_squeezeblocks(wc_Shake* shake128, byte* out, int blocks)
3414
41.7k
{
3415
41.7k
    return wc_Shake128_SqueezeBlocks(shake128, out, (word32)blocks);
3416
41.7k
}
3417
#endif
3418
3419
/* New/Initialize SHA-3 object.
3420
 *
3421
 * FIPS 203, Section 4.1:
3422
 * H(s) := SHA3-256(s)
3423
 *
3424
 * @param  [in, out]  hash    SHA-3 object.
3425
 * @param  [in]       heap    Dynamic memory allocator hint.
3426
 * @param  [in]       devId   Device id.
3427
 * @return  0 on success always.
3428
 */
3429
int mlkem_hash_new(wc_Sha3* hash, void* heap, int devId)
3430
4.54k
{
3431
4.54k
    return wc_InitSha3_256(hash, heap, devId);
3432
4.54k
}
3433
3434
/* Free SHA-3 object.
3435
 *
3436
 * FIPS 203, Section 4.1:
3437
 * H(s) := SHA3-256(s)
3438
 *
3439
 * @param  [in, out]  hash  SHA-3 object.
3440
 */
3441
void mlkem_hash_free(wc_Sha3* hash)
3442
4.54k
{
3443
4.54k
    wc_Sha3_256_Free(hash);
3444
4.54k
}
3445
3446
/* Hash data using SHA3-256 with SHA-3 object.
3447
 *
3448
 * FIPS 203, Section 4.1:
3449
 * H(s) := SHA3-256(s)
3450
 *
3451
 * @param  [in, out]  hash     SHA-3 object.
3452
 * @param  [in]       data     Data to be hashed.
3453
 * @param  [in]       dataLen  Length of data in bytes.
3454
 * @param  [out]      out      Hash of data.
3455
 * @return  0 on success.
3456
 */
3457
int mlkem_hash256(wc_Sha3* hash, const byte* data, word32 dataLen, byte* out)
3458
4.49k
{
3459
4.49k
    int ret;
3460
3461
    /* Process all data. */
3462
4.49k
    ret = wc_Sha3_256_Update(hash, data, dataLen);
3463
4.49k
    if (ret == 0) {
3464
        /* Calculate Hash of data passed in and re-initialize. */
3465
4.49k
        ret = wc_Sha3_256_Final(hash, out);
3466
4.49k
    }
3467
3468
4.49k
    return ret;
3469
4.49k
}
3470
3471
/* Hash one or two blocks of data using SHA3-512 with SHA-3 object.
3472
 *
3473
 * FIPS 203, Section 4.1:
3474
 * G(s) := SHA3-512(s)
3475
 *
3476
 * @param  [in, out]  hash      SHA-3 object.
3477
 * @param  [in]       data1     First block of data to be hashed.
3478
 * @param  [in]       data1Len  Length of first block of data in bytes.
3479
 * @param  [in]       data2     Second block of data to be hashed. May be NULL.
3480
 * @param  [in]       data2Len  Length of second block of data in bytes.
3481
 * @param  [out]      out       Hash of all data.
3482
 * @return  0 on success.
3483
 */
3484
int mlkem_hash512(wc_Sha3* hash, const byte* data1, word32 data1Len,
3485
    const byte* data2, word32 data2Len, byte* out)
3486
4.49k
{
3487
4.49k
    int ret;
3488
3489
    /* Process first block of data. */
3490
4.49k
    ret = wc_Sha3_512_Update(hash, data1, data1Len);
3491
    /* Check if there is a second block of data. */
3492
4.49k
    if ((ret == 0) && (data2 != NULL) && (data2Len > 0)) {
3493
        /* Process second block of data. */
3494
4.49k
        ret = wc_Sha3_512_Update(hash, data2, data2Len);
3495
4.49k
    }
3496
4.49k
    if (ret == 0) {
3497
        /* Calculate Hash of data passed in and re-initialize. */
3498
4.49k
        ret = wc_Sha3_512_Final(hash, out);
3499
4.49k
    }
3500
3501
4.49k
    return ret;
3502
4.49k
}
3503
3504
/* Initialize SHAKE-256 object.
3505
 *
3506
 * @param  [in, out]  prf  SHAKE-256 object.
3507
 */
3508
void mlkem_prf_init(wc_Shake* prf)
3509
4.49k
{
3510
4.49k
    wc_InitShake256(prf, NULL, 0);
3511
4.49k
}
3512
3513
/* New/Initialize SHAKE-256 object.
3514
 *
3515
 * FIPS 203, Section 4.1, 4.3:
3516
 * PRF_eta(s,b) := SHAKE256(s||b,8.64.eta)
3517
 *
3518
 * @param  [in, out]  prf    SHAKE-256 object.
3519
 * @param  [in]       heap   Dynamic memory allocator hint.
3520
 * @param  [in]       devId  Device id.
3521
 * @return  0 on success always.
3522
 */
3523
int mlkem_prf_new(wc_Shake* prf, void* heap, int devId)
3524
4.54k
{
3525
4.54k
    return wc_InitShake256(prf, heap, devId);
3526
4.54k
}
3527
3528
/* Free SHAKE-256 object.
3529
 *
3530
 * FIPS 203, Section 4.1, 4.3:
3531
 * PRF_eta(s,b) := SHAKE256(s||b,8.64.eta)
3532
 *
3533
 * @param  [in, out]  prf  SHAKE-256 object.
3534
 */
3535
void mlkem_prf_free(wc_Shake* prf)
3536
4.54k
{
3537
4.54k
    wc_Shake256_Free(prf);
3538
4.54k
}
3539
3540
#if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__))
3541
/* Create pseudo-random data from the key using SHAKE-256.
3542
 *
3543
 * FIPS 203, Section 4.1, 4.3:
3544
 * PRF_eta(s,b) := SHAKE256(s||b,8.64.eta)
3545
 *
3546
 * @param  [in, out]  shake256  SHAKE-256 object.
3547
 * @param  [out]      out       Buffer to write to.
3548
 * @param  [in]       outLen    Number of bytes to write.
3549
 * @param  [in]       key       Data to derive from. Must be:
3550
 *                                WC_ML_KEM_SYM_SZ + 1 bytes in length.
3551
 * @return  0 on success always.
3552
 */
3553
static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen,
3554
    const byte* key)
3555
27.2k
{
3556
#ifdef USE_INTEL_SPEEDUP
3557
    word64 state[25];
3558
3559
    (void)shake256;
3560
3561
    /* Put first WC_ML_KEM_SYM_SZ bytes of key into blank state. */
3562
    readUnalignedWords64(state, key, WC_ML_KEM_SYM_SZ / sizeof(word64));
3563
    /* Last byte in with end of content marker. */
3564
    state[WC_ML_KEM_SYM_SZ / 8] = 0x1f00 | key[WC_ML_KEM_SYM_SZ];
3565
    /* Set rest of state to 0. */
3566
    XMEMSET(state + WC_ML_KEM_SYM_SZ / 8 + 1, 0,
3567
        (25 - WC_ML_KEM_SYM_SZ / 8 - 1) * sizeof(word64));
3568
    /* ... except for rate marker. */
3569
    state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000);
3570
3571
    /* Generate as much output as is required. */
3572
    while (outLen > 0) {
3573
        /* Get as much of an output block as is needed. */
3574
        unsigned int len = min(outLen, WC_SHA3_256_BLOCK_SIZE);
3575
3576
        /* Perform a block operation on the state for next block of output. */
3577
#ifndef WC_SHA3_NO_ASM
3578
        if (IS_INTEL_BMI2(cpuid_flags)) {
3579
            sha3_block_bmi2(state);
3580
        }
3581
        else if (IS_INTEL_AVX2(cpuid_flags) &&
3582
                 (SAVE_VECTOR_REGISTERS2() == 0)) {
3583
            sha3_block_avx2(state);
3584
            RESTORE_VECTOR_REGISTERS();
3585
        }
3586
        else
3587
#endif /* !WC_SHA3_NO_ASM */
3588
        {
3589
            BlockSha3(state);
3590
        }
3591
3592
        /* Copy the state as output. */
3593
        XMEMCPY(out, state, len);
3594
        /* Update output pointer and length. */
3595
        out += len;
3596
        outLen -= len;
3597
    }
3598
3599
    /* state holds secret PRF output. */
3600
#ifdef WOLFSSL_CHECK_MEM_ZERO
3601
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
3602
#endif
3603
    ForceZero(state, sizeof(state));
3604
#ifdef WOLFSSL_CHECK_MEM_ZERO
3605
    wc_MemZero_Check(state, sizeof(state));
3606
#endif
3607
    return 0;
3608
#else
3609
27.2k
    int ret;
3610
3611
    /* Process all data. */
3612
27.2k
    ret = wc_Shake256_Update(shake256, key, WC_ML_KEM_SYM_SZ + 1);
3613
27.2k
    if (ret == 0) {
3614
        /* Calculate Hash of data passed in and re-initialize. */
3615
27.2k
        ret = wc_Shake256_Final(shake256, out, outLen);
3616
27.2k
    }
3617
3618
27.2k
    return ret;
3619
27.2k
#endif
3620
27.2k
}
3621
#endif
3622
3623
#ifdef WOLFSSL_MLKEM_KYBER
3624
#ifdef USE_INTEL_SPEEDUP
3625
/* Create pseudo-random key from the seed using SHAKE-256.
3626
 *
3627
 * @param  [in]  seed      Data to derive from.
3628
 * @param  [in]  seedLen   Length of data to derive from in bytes.
3629
 * @param  [out] out       Buffer to write to.
3630
 * @param  [in]  outLen    Number of bytes to derive.
3631
 * @return  0 on success always.
3632
 */
3633
int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen)
3634
{
3635
    word64 state[25];
3636
    word32 len64 = seedLen / 8;
3637
3638
    readUnalignedWords64(state, seed, len64);
3639
    state[len64] = 0x1f;
3640
    XMEMSET(state + len64 + 1, 0, (25 - len64 - 1) * sizeof(word64));
3641
    state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000);
3642
3643
#ifndef WC_SHA3_NO_ASM
3644
    if (IS_INTEL_BMI2(cpuid_flags)) {
3645
        sha3_block_bmi2(state);
3646
    }
3647
    else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
3648
        sha3_block_avx2(state);
3649
        RESTORE_VECTOR_REGISTERS();
3650
    }
3651
    else
3652
#endif
3653
    {
3654
        BlockSha3(state);
3655
    }
3656
    XMEMCPY(out, state, outLen);
3657
3658
    /* state holds secret KDF output. */
3659
#ifdef WOLFSSL_CHECK_MEM_ZERO
3660
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
3661
#endif
3662
    ForceZero(state, sizeof(state));
3663
#ifdef WOLFSSL_CHECK_MEM_ZERO
3664
    wc_MemZero_Check(state, sizeof(state));
3665
#endif
3666
    return 0;
3667
}
3668
#endif
3669
3670
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
3671
/* Create pseudo-random key from the seed using SHAKE-256.
3672
 *
3673
 * @param  [in]  seed      Data to derive from.
3674
 * @param  [in]  seedLen   Length of data to derive from in bytes.
3675
 * @param  [out] out       Buffer to write to.
3676
 * @param  [in]  outLen    Number of bytes to derive.
3677
 * @return  0 on success always.
3678
 */
3679
int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen)
3680
{
3681
    word64 state[25];
3682
    word32 len64 = seedLen / 8;
3683
3684
    readUnalignedWords64(state, seed, len64);
3685
    state[len64] = 0x1f;
3686
    XMEMSET(state + len64 + 1, 0, (25 - len64 - 1) * sizeof(word64));
3687
    state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000);
3688
3689
    BlockSha3(state);
3690
    XMEMCPY(out, state, outLen);
3691
3692
    /* state holds secret KDF output. */
3693
#ifdef WOLFSSL_CHECK_MEM_ZERO
3694
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
3695
#endif
3696
    ForceZero(state, sizeof(state));
3697
#ifdef WOLFSSL_CHECK_MEM_ZERO
3698
    wc_MemZero_Check(state, sizeof(state));
3699
#endif
3700
    return 0;
3701
}
3702
#endif
3703
#endif
3704
3705
#ifndef WOLFSSL_NO_ML_KEM
3706
/* Derive the secret from z and cipher text.
3707
 *
3708
 * @param [in, out]  prf   SHAKE-256 object.
3709
 * @param [in]       z     Implicit rejection value.
3710
 * @param [in]       ct    Cipher text.
3711
 * @param [in]       ctSz  Length of cipher text in bytes.
3712
 * @param [out]      ss    Shared secret.
3713
 * @return  0 on success.
3714
 * @return  MEMORY_E when dynamic memory allocation failed.
3715
 * @return  Other negative value when a hash error occurred.
3716
 */
3717
int mlkem_derive_secret(wc_Shake* prf, const byte* z, const byte* ct,
3718
    word32 ctSz, byte* ss)
3719
0
{
3720
0
    int ret;
3721
3722
#ifdef USE_INTEL_SPEEDUP
3723
    #ifdef WC_C_DYNAMIC_FALLBACK
3724
    /* The buffer-stuffing shortcut below assumes a freshly initialized object
3725
     * (zeroed sponge state).  When WC_C_DYNAMIC_FALLBACK, that doesn't
3726
     * generally hold: other users of the shared object - e.g. the C fallback
3727
     * legs of mlkem_gen_matrix()/mlkem_get_noise() reached when
3728
     * SAVE_VECTOR_REGISTERS2() fails (kernel context, or
3729
     * DEBUG_VECTOR_REGISTER_ACCESS_FUZZING) - drive the XOF on the object via
3730
     * absorb/squeeze and leave it mid-squeeze.
3731
     *
3732
     * Without WC_C_DYNAMIC_FALLBACK, SAVE_VECTOR_REGISTERS2() cannot fail in
3733
     * supported configurations and the asm legs keep their working state in
3734
     * local buffers, so the object provably stays pristine and the
3735
     * re-initialization is safely skipped.
3736
     *
3737
     * TL;DR: when WC_C_DYNAMIC_FALLBACK, re-initialize, as the
3738
     * non-USE_INTEL_SPEEDUP path does.
3739
     */
3740
    ret = wc_InitShake256(prf, NULL, INVALID_DEVID);
3741
    if (ret != 0)
3742
        return ret;
3743
    #endif /* WC_C_DYNAMIC_FALLBACK */
3744
3745
    XMEMCPY(prf->t, z, WC_ML_KEM_SYM_SZ);
3746
    XMEMCPY(prf->t + WC_ML_KEM_SYM_SZ, ct,
3747
        WC_SHA3_256_COUNT * 8 - WC_ML_KEM_SYM_SZ);
3748
    prf->i = WC_ML_KEM_SYM_SZ + WC_SHA3_256_COUNT * 8 - WC_ML_KEM_SYM_SZ;
3749
    ct += WC_SHA3_256_COUNT * 8 - WC_ML_KEM_SYM_SZ;
3750
    ctSz -= WC_SHA3_256_COUNT * 8 - WC_ML_KEM_SYM_SZ;
3751
    ret = wc_Shake256_Update(prf, ct, ctSz);
3752
    if (ret == 0) {
3753
        ret = wc_Shake256_Final(prf, ss, WC_ML_KEM_SS_SZ);
3754
    }
3755
#else
3756
0
    ret = wc_InitShake256(prf, NULL, INVALID_DEVID);
3757
0
    if (ret == 0) {
3758
0
        ret = wc_Shake256_Update(prf, z, WC_ML_KEM_SYM_SZ);
3759
0
    }
3760
0
    if (ret == 0) {
3761
0
        ret = wc_Shake256_Update(prf, ct, ctSz);
3762
0
    }
3763
0
    if (ret == 0) {
3764
0
        ret = wc_Shake256_Final(prf, ss, WC_ML_KEM_SS_SZ);
3765
0
    }
3766
0
#endif
3767
3768
0
    return ret;
3769
0
}
3770
#endif
3771
3772
#if !defined(WOLFSSL_ARMASM)
3773
/* Rejection sampling on uniform random bytes to generate uniform random
3774
 * integers mod q.
3775
 *
3776
 * FIPS 203, Algorithm 7: SampleNTT(B)
3777
 * Takes a 32-byte seed and two indices as input and outputs a pseudorandom
3778
 * element of T_q.
3779
 *   ...
3780
 *   4: while j < 256 do
3781
 *   5:     (ctx,C) <- XOF.Squeeze(ctx,3)
3782
 *   6:     d1 <- C[0] + 256.(C[1] mod 16)
3783
 *   7:     d2 <- lower(C[1] / 16) + 16.C[2]
3784
 *   8:     if d1 < q then
3785
 *   9:         a_hat[j] <- d1
3786
 *  10:         j <- j + 1
3787
 *  11:     end if
3788
 *  12:     if d2 < q and j < 256 then
3789
 *  13:         a_hat[j] <- d2
3790
 *  14:         j <- j + 1
3791
 *  15:     end if
3792
 *  16: end while
3793
 *  ...
3794
 *
3795
 * @param  [out]  p     Uniform random integers mod q.
3796
 * @param  [in]   len   Maximum number of integers.
3797
 * @param  [in]   r     Uniform random bytes buffer.
3798
 * @param  [in]   rLen  Length of random data in buffer.
3799
 * @return  Number of integers sampled.
3800
 */
3801
static unsigned int mlkem_rej_uniform_c(sword16* p, unsigned int len,
3802
    const byte* r, unsigned int rLen)
3803
41.7k
{
3804
41.7k
    unsigned int i;
3805
41.7k
    unsigned int j;
3806
3807
#if defined(WOLFSSL_MLKEM_SMALL) || !defined(WC_64BIT_CPU) || \
3808
    defined(BIG_ENDIAN_ORDER) || defined(WOLFSSL_WIDE_BYTE)
3809
    /* Keep sampling until max number of integers reached or buffer is used up.
3810
     * Step 4. */
3811
    for (i = 0, j = 0; (i < len) && (j <= rLen - 3); j += 3) {
3812
        /* Step 5 - Now using 3 bytes of what the caller generated. */
3813
        /* Use 24 bits (3 bytes) as two 12 bits integers. */
3814
        /* Step 6. */
3815
        sword16 v0 = ((r[0] >> 0) | ((word16)r[1] << 8)) & 0xFFF;
3816
        /* Step 7. */
3817
        sword16 v1 = ((r[1] >> 4) | ((word16)r[2] << 4)) & 0xFFF;
3818
3819
        /* Reject first 12-bit integer if greater than or equal to q.
3820
         * Step 8 */
3821
        if (v0 < MLKEM_Q) {
3822
            /* Steps 9-10 */
3823
            p[i++] = v0;
3824
        }
3825
        /* Check second if we don't have enough integers yet.
3826
         * Reject second 12-bit integer if greater than or equal to q.
3827
         * Step 12 */
3828
        if ((i < len) && (v1 < MLKEM_Q)) {
3829
            /* Steps 13-14 */
3830
            p[i++] = v1;
3831
        }
3832
3833
        /* Move over used bytes. */
3834
        r += 3;
3835
    }
3836
#else
3837
    /* Unroll loops. Minimal work per loop. */
3838
41.7k
    unsigned int minJ;
3839
3840
    /* Calculate minimum number of 6 byte data blocks to get all required
3841
     * numbers assuming no rejections. */
3842
41.7k
    minJ = len / 4 * 6;
3843
41.7k
    if (minJ > rLen)
3844
0
        minJ = rLen;
3845
41.7k
    i = 0;
3846
2.69M
    for (j = 0; j < minJ; j += 6) {
3847
        /* Use 48 bits (6 bytes) as four 12-bit integers. */
3848
2.65M
        word64 r_word = readUnalignedWord64(r);
3849
2.65M
        sword16 v0 =  r_word        & 0xfff;
3850
2.65M
        sword16 v1 = (r_word >> 12) & 0xfff;
3851
2.65M
        sword16 v2 = (r_word >> 24) & 0xfff;
3852
2.65M
        sword16 v3 = (r_word >> 36) & 0xfff;
3853
3854
2.65M
        p[i] = v0;
3855
2.65M
        i += (v0 < MLKEM_Q);
3856
2.65M
        p[i] = v1;
3857
2.65M
        i += (v1 < MLKEM_Q);
3858
2.65M
        p[i] = v2;
3859
2.65M
        i += (v2 < MLKEM_Q);
3860
2.65M
        p[i] = v3;
3861
2.65M
        i += (v3 < MLKEM_Q);
3862
3863
        /* Move over used bytes. */
3864
2.65M
        r += 6;
3865
2.65M
    }
3866
    /* Check whether we have all the numbers we need. */
3867
41.7k
    if (j < rLen) {
3868
        /* Keep trying until we have fewer than 4 numbers to find or data is
3869
         * used up. */
3870
616k
        for (; (i + 4 < len) && (j < rLen); j += 6) {
3871
            /* Use 48 bits (6 bytes) as four 12-bit integers. */
3872
575k
            word64 r_word = readUnalignedWord64(r);
3873
575k
            sword16 v0 =  r_word        & 0xfff;
3874
575k
            sword16 v1 = (r_word >> 12) & 0xfff;
3875
575k
            sword16 v2 = (r_word >> 24) & 0xfff;
3876
575k
            sword16 v3 = (r_word >> 36) & 0xfff;
3877
3878
575k
            p[i] = v0;
3879
575k
            i += (v0 < MLKEM_Q);
3880
575k
            p[i] = v1;
3881
575k
            i += (v1 < MLKEM_Q);
3882
575k
            p[i] = v2;
3883
575k
            i += (v2 < MLKEM_Q);
3884
575k
            p[i] = v3;
3885
575k
            i += (v3 < MLKEM_Q);
3886
3887
            /* Move over used bytes. */
3888
575k
            r += 6;
3889
575k
        }
3890
        /* Keep trying until we have all the numbers we need or the data is used
3891
         * up. */
3892
92.7k
        for (; (i < len) && (j < rLen); j += 6) {
3893
            /* Use 48 bits (6 bytes) as four 12-bit integers. */
3894
50.9k
            word64 r_word = readUnalignedWord64(r);
3895
50.9k
            sword16 v0 =  r_word        & 0xfff;
3896
50.9k
            sword16 v1 = (r_word >> 12) & 0xfff;
3897
50.9k
            sword16 v2 = (r_word >> 24) & 0xfff;
3898
50.9k
            sword16 v3 = (r_word >> 36) & 0xfff;
3899
3900
            /* Reject first 12-bit integer if greater than or equal to q. */
3901
50.9k
            if (v0 < MLKEM_Q) {
3902
41.4k
                p[i++] = v0;
3903
41.4k
            }
3904
            /* Check second if we don't have enough integers yet.
3905
             * Reject second 12-bit integer if greater than or equal to q. */
3906
50.9k
            if ((i < len) && (v1 < MLKEM_Q)) {
3907
32.9k
                p[i++] = v1;
3908
32.9k
            }
3909
            /* Check third if we don't have enough integers yet.
3910
             * Reject third 12-bit integer if greater than or equal to q. */
3911
50.9k
            if ((i < len) && (v2 < MLKEM_Q)) {
3912
24.5k
                p[i++] = v2;
3913
24.5k
            }
3914
            /* Check fourth if we don't have enough integers yet.
3915
             * Reject fourth 12-bit integer if greater than or equal to q. */
3916
50.9k
            if ((i < len) && (v3 < MLKEM_Q)) {
3917
16.1k
                p[i++] = v3;
3918
16.1k
            }
3919
3920
            /* Move over used bytes. */
3921
50.9k
            r += 6;
3922
50.9k
        }
3923
41.7k
    }
3924
41.7k
#endif
3925
3926
41.7k
    return i;
3927
41.7k
}
3928
#endif
3929
3930
#if !defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
3931
    !defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
3932
3933
#if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__))
3934
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
3935
 *
3936
 * Seed used with XOF to generate random bytes.
3937
 *
3938
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
3939
 *   ...
3940
 *   3: for (i <- 0; i < k; i++)
3941
 *   4:     for (j <- 0; j < k; j++)
3942
 *   5:         A_hat[i,j] <- SampleNTT(rho||j||i)
3943
 *   6:     end for
3944
 *   7: end for
3945
 *   ...
3946
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
3947
 *   ...
3948
 *   4: for (i <- 0; i < k; i++)
3949
 *   5:     for (j <- 0; j < k; j++)
3950
 *   6:         A_hat[i,j] <- SampleNTT(rho||j||i)  (Transposed is rho||i||j)
3951
 *   7:     end for
3952
 *   8: end for
3953
 *   ...
3954
 * FIPS 203, Algorithm 7: SampleNTT(B)
3955
 * Takes a 32-byte seed and two indices as input and outputs a pseudorandom
3956
 * element of T_q.
3957
 *   1: ctx <- XOF.init()
3958
 *   2: ctx <- XOF.Absorb(ctx,B)
3959
 *   3: j <- 0
3960
 *   4: while j < 256 do
3961
 *   5:     (ctx,C) <- XOF.Squeeze(ctx,3)
3962
 *   ...
3963
 *  16: end while
3964
 *  17: return a_hat
3965
 *
3966
 * @param  [in, out]  prf         XOF object.
3967
 * @param  [out]      a           Matrix of uniform integers.
3968
 * @param  [in]       k           Number of dimensions. k x k polynomials.
3969
 * @param  [in]       seed        Bytes to seed XOF generation.
3970
 * @param  [in]       transposed  Whether A or A^T is generated.
3971
 * @return  0 on success.
3972
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
3973
 *          WOLFSSL_SMALL_STACK is defined.
3974
 */
3975
static int mlkem_gen_matrix_c(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed,
3976
    int transposed)
3977
4.23k
{
3978
4.23k
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
3979
4.23k
    byte* rand;
3980
#else
3981
    byte rand[GEN_MATRIX_SIZE + 2];
3982
#endif
3983
4.23k
    byte extSeed[WC_ML_KEM_SYM_SZ + 2];
3984
4.23k
    int ret = 0;
3985
4.23k
    int i;
3986
3987
    /* Copy seed into buffer that has space for i and j to be appended. */
3988
4.23k
    XMEMCPY(extSeed, seed, WC_ML_KEM_SYM_SZ);
3989
3990
4.23k
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
3991
    /* Allocate large amount of memory to hold random bytes to be sampled. */
3992
4.23k
    rand = (byte*)XMALLOC(GEN_MATRIX_SIZE + 2, NULL, DYNAMIC_TYPE_TMP_BUFFER);
3993
4.23k
    if (rand == NULL) {
3994
1
        ret = MEMORY_E;
3995
1
    }
3996
4.23k
#endif
3997
3998
4.23k
#if !defined(WOLFSSL_MLKEM_SMALL) && defined(WC_64BIT_CPU)
3999
    /* Loading 64 bits, only using 48 bits. Loading 2 bytes more than used. */
4000
4.23k
    if (ret == 0) {
4001
4.23k
        rand[GEN_MATRIX_SIZE+0] = 0xff;
4002
4.23k
        rand[GEN_MATRIX_SIZE+1] = 0xff;
4003
4.23k
    }
4004
4.23k
#endif
4005
4006
    /* Generate each vector of polynomials.
4007
     * Alg 13, Step 3. Alg 14, Step 4. */
4008
16.9k
    for (i = 0; (ret == 0) && (i < k); i++, a += k * MLKEM_N) {
4009
12.7k
        int j;
4010
        /* Generate each polynomial in vector from seed with indices.
4011
         * Alg 13, Step 4. Alg 14, Step 5. */
4012
50.9k
        for (j = 0; (ret == 0) && (j < k); j++) {
4013
38.2k
            if (transposed) {
4014
                /* Alg 14, Step 6: .. rho||i||j ... */
4015
0
                extSeed[WC_ML_KEM_SYM_SZ + 0] = (byte)i;
4016
0
                extSeed[WC_ML_KEM_SYM_SZ + 1] = (byte)j;
4017
0
            }
4018
38.2k
            else {
4019
                /* Alg 13, Step 5: .. rho||j||i ... */
4020
38.2k
                extSeed[WC_ML_KEM_SYM_SZ + 0] = (byte)j;
4021
38.2k
                extSeed[WC_ML_KEM_SYM_SZ + 1] = (byte)i;
4022
38.2k
            }
4023
            /* Absorb the index specific seed.
4024
             * Alg 7, Step 1-2 */
4025
38.2k
            ret = mlkem_xof_absorb(prf, extSeed, sizeof(extSeed));
4026
38.2k
            if (ret == 0) {
4027
                /* Create data based on the seed.
4028
                 * Alg 7, Step 5. Generating enough to, on average, be able to
4029
                 * get enough valid values. */
4030
38.2k
                ret = mlkem_xof_squeezeblocks(prf, rand, GEN_MATRIX_NBLOCKS);
4031
38.2k
            }
4032
38.2k
            if (ret == 0) {
4033
38.2k
                unsigned int ctr;
4034
4035
                /* Sample random bytes to create a polynomial.
4036
                 * Alg 7, Step 3 - implicitly counter is 0.
4037
                 * Alg 7, Step 4-16. */
4038
38.2k
                ctr = mlkem_rej_uniform_c(a + j * MLKEM_N, MLKEM_N, rand,
4039
38.2k
                    GEN_MATRIX_SIZE);
4040
                /* Create more blocks if too many rejected.
4041
                 * Alg 7, Step 4. */
4042
38.5k
                while (ctr < MLKEM_N) {
4043
                    /* Alg 7, Step 5. */
4044
307
                    mlkem_xof_squeezeblocks(prf, rand, 1);
4045
                    /* Alg 7, Step 4-16. */
4046
307
                    ctr += mlkem_rej_uniform_c(a + j * MLKEM_N + ctr,
4047
307
                        MLKEM_N - ctr, rand, XOF_BLOCK_SIZE);
4048
307
                }
4049
38.2k
            }
4050
38.2k
        }
4051
12.7k
    }
4052
4053
4.23k
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
4054
    /* Dispose of temporary buffer. */
4055
4.23k
    XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4056
4.23k
#endif
4057
4058
4.23k
    return ret;
4059
4.23k
}
4060
#endif
4061
4062
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
4063
 *
4064
 * Seed used with XOF to generate random bytes.
4065
 *
4066
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d), Steps 3-7
4067
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r), Steps 4-8
4068
 *
4069
 * @param  [in, out]  prf         XOF object.
4070
 * @param  [out]      a           Matrix of uniform integers.
4071
 * @param  [in]       k           Number of dimensions. k x k polynomials.
4072
 * @param  [in]       seed        Bytes to seed XOF generation.
4073
 * @param  [in]       transposed  Whether A or A^T is generated.
4074
 * @return  0 on success.
4075
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4076
 *          WOLFSSL_SMALL_STACK is defined.
4077
 */
4078
int mlkem_gen_matrix(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed,
4079
    int transposed)
4080
4.49k
{
4081
4.49k
    int ret;
4082
4083
4.49k
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
4084
4.49k
    if (k == WC_ML_KEM_512_K) {
4085
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
4086
        ret = mlkem_gen_matrix_k2_aarch64(a, seed, transposed);
4087
#else
4088
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
4089
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
4090
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4091
            ret = mlkem_gen_matrix_k2_avx512(a, seed, transposed);
4092
            RESTORE_VECTOR_REGISTERS();
4093
        }
4094
        else
4095
    #endif
4096
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4097
            ret = mlkem_gen_matrix_k2_avx2(a, seed, transposed);
4098
            RESTORE_VECTOR_REGISTERS();
4099
        }
4100
        else
4101
    #endif
4102
0
        {
4103
0
            ret = mlkem_gen_matrix_c(prf, a, WC_ML_KEM_512_K, seed, transposed);
4104
0
        }
4105
0
#endif
4106
0
    }
4107
4.49k
    else
4108
4.49k
#endif
4109
4.49k
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
4110
4.49k
    if (k == WC_ML_KEM_768_K) {
4111
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
4112
        ret = mlkem_gen_matrix_k3_aarch64(a, seed, transposed);
4113
#else
4114
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
4115
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
4116
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4117
            ret = mlkem_gen_matrix_k3_avx512(a, seed, transposed);
4118
            RESTORE_VECTOR_REGISTERS();
4119
        }
4120
        else
4121
    #endif
4122
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4123
            ret = mlkem_gen_matrix_k3_avx2(a, seed, transposed);
4124
            RESTORE_VECTOR_REGISTERS();
4125
        }
4126
        else
4127
    #endif
4128
4.34k
        {
4129
4.34k
            ret = mlkem_gen_matrix_c(prf, a, WC_ML_KEM_768_K, seed, transposed);
4130
4.34k
        }
4131
4.34k
#endif
4132
4.34k
    }
4133
145
    else
4134
145
#endif
4135
145
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
4136
145
    if (k == WC_ML_KEM_1024_K) {
4137
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
4138
        ret = mlkem_gen_matrix_k4_aarch64(a, seed, transposed);
4139
#else
4140
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
4141
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
4142
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4143
            ret = mlkem_gen_matrix_k4_avx512(a, seed, transposed);
4144
            RESTORE_VECTOR_REGISTERS();
4145
        }
4146
        else
4147
    #endif
4148
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4149
            ret = mlkem_gen_matrix_k4_avx2(a, seed, transposed);
4150
            RESTORE_VECTOR_REGISTERS();
4151
        }
4152
        else
4153
    #endif
4154
145
        {
4155
145
            ret = mlkem_gen_matrix_c(prf, a, WC_ML_KEM_1024_K, seed,
4156
145
                transposed);
4157
145
        }
4158
145
#endif
4159
145
    }
4160
0
    else
4161
0
#endif
4162
0
    {
4163
0
        ret = BAD_STATE_E;
4164
0
    }
4165
4166
4.49k
    (void)prf;
4167
4168
4.49k
    return ret;
4169
4.49k
}
4170
4171
#endif
4172
4173
#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
4174
    defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
4175
4176
/* Deterministically generate a matrix (or transpose) of uniform integers mod q.
4177
 *
4178
 * Seed used with XOF to generate random bytes.
4179
 *
4180
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
4181
 * ...
4182
 * 4:     for (j <- 0; j < k; j++)
4183
 * 5:         A_hat[i,j] <- SampleNTT(rho||j||i)
4184
 * 6:     end for
4185
 * ...
4186
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4187
 * ...
4188
 * 5:     for (j <- 0; j < k; j++)
4189
 * 6:         A_hat[i,j] <- SampleNTT(rho||j||i)  (Transposed is rho||i||j)
4190
 * 7:     end for
4191
 * ...
4192
 *
4193
 * @param  [in, out]  prf         XOF object.
4194
 * @param  [out]      a           Matrix of uniform integers.
4195
 * @param  [in]       k           Number of dimensions. k x k polynomials.
4196
 * @param  [in]       seed        Bytes to seed XOF generation.
4197
 * @param  [in]       i           Index of vector to generate.
4198
 * @param  [in]       transposed  Whether A or A^T is generated.
4199
 * @return  0 on success.
4200
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4201
 *          WOLFSSL_SMALL_STACK is defined.
4202
 */
4203
static int mlkem_gen_matrix_i(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed,
4204
    int i, int transposed)
4205
{
4206
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
4207
    byte* rand;
4208
#else
4209
    byte rand[GEN_MATRIX_SIZE + 2];
4210
#endif
4211
    byte extSeed[WC_ML_KEM_SYM_SZ + 2];
4212
    int ret = 0;
4213
    int j;
4214
4215
    XMEMCPY(extSeed, seed, WC_ML_KEM_SYM_SZ);
4216
4217
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
4218
    /* Allocate large amount of memory to hold random bytes to be sampled. */
4219
    rand = (byte*)XMALLOC(GEN_MATRIX_SIZE + 2, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4220
    if (rand == NULL) {
4221
        ret = MEMORY_E;
4222
    }
4223
#endif
4224
4225
#if !defined(WOLFSSL_MLKEM_SMALL) && defined(WC_64BIT_CPU)
4226
    /* Loading 64 bits, only using 48 bits. Loading 2 bytes more than used. */
4227
    if (ret == 0) {
4228
        rand[GEN_MATRIX_SIZE+0] = 0xff;
4229
        rand[GEN_MATRIX_SIZE+1] = 0xff;
4230
    }
4231
#endif
4232
4233
    /* Generate each polynomial in vector from seed with indices.
4234
     * Alg 13, Step 4. Alg 14, Step 5. */
4235
    for (j = 0; (ret == 0) && (j < k); j++) {
4236
        if (transposed) {
4237
            /* Alg 14, Step 6: .. rho||i||j ... */
4238
            extSeed[WC_ML_KEM_SYM_SZ + 0] = (byte)i;
4239
            extSeed[WC_ML_KEM_SYM_SZ + 1] = (byte)j;
4240
        }
4241
        else {
4242
            /* Alg 13, Step 5: .. rho||j||i ... */
4243
            extSeed[WC_ML_KEM_SYM_SZ + 0] = (byte)j;
4244
            extSeed[WC_ML_KEM_SYM_SZ + 1] = (byte)i;
4245
        }
4246
        /* Absorb the index specific seed.
4247
         * Alg 7, Step 1-2 */
4248
        ret = mlkem_xof_absorb(prf, extSeed, sizeof(extSeed));
4249
        if (ret == 0) {
4250
            /* Create data based on the seed.
4251
             * Alg 7, Step 5. Generating enough to, on average, be able to get
4252
             * enough valid values. */
4253
            ret = mlkem_xof_squeezeblocks(prf, rand, GEN_MATRIX_NBLOCKS);
4254
        }
4255
        if (ret == 0) {
4256
            unsigned int ctr;
4257
4258
            /* Sample random bytes to create a polynomial.
4259
             * Alg 7, Step 3 - implicitly counter is 0.
4260
             * Alg 7, Step 4-16. */
4261
            ctr = mlkem_rej_uniform_c(a + j * MLKEM_N, MLKEM_N, rand,
4262
                GEN_MATRIX_SIZE);
4263
            /* Create more blocks if too many rejected.
4264
             * Alg 7, Step 4. */
4265
            while (ctr < MLKEM_N) {
4266
                /* Alg 7, Step 5. */
4267
                mlkem_xof_squeezeblocks(prf, rand, 1);
4268
                /* Alg 7, Step 4-16. */
4269
                ctr += mlkem_rej_uniform_c(a + j * MLKEM_N + ctr,
4270
                    MLKEM_N - ctr, rand, XOF_BLOCK_SIZE);
4271
            }
4272
        }
4273
    }
4274
4275
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
4276
    /* Dispose of temporary buffer. */
4277
    XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4278
#endif
4279
4280
    return ret;
4281
}
4282
4283
#endif
4284
4285
4286
/******************************************************************************/
4287
4288
/* Subtract one 2 bit value from another out of a larger number.
4289
 *
4290
 * FIPS 203, Algorithm 8: SamplePolyCBD_eta(B)
4291
 * Takes a seed as input and outputs a pseudorandom sample from the distribution
4292
 * D_eta(R_q).
4293
 *
4294
 * @param  [in]  d  Value containing sequential 2 bit values.
4295
 * @param  [in]  i  Start index of the two values in 2 bits each.
4296
 * @return  Difference of the two values with range -2..2.
4297
 */
4298
#define ETA2_SUB(d, i) \
4299
6.97M
    (sword16)(((sword16)(((d) >> ((i) * 4 + 0)) & 0x3)) - \
4300
6.97M
              ((sword16)(((d) >> ((i) * 4 + 2)) & 0x3)))
4301
4302
/* Compute polynomial with coefficients distributed according to a centered
4303
 * binomial distribution with parameter eta2 from uniform random bytes.
4304
 *
4305
 * FIPS 203, Algorithm 8: SamplePolyCBD_eta(B)
4306
 * Takes a seed as input and outputs a pseudorandom sample from the distribution
4307
 * D_eta(R_q).
4308
 *
4309
 * @param [out]  p  Polynomial computed.
4310
 * @param [in]   r  Random bytes.
4311
 */
4312
static void mlkem_cbd_eta2(sword16* p, const byte* r)
4313
27.2k
{
4314
27.2k
    unsigned int i;
4315
4316
#ifndef WORD64_AVAILABLE
4317
    /* Calculate eight integer coefficients at a time. */
4318
    for (i = 0; i < MLKEM_N; i += 8) {
4319
    #ifdef WOLFSSL_MLKEM_SMALL
4320
        unsigned int j;
4321
    #endif
4322
        /* Take the next 4 bytes, little endian, as a 32 bit value. */
4323
    #ifdef BIG_ENDIAN_ORDER
4324
        word32 t = ByteReverseWord32(readUnalignedWord32(r));
4325
    #else
4326
        word32 t = readUnalignedWord32(r);
4327
    #endif
4328
        word32 d;
4329
        /* Add second bits to first. */
4330
        d  = (t >> 0) & 0x55555555;
4331
        d += (t >> 1) & 0x55555555;
4332
        /* Values 0, 1 or 2 in consecutive 2 bits.
4333
         * 0 - 1/4, 1 - 2/4, 2 - 1/4. */
4334
4335
    #ifdef WOLFSSL_MLKEM_SMALL
4336
        for (j = 0; j < 8; j++) {
4337
            p[i + j] = ETA2_SUB(d, j);
4338
        }
4339
    #else
4340
        p[i + 0] = ETA2_SUB(d, 0);
4341
        p[i + 1] = ETA2_SUB(d, 1);
4342
        p[i + 2] = ETA2_SUB(d, 2);
4343
        p[i + 3] = ETA2_SUB(d, 3);
4344
        p[i + 4] = ETA2_SUB(d, 4);
4345
        p[i + 5] = ETA2_SUB(d, 5);
4346
        p[i + 6] = ETA2_SUB(d, 6);
4347
        p[i + 7] = ETA2_SUB(d, 7);
4348
    #endif
4349
        /* -2 - 1/16, -1 - 4/16, 0 - 6/16, 1 - 4/16, 2 - 1/16  */
4350
4351
        /* Move over used bytes. */
4352
        r += 4;
4353
    }
4354
#else
4355
    /* Calculate sixteen integer coefficients at a time. */
4356
463k
    for (i = 0; i < MLKEM_N; i += 16) {
4357
    #ifdef WOLFSSL_MLKEM_SMALL
4358
        unsigned int j;
4359
    #endif
4360
        /* Take the next 8 bytes, little endian, as a 64 bit value. */
4361
    #ifdef BIG_ENDIAN_ORDER
4362
        word64 t = ByteReverseWord64(readUnalignedWord64(r));
4363
    #else
4364
435k
        word64 t = readUnalignedWord64(r);
4365
435k
    #endif
4366
435k
        word64 d;
4367
        /* Add second bits to first. */
4368
435k
        d  = (t >> 0) & 0x5555555555555555L;
4369
435k
        d += (t >> 1) & 0x5555555555555555L;
4370
        /* Values 0, 1 or 2 in consecutive 2 bits.
4371
         * 0 - 1/4, 1 - 2/4, 2 - 1/4. */
4372
4373
    #ifdef WOLFSSL_MLKEM_SMALL
4374
        for (j = 0; j < 16; j++) {
4375
            p[i + j] = ETA2_SUB(d, j);
4376
        }
4377
    #else
4378
435k
        p[i +  0] = ETA2_SUB(d,  0);
4379
435k
        p[i +  1] = ETA2_SUB(d,  1);
4380
435k
        p[i +  2] = ETA2_SUB(d,  2);
4381
435k
        p[i +  3] = ETA2_SUB(d,  3);
4382
435k
        p[i +  4] = ETA2_SUB(d,  4);
4383
435k
        p[i +  5] = ETA2_SUB(d,  5);
4384
435k
        p[i +  6] = ETA2_SUB(d,  6);
4385
435k
        p[i +  7] = ETA2_SUB(d,  7);
4386
435k
        p[i +  8] = ETA2_SUB(d,  8);
4387
435k
        p[i +  9] = ETA2_SUB(d,  9);
4388
435k
        p[i + 10] = ETA2_SUB(d, 10);
4389
435k
        p[i + 11] = ETA2_SUB(d, 11);
4390
435k
        p[i + 12] = ETA2_SUB(d, 12);
4391
435k
        p[i + 13] = ETA2_SUB(d, 13);
4392
435k
        p[i + 14] = ETA2_SUB(d, 14);
4393
435k
        p[i + 15] = ETA2_SUB(d, 15);
4394
435k
    #endif
4395
        /* -2 - 1/16, -1 - 4/16, 0 - 6/16, 1 - 4/16, 2 - 1/16  */
4396
4397
        /* Move over used bytes. */
4398
435k
        r += 8;
4399
435k
    }
4400
27.2k
#endif
4401
27.2k
}
4402
4403
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
4404
/* Subtract one 3 bit value from another out of a larger number.
4405
 *
4406
 * FIPS 203, Algorithm 8: SamplePolyCBD_eta(B)
4407
 * Takes a seed as input and outputs a pseudorandom sample from the distribution
4408
 * D_eta(R_q).
4409
 *
4410
 * @param  [in]  d  Value containing sequential 3 bit values.
4411
 * @param  [in]  i  Start index of the two values in 3 bits each.
4412
 * @return  Difference of the two values with range -3..3.
4413
 */
4414
#define ETA3_SUB(d, i) \
4415
0
    (sword16)(((sword16)(((d) >> ((i) * 6 + 0)) & 0x7)) - \
4416
0
              ((sword16)(((d) >> ((i) * 6 + 3)) & 0x7)))
4417
4418
/* Compute polynomial with coefficients distributed according to a centered
4419
 * binomial distribution with parameter eta3 from uniform random bytes.
4420
 *
4421
 * FIPS 203, Algorithm 8: SamplePolyCBD_eta(B)
4422
 * Takes a seed as input and outputs a pseudorandom sample from the distribution
4423
 * D_eta(R_q).
4424
 *
4425
 * @param [out]  p  Polynomial computed.
4426
 * @param [in]   r  Random bytes.
4427
 */
4428
static void mlkem_cbd_eta3(sword16* p, const byte* r)
4429
0
{
4430
0
    unsigned int i;
4431
4432
0
#if defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE) || \
4433
0
    defined(BIG_ENDIAN_ORDER) || defined(WOLFSSL_WIDE_BYTE)
4434
#ifndef WORD64_AVAILABLE
4435
    /* Calculate four integer coefficients at a time. */
4436
    for (i = 0; i < MLKEM_N; i += 4) {
4437
    #ifdef WOLFSSL_MLKEM_SMALL
4438
        unsigned int j;
4439
    #endif
4440
        /* Take the next 3 bytes, little endian, as a 24 bit value. */
4441
        word32 t = (((word32)(r[0])) <<  0) |
4442
                   (((word32)(r[1])) <<  8) |
4443
                   (((word32)(r[2])) << 16);
4444
        word32 d;
4445
        /* Add second and third bits to first. */
4446
        d  = (t >> 0) & 0x00249249;
4447
        d += (t >> 1) & 0x00249249;
4448
        d += (t >> 2) & 0x00249249;
4449
        /* Values 0, 1, 2 or 3 in consecutive 3 bits.
4450
         * 0 - 1/8, 1 - 3/8, 2 - 3/8, 3 - 1/8. */
4451
4452
    #ifdef WOLFSSL_MLKEM_SMALL
4453
        for (j = 0; j < 4; j++) {
4454
            p[i + j] = ETA3_SUB(d, j);
4455
        }
4456
    #else
4457
        p[i + 0] = ETA3_SUB(d, 0);
4458
        p[i + 1] = ETA3_SUB(d, 1);
4459
        p[i + 2] = ETA3_SUB(d, 2);
4460
        p[i + 3] = ETA3_SUB(d, 3);
4461
    #endif
4462
        /* -3-1/64, -2-6/64, -1-15/64, 0-20/64, 1-15/64, 2-6/64, 3-1/64 */
4463
4464
        /* Move over used bytes. */
4465
        r += 3;
4466
    }
4467
#else
4468
    /* Calculate eight integer coefficients at a time. */
4469
0
    for (i = 0; i < MLKEM_N; i += 8) {
4470
    #ifdef WOLFSSL_MLKEM_SMALL
4471
        unsigned int j;
4472
    #endif
4473
        /* Take the next 6 bytes, little endian, as a 48 bit value. */
4474
0
        word64 t = (((word64)(r[0])) <<  0) |
4475
0
                   (((word64)(r[1])) <<  8) |
4476
0
                   (((word64)(r[2])) << 16) |
4477
0
                   (((word64)(r[3])) << 24) |
4478
0
                   (((word64)(r[4])) << 32) |
4479
0
                   (((word64)(r[5])) << 40);
4480
0
        word64 d;
4481
        /* Add second and third bits to first. */
4482
0
        d  = (t >> 0) & 0x0000249249249249L;
4483
0
        d += (t >> 1) & 0x0000249249249249L;
4484
0
        d += (t >> 2) & 0x0000249249249249L;
4485
        /* Values 0, 1, 2 or 3 in consecutive 3 bits.
4486
         * 0 - 1/8, 1 - 3/8, 2 - 3/8, 3 - 1/8. */
4487
4488
    #ifdef WOLFSSL_MLKEM_SMALL
4489
        for (j = 0; j < 8; j++) {
4490
            p[i + j] = ETA3_SUB(d, j);
4491
        }
4492
    #else
4493
0
        p[i + 0] = ETA3_SUB(d, 0);
4494
0
        p[i + 1] = ETA3_SUB(d, 1);
4495
0
        p[i + 2] = ETA3_SUB(d, 2);
4496
0
        p[i + 3] = ETA3_SUB(d, 3);
4497
0
        p[i + 4] = ETA3_SUB(d, 4);
4498
0
        p[i + 5] = ETA3_SUB(d, 5);
4499
0
        p[i + 6] = ETA3_SUB(d, 6);
4500
0
        p[i + 7] = ETA3_SUB(d, 7);
4501
0
    #endif
4502
        /* -3-1/64, -2-6/64, -1-15/64, 0-20/64, 1-15/64, 2-6/64, 3-1/64 */
4503
4504
        /* Move over used bytes. */
4505
0
        r += 6;
4506
0
    }
4507
0
#endif /* WORD64_AVAILABLE */
4508
#else
4509
    /* Calculate eight integer coefficients at a time. */
4510
    for (i = 0; i < MLKEM_N; i += 16) {
4511
        word32 r0 = readUnalignedWord32(r);
4512
        word32 r1 = readUnalignedWord32(r + 4);
4513
        word32 r2 = readUnalignedWord32(r + 8);
4514
        /* Take the next 12 bytes, little endian, as 24 bit values. */
4515
        word32 t0 =   r0                      & 0xffffff;
4516
        word32 t1 = ((r0 >> 24) | (r1 <<  8)) & 0xffffff;
4517
        word32 t2 = ((r1 >> 16) | (r2 << 16)) & 0xffffff;
4518
        word32 t3 =   r2 >>  8                          ;
4519
        word32 d0;
4520
        word32 d1;
4521
        word32 d2;
4522
        word32 d3;
4523
4524
        /* Add second and third bits to first. */
4525
        d0  = (t0 >> 0) & 0x00249249;
4526
        d0 += (t0 >> 1) & 0x00249249;
4527
        d0 += (t0 >> 2) & 0x00249249;
4528
        d1  = (t1 >> 0) & 0x00249249;
4529
        d1 += (t1 >> 1) & 0x00249249;
4530
        d1 += (t1 >> 2) & 0x00249249;
4531
        d2  = (t2 >> 0) & 0x00249249;
4532
        d2 += (t2 >> 1) & 0x00249249;
4533
        d2 += (t2 >> 2) & 0x00249249;
4534
        d3  = (t3 >> 0) & 0x00249249;
4535
        d3 += (t3 >> 1) & 0x00249249;
4536
        d3 += (t3 >> 2) & 0x00249249;
4537
        /* Values 0, 1, 2 or 3 in consecutive 3 bits.
4538
         * 0 - 1/8, 1 - 3/8, 2 - 3/8, 3 - 1/8. */
4539
4540
        p[i +  0] = ETA3_SUB(d0, 0);
4541
        p[i +  1] = ETA3_SUB(d0, 1);
4542
        p[i +  2] = ETA3_SUB(d0, 2);
4543
        p[i +  3] = ETA3_SUB(d0, 3);
4544
        p[i +  4] = ETA3_SUB(d1, 0);
4545
        p[i +  5] = ETA3_SUB(d1, 1);
4546
        p[i +  6] = ETA3_SUB(d1, 2);
4547
        p[i +  7] = ETA3_SUB(d1, 3);
4548
        p[i +  8] = ETA3_SUB(d2, 0);
4549
        p[i +  9] = ETA3_SUB(d2, 1);
4550
        p[i + 10] = ETA3_SUB(d2, 2);
4551
        p[i + 11] = ETA3_SUB(d2, 3);
4552
        p[i + 12] = ETA3_SUB(d3, 0);
4553
        p[i + 13] = ETA3_SUB(d3, 1);
4554
        p[i + 14] = ETA3_SUB(d3, 2);
4555
        p[i + 15] = ETA3_SUB(d3, 3);
4556
        /* -3-1/64, -2-6/64, -1-15/64, 0-20/64, 1-15/64, 2-6/64, 3-1/64 */
4557
4558
        /* Move over used bytes. */
4559
        r += 12;
4560
    }
4561
#endif /* WOLFSSL_SMALL_STACK || WOLFSSL_MLKEM_NO_LARGE_CODE ||
4562
        * BIG_ENDIAN_ORDER */
4563
0
}
4564
#endif
4565
4566
#if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM))
4567
4568
/* Get noise/error by calculating random bytes and sampling to a binomial
4569
 * distribution.
4570
 *
4571
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
4572
 *   ...
4573
 *   9:     s[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
4574
 *   ...
4575
 *  13:     e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
4576
 *   ...
4577
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4578
 *   ...
4579
 *  10:     y[i] <- SamplePolyCBD_eta_1(PRF_eta_1(r, N))
4580
 *   ...
4581
 *
4582
 * @param  [in, out]  prf   Pseudo-random function object.
4583
 * @param  [out]      p     Polynomial.
4584
 * @param  [in]       seed  Seed to use when calculating random.
4585
 * @param  [in]       eta1  Size of noise/error integers.
4586
 * @return  0 on success.
4587
 */
4588
static int mlkem_get_noise_eta1_c(MLKEM_PRF_T* prf, sword16* p,
4589
    const byte* seed, byte eta1)
4590
27.2k
{
4591
27.2k
    int ret;
4592
4593
27.2k
    (void)eta1;
4594
4595
27.2k
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
4596
27.2k
    if (eta1 == MLKEM_CBD_ETA3) {
4597
0
        byte rand[ETA3_RAND_SIZE];
4598
4599
        /* Calculate random bytes from seed with PRF. */
4600
0
        ret = mlkem_prf(prf, rand, sizeof(rand), seed);
4601
0
        if (ret == 0) {
4602
            /* Sample for values in range -3..3 from 3 bits of random. */
4603
0
            mlkem_cbd_eta3(p, rand);
4604
0
         }
4605
        /* rand holds secret noise. */
4606
#ifdef WOLFSSL_CHECK_MEM_ZERO
4607
        wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
4608
#endif
4609
0
        ForceZero(rand, sizeof(rand));
4610
#ifdef WOLFSSL_CHECK_MEM_ZERO
4611
        wc_MemZero_Check(rand, sizeof(rand));
4612
#endif
4613
0
    }
4614
27.2k
    else
4615
27.2k
#endif
4616
27.2k
    {
4617
27.2k
        byte rand[ETA2_RAND_SIZE];
4618
4619
        /* Calculate random bytes from seed with PRF. */
4620
27.2k
        ret = mlkem_prf(prf, rand, sizeof(rand), seed);
4621
27.2k
        if (ret == 0) {
4622
            /* Sample for values in range -2..2 from 2 bits of random. */
4623
27.2k
            mlkem_cbd_eta2(p, rand);
4624
27.2k
        }
4625
        /* rand holds secret noise. */
4626
#ifdef WOLFSSL_CHECK_MEM_ZERO
4627
        wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
4628
#endif
4629
27.2k
        ForceZero(rand, sizeof(rand));
4630
#ifdef WOLFSSL_CHECK_MEM_ZERO
4631
        wc_MemZero_Check(rand, sizeof(rand));
4632
#endif
4633
27.2k
    }
4634
4635
27.2k
    return ret;
4636
27.2k
}
4637
4638
/* Get noise/error by calculating random bytes and sampling to a binomial
4639
 * distribution. Values -2..2
4640
 *
4641
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4642
 *   ...
4643
 *  14:     e1[i] <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4644
 *   ...
4645
 *  17:     e2 <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4646
 *   ...
4647
 *
4648
 * @param  [in, out]  prf   Pseudo-random function object.
4649
 * @param  [out]      p     Polynomial.
4650
 * @param  [in]       seed  Seed to use when calculating random.
4651
 * @return  0 on success.
4652
 */
4653
static int mlkem_get_noise_eta2_c(MLKEM_PRF_T* prf, sword16* p,
4654
    const byte* seed)
4655
0
{
4656
0
    int ret;
4657
0
    byte rand[ETA2_RAND_SIZE];
4658
4659
    /* Calculate random bytes from seed with PRF. */
4660
0
    ret = mlkem_prf(prf, rand, sizeof(rand), seed);
4661
0
    if (ret == 0) {
4662
0
        mlkem_cbd_eta2(p, rand);
4663
0
    }
4664
4665
    /* rand holds secret noise. */
4666
#ifdef WOLFSSL_CHECK_MEM_ZERO
4667
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
4668
#endif
4669
0
    ForceZero(rand, sizeof(rand));
4670
#ifdef WOLFSSL_CHECK_MEM_ZERO
4671
    wc_MemZero_Check(rand, sizeof(rand));
4672
#endif
4673
0
    return ret;
4674
0
}
4675
4676
#endif
4677
4678
#if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
4679
#define PRF_RAND_SZ   (2 * SHA3_256_BYTES)
4680
4681
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) || \
4682
    defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
4683
/* Get the noise/error by calculating random bytes.
4684
 *
4685
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4686
 *   ...
4687
 *  14:     e1[i] <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4688
 *   ...
4689
 *  17:     e2 <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4690
 *   ...
4691
 *
4692
 * @param  [out]  rand  Random number byte array.
4693
 * @param  [in]   seed  Seed to generate random from.
4694
 * @param  [in]   o     Offset of seed count.
4695
 */
4696
static void mlkem_get_noise_x4_eta2_avx2(byte* rand, byte* seed, byte o)
4697
{
4698
    int i;
4699
    word64 state[25 * 4];
4700
4701
    for (i = 0; i < 4; i++) {
4702
        state[4*4 + i] = (word32)(0x1f00 + i + o);
4703
    }
4704
4705
    sha3_256_blocksx4_seed_avx2(state, seed);
4706
    mlkem_redistribute_16_rand_ins(state, rand + 0 * ETA2_RAND_SIZE,
4707
        rand + 1 * ETA2_RAND_SIZE, rand + 2 * ETA2_RAND_SIZE,
4708
        rand + 3 * ETA2_RAND_SIZE);
4709
4710
    /* state is secret-seeded; caller zeroizes rand. */
4711
#ifdef WOLFSSL_CHECK_MEM_ZERO
4712
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
4713
#endif
4714
    ForceZero(state, sizeof(state));
4715
#ifdef WOLFSSL_CHECK_MEM_ZERO
4716
    wc_MemZero_Check(state, sizeof(state));
4717
#endif
4718
}
4719
4720
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
4721
/* Get eight lanes of ETA2 random bytes using eight-way AVX-512 SHA3. Lane j
4722
 * uses seed count j and its output is written to rand + j * ETA2_RAND_SIZE.
4723
 *
4724
 * @param  [out]  rand  Random number byte array (8 * ETA2_RAND_SIZE bytes).
4725
 * @param  [in]   seed  Seed to generate random from.
4726
 * @return  0 on success.
4727
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4728
 *          WOLFSSL_SMALL_STACK is defined.
4729
 */
4730
static int mlkem_get_noise_x8_eta2_avx512(byte* rand, byte* seed)
4731
{
4732
    int i;
4733
    WC_DECLARE_VAR(state, word64, 25 * 8, 0);
4734
4735
    WC_ALLOC_VAR_EX(state, word64, 25 * 8, NULL, DYNAMIC_TYPE_TMP_BUFFER,
4736
        return MEMORY_E);
4737
4738
    for (i = 0; i < 8; i++) {
4739
        state[4*8 + i] = (word32)(0x1f00 + i);
4740
    }
4741
4742
    sha3_256_blocksx8_seed_avx512(state, seed);
4743
    mlkem_redistribute_16_rand_x8_avx512(state, rand, ETA2_RAND_SIZE);
4744
4745
    /* state is secret-seeded; caller zeroizes rand. */
4746
    ForceZero(state, sizeof(word64) * 25 * 8);
4747
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4748
    return 0;
4749
}
4750
#endif
4751
#endif
4752
4753
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \
4754
    defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
4755
/* Get noise/error by calculating random bytes and sampling to a binomial
4756
 * distribution. Values -2..2
4757
 *
4758
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4759
 *   ...
4760
 *  14:     e1[i] <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4761
 *   ...
4762
 *  17:     e2 <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
4763
 *   ...
4764
 *
4765
 * @param  [in, out]  prf   Pseudo-random function object.
4766
 * @param  [out]      p     Polynomial.
4767
 * @param  [in]       seed  Seed to use when calculating random.
4768
 * @return  0 on success.
4769
 */
4770
static int mlkem_get_noise_eta2_avx2(MLKEM_PRF_T* prf, sword16* p,
4771
    const byte* seed)
4772
{
4773
    word64 state[25];
4774
4775
    (void)prf;
4776
4777
    /* Put first WC_ML_KEM_SYM_SZ bytes of key into blank state. */
4778
    readUnalignedWords64(state, seed, WC_ML_KEM_SYM_SZ / sizeof(word64));
4779
    /* Last byte in with end of content marker. */
4780
    state[WC_ML_KEM_SYM_SZ / 8] = 0x1f00 | seed[WC_ML_KEM_SYM_SZ];
4781
    /* Set rest of state to 0. */
4782
    XMEMSET(state + WC_ML_KEM_SYM_SZ / 8 + 1, 0,
4783
        (25 - WC_ML_KEM_SYM_SZ / 8 - 1) * sizeof(word64));
4784
    /* ... except for rate marker. */
4785
    state[WC_SHA3_256_COUNT - 1] = W64LIT(0x8000000000000000);
4786
4787
    /* Perform a block operation on the state for next block of output. */
4788
#ifndef WC_SHA3_NO_ASM
4789
    if (IS_INTEL_BMI2(cpuid_flags)) {
4790
        sha3_block_bmi2(state);
4791
    }
4792
    else if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
4793
        sha3_block_avx2(state);
4794
        RESTORE_VECTOR_REGISTERS();
4795
    }
4796
    else
4797
#endif /* !WC_SHA3_NO_ASM */
4798
    {
4799
        BlockSha3(state);
4800
    }
4801
    mlkem_cbd_eta2_ins(p, (byte*)state);
4802
4803
    /* state holds secret noise. */
4804
#ifdef WOLFSSL_CHECK_MEM_ZERO
4805
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
4806
#endif
4807
    ForceZero(state, sizeof(state));
4808
#ifdef WOLFSSL_CHECK_MEM_ZERO
4809
    wc_MemZero_Check(state, sizeof(state));
4810
#endif
4811
    return 0;
4812
}
4813
#endif
4814
4815
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
4816
/* Get the noise/error by calculating random bytes.
4817
 *
4818
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
4819
 *   ...
4820
 *   9:     s[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
4821
 *   ...
4822
 *  13:     e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
4823
 *   ...
4824
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
4825
 *   ...
4826
 *  10:     y[i] <- SamplePolyCBD_eta_1(PRF_eta_1(r, N))
4827
 *   ...
4828
 *
4829
 * @param  [out]  rand  Random number byte array.
4830
 * @param  [in]   seed  Seed to generate random from.
4831
 */
4832
static void mlkem_get_noise_x4_eta3_avx2(byte* rand, byte* seed)
4833
{
4834
    word64 state[25 * 4];
4835
    int i;
4836
4837
    state[4*4 + 0] = 0x1f00 + 0;
4838
    state[4*4 + 1] = 0x1f00 + 1;
4839
    state[4*4 + 2] = 0x1f00 + 2;
4840
    state[4*4 + 3] = 0x1f00 + 3;
4841
4842
    sha3_256_blocksx4_seed_avx2(state, seed);
4843
    mlkem_redistribute_17_rand_ins(state, rand + 0 * PRF_RAND_SZ,
4844
        rand + 1 * PRF_RAND_SZ, rand + 2 * PRF_RAND_SZ,
4845
        rand + 3 * PRF_RAND_SZ);
4846
    i = SHA3_256_BYTES;
4847
    sha3_blocksx4_avx2(state);
4848
    mlkem_redistribute_8_rand_ins(state, rand + i + 0 * PRF_RAND_SZ,
4849
        rand + i + 1 * PRF_RAND_SZ, rand + i + 2 * PRF_RAND_SZ,
4850
        rand + i + 3 * PRF_RAND_SZ);
4851
4852
    /* state is secret-seeded; caller zeroizes rand. */
4853
#ifdef WOLFSSL_CHECK_MEM_ZERO
4854
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
4855
#endif
4856
    ForceZero(state, sizeof(state));
4857
#ifdef WOLFSSL_CHECK_MEM_ZERO
4858
    wc_MemZero_Check(state, sizeof(state));
4859
#endif
4860
}
4861
4862
/* Get the noise/error by calculating random bytes and sampling to a binomial
4863
 * distribution.
4864
 *
4865
 * @param  [in, out]  prf   Pseudo-random function object.
4866
 * @param  [out]      vec1  First Vector of polynomials.
4867
 * @param  [out]      vec2  Second Vector of polynomials.
4868
 * @param  [out]      poly  Polynomial.
4869
 * @param  [in, out]  seed  Seed to use when calculating random.
4870
 * @return  0 on success.
4871
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4872
 *          WOLFSSL_SMALL_STACK is defined.
4873
 */
4874
static int mlkem_get_noise_k2_avx2(MLKEM_PRF_T* prf, sword16* vec1,
4875
    sword16* vec2, sword16* poly, byte* seed)
4876
{
4877
    int ret = 0;
4878
    WC_DECLARE_VAR(rand, byte, 4 * PRF_RAND_SZ, 0);
4879
4880
    WC_ALLOC_VAR_EX(rand, byte, 4 * PRF_RAND_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER,
4881
        return MEMORY_E);
4882
4883
    mlkem_get_noise_x4_eta3_avx2(rand, seed);
4884
    mlkem_cbd_eta3_ins(vec1          , rand + 0 * PRF_RAND_SZ);
4885
    mlkem_cbd_eta3_ins(vec1 + MLKEM_N, rand + 1 * PRF_RAND_SZ);
4886
    if (poly == NULL) {
4887
        mlkem_cbd_eta3_ins(vec2          , rand + 2 * PRF_RAND_SZ);
4888
        mlkem_cbd_eta3_ins(vec2 + MLKEM_N, rand + 3 * PRF_RAND_SZ);
4889
    }
4890
    else {
4891
        mlkem_cbd_eta2_ins(vec2          , rand + 2 * PRF_RAND_SZ);
4892
        mlkem_cbd_eta2_ins(vec2 + MLKEM_N, rand + 3 * PRF_RAND_SZ);
4893
4894
        seed[WC_ML_KEM_SYM_SZ] = 4;
4895
        ret = mlkem_get_noise_eta2_avx2(prf, poly, seed);
4896
    }
4897
4898
    /* rand holds secret noise. */
4899
    ForceZero(rand, 4 * PRF_RAND_SZ);
4900
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4901
4902
    return ret;
4903
}
4904
4905
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
4906
/* Get eight lanes of ETA3-length random bytes using eight-way AVX-512 SHA3.
4907
 * Lane j uses seed count j; its output (two blocks) is written to
4908
 * rand + j * PRF_RAND_SZ. ETA2 samplers may read the same lanes (they consume
4909
 * fewer bytes of the identical SHAKE stream).
4910
 *
4911
 * @param  [out]  rand  Random number byte array (8 * PRF_RAND_SZ bytes).
4912
 * @param  [in]   seed  Seed to generate random from.
4913
 * @return  0 on success.
4914
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4915
 *          WOLFSSL_SMALL_STACK is defined.
4916
 */
4917
static int mlkem_get_noise_x8_eta3_avx512(byte* rand, byte* seed)
4918
{
4919
    int i;
4920
    WC_DECLARE_VAR(state, word64, 25 * 8, 0);
4921
4922
    WC_ALLOC_VAR_EX(state, word64, 25 * 8, NULL, DYNAMIC_TYPE_TMP_BUFFER,
4923
        return MEMORY_E);
4924
4925
    for (i = 0; i < 8; i++) {
4926
        state[4*8 + i] = (word32)(0x1f00 + i);
4927
    }
4928
4929
    sha3_256_blocksx8_seed_avx512(state, seed);
4930
    mlkem_redistribute_17_rand_x8_avx512(state, rand, PRF_RAND_SZ);
4931
    sha3_blocksx8_avx512(state);
4932
    mlkem_redistribute_8_rand_x8_avx512(state, rand + SHA3_256_BYTES,
4933
        PRF_RAND_SZ);
4934
4935
    /* state is secret-seeded; caller zeroizes rand. */
4936
    ForceZero(state, sizeof(word64) * 25 * 8);
4937
    WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4938
    return 0;
4939
}
4940
4941
/* Get the noise/error by calculating random bytes and sampling to a binomial
4942
 * distribution, using eight-way AVX-512 SHA3. The ETA3 vector lanes and (for
4943
 * encapsulation) the extra ETA2 polynomial share one eight-way batch - the
4944
 * ETA2 samples read fewer bytes of the same SHAKE-256 stream, so the extra
4945
 * polynomial no longer needs a separate single-lane hash.
4946
 *
4947
 * @param  [in, out]  prf   Pseudo-random function object.
4948
 * @param  [out]      vec1  First Vector of polynomials.
4949
 * @param  [out]      vec2  Second Vector of polynomials.
4950
 * @param  [out]      poly  Polynomial.
4951
 * @param  [in]       seed  Seed to use when calculating random.
4952
 * @return  0 on success.
4953
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
4954
 *          WOLFSSL_SMALL_STACK is defined.
4955
 */
4956
static int mlkem_get_noise_k2_avx512(MLKEM_PRF_T* prf, sword16* vec1,
4957
    sword16* vec2, sword16* poly, byte* seed)
4958
{
4959
    int ret;
4960
    WC_DECLARE_VAR(rand, byte, 8 * PRF_RAND_SZ, 0);
4961
4962
    (void)prf;
4963
4964
    WC_ALLOC_VAR_EX(rand, byte, 8 * PRF_RAND_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER,
4965
        return MEMORY_E);
4966
4967
    ret = mlkem_get_noise_x8_eta3_avx512(rand, seed);
4968
    if (ret == 0) {
4969
        mlkem_cbd_eta3_ins(vec1          , rand + 0 * PRF_RAND_SZ);
4970
        mlkem_cbd_eta3_ins(vec1 + MLKEM_N, rand + 1 * PRF_RAND_SZ);
4971
        if (poly == NULL) {
4972
            mlkem_cbd_eta3_ins(vec2          , rand + 2 * PRF_RAND_SZ);
4973
            mlkem_cbd_eta3_ins(vec2 + MLKEM_N, rand + 3 * PRF_RAND_SZ);
4974
        }
4975
        else {
4976
            mlkem_cbd_eta2_ins(vec2          , rand + 2 * PRF_RAND_SZ);
4977
            mlkem_cbd_eta2_ins(vec2 + MLKEM_N, rand + 3 * PRF_RAND_SZ);
4978
            mlkem_cbd_eta2_ins(poly          , rand + 4 * PRF_RAND_SZ);
4979
        }
4980
    }
4981
4982
    /* rand holds secret noise. */
4983
    ForceZero(rand, 8 * PRF_RAND_SZ);
4984
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
4985
    return ret;
4986
}
4987
#endif
4988
#endif
4989
4990
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
4991
/* Get the noise/error by calculating random bytes and sampling to a binomial
4992
 * distribution.
4993
 *
4994
 * @param  [out]      vec1  First Vector of polynomials.
4995
 * @param  [out]      vec2  Second Vector of polynomials.
4996
 * @param  [out]      poly  Polynomial.
4997
 * @param  [in]       seed  Seed to use when calculating random.
4998
 * @return  0 on success.
4999
 */
5000
static int mlkem_get_noise_k3_avx2(sword16* vec1, sword16* vec2, sword16* poly,
5001
    byte* seed)
5002
{
5003
    byte rand[4 * ETA2_RAND_SIZE];
5004
5005
    mlkem_get_noise_x4_eta2_avx2(rand, seed, 0);
5006
    mlkem_cbd_eta2_ins(vec1              , rand + 0 * ETA2_RAND_SIZE);
5007
    mlkem_cbd_eta2_ins(vec1 + 1 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5008
    mlkem_cbd_eta2_ins(vec1 + 2 * MLKEM_N, rand + 2 * ETA2_RAND_SIZE);
5009
    mlkem_cbd_eta2_ins(vec2              , rand + 3 * ETA2_RAND_SIZE);
5010
    mlkem_get_noise_x4_eta2_avx2(rand, seed, 4);
5011
    mlkem_cbd_eta2_ins(vec2 + 1 * MLKEM_N, rand + 0 * ETA2_RAND_SIZE);
5012
    mlkem_cbd_eta2_ins(vec2 + 2 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5013
    if (poly != NULL) {
5014
        mlkem_cbd_eta2_ins(poly, rand + 2 * ETA2_RAND_SIZE);
5015
    }
5016
5017
    /* rand holds secret noise. */
5018
#ifdef WOLFSSL_CHECK_MEM_ZERO
5019
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
5020
#endif
5021
    ForceZero(rand, sizeof(rand));
5022
#ifdef WOLFSSL_CHECK_MEM_ZERO
5023
    wc_MemZero_Check(rand, sizeof(rand));
5024
#endif
5025
    return 0;
5026
}
5027
5028
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5029
/* Get the noise/error by calculating random bytes and sampling to a binomial
5030
 * distribution, using eight-way AVX-512 SHA3. Eight ETA2 lanes are produced
5031
 * in one batch; up to seven are consumed (six when poly is NULL).
5032
 *
5033
 * @param  [out]  vec1  First Vector of polynomials.
5034
 * @param  [out]  vec2  Second Vector of polynomials.
5035
 * @param  [out]  poly  Polynomial.
5036
 * @param  [in]   seed  Seed to use when calculating random.
5037
 * @return  0 on success.
5038
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
5039
 *          WOLFSSL_SMALL_STACK is defined.
5040
 */
5041
static int mlkem_get_noise_k3_avx512(sword16* vec1, sword16* vec2,
5042
    sword16* poly, byte* seed)
5043
{
5044
    int ret;
5045
    WC_DECLARE_VAR(rand, byte, 8 * ETA2_RAND_SIZE, 0);
5046
5047
    WC_ALLOC_VAR_EX(rand, byte, 8 * ETA2_RAND_SIZE, NULL,
5048
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
5049
5050
    ret = mlkem_get_noise_x8_eta2_avx512(rand, seed);
5051
    if (ret == 0) {
5052
        mlkem_cbd_eta2_ins(vec1              , rand + 0 * ETA2_RAND_SIZE);
5053
        mlkem_cbd_eta2_ins(vec1 + 1 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5054
        mlkem_cbd_eta2_ins(vec1 + 2 * MLKEM_N, rand + 2 * ETA2_RAND_SIZE);
5055
        mlkem_cbd_eta2_ins(vec2              , rand + 3 * ETA2_RAND_SIZE);
5056
        mlkem_cbd_eta2_ins(vec2 + 1 * MLKEM_N, rand + 4 * ETA2_RAND_SIZE);
5057
        mlkem_cbd_eta2_ins(vec2 + 2 * MLKEM_N, rand + 5 * ETA2_RAND_SIZE);
5058
        if (poly != NULL) {
5059
            mlkem_cbd_eta2_ins(poly, rand + 6 * ETA2_RAND_SIZE);
5060
        }
5061
    }
5062
5063
    /* rand holds secret noise. */
5064
    ForceZero(rand, 8 * ETA2_RAND_SIZE);
5065
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
5066
    return ret;
5067
}
5068
#endif
5069
#endif
5070
5071
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
5072
/* Get the noise/error by calculating random bytes and sampling to a binomial
5073
 * distribution.
5074
 *
5075
 * @param  [in, out]  prf   Pseudo-random function object.
5076
 * @param  [out]      vec1  First Vector of polynomials.
5077
 * @param  [out]      vec2  Second Vector of polynomials.
5078
 * @param  [out]      poly  Polynomial.
5079
 * @param  [in, out]  seed  Seed to use when calculating random.
5080
 * @return  0 on success.
5081
 */
5082
static int mlkem_get_noise_k4_avx2(MLKEM_PRF_T* prf, sword16* vec1,
5083
    sword16* vec2, sword16* poly, byte* seed)
5084
{
5085
    int ret = 0;
5086
    byte rand[4 * ETA2_RAND_SIZE];
5087
5088
    (void)prf;
5089
5090
    mlkem_get_noise_x4_eta2_avx2(rand, seed, 0);
5091
    mlkem_cbd_eta2_ins(vec1              , rand + 0 * ETA2_RAND_SIZE);
5092
    mlkem_cbd_eta2_ins(vec1 + 1 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5093
    mlkem_cbd_eta2_ins(vec1 + 2 * MLKEM_N, rand + 2 * ETA2_RAND_SIZE);
5094
    mlkem_cbd_eta2_ins(vec1 + 3 * MLKEM_N, rand + 3 * ETA2_RAND_SIZE);
5095
    mlkem_get_noise_x4_eta2_avx2(rand, seed, 4);
5096
    mlkem_cbd_eta2_ins(vec2              , rand + 0 * ETA2_RAND_SIZE);
5097
    mlkem_cbd_eta2_ins(vec2 + 1 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5098
    mlkem_cbd_eta2_ins(vec2 + 2 * MLKEM_N, rand + 2 * ETA2_RAND_SIZE);
5099
    mlkem_cbd_eta2_ins(vec2 + 3 * MLKEM_N, rand + 3 * ETA2_RAND_SIZE);
5100
    if (poly != NULL) {
5101
        seed[WC_ML_KEM_SYM_SZ] = 8;
5102
        ret = mlkem_get_noise_eta2_avx2(prf, poly, seed);
5103
    }
5104
5105
    /* rand holds secret noise. */
5106
#ifdef WOLFSSL_CHECK_MEM_ZERO
5107
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
5108
#endif
5109
    ForceZero(rand, sizeof(rand));
5110
#ifdef WOLFSSL_CHECK_MEM_ZERO
5111
    wc_MemZero_Check(rand, sizeof(rand));
5112
#endif
5113
    return ret;
5114
}
5115
5116
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5117
/* Get the noise/error by calculating random bytes and sampling to a binomial
5118
 * distribution, using eight-way AVX-512 SHA3. The eight ETA2 vector lanes are
5119
 * produced in one batch; the extra polynomial uses a single SHA3 state.
5120
 *
5121
 * @param  [in, out]  prf   Pseudo-random function object.
5122
 * @param  [out]      vec1  First Vector of polynomials.
5123
 * @param  [out]      vec2  Second Vector of polynomials.
5124
 * @param  [out]      poly  Polynomial.
5125
 * @param  [in, out]  seed  Seed to use when calculating random.
5126
 * @return  0 on success.
5127
 * @return  MEMORY_E when dynamic memory allocation fails. Only possible when
5128
 *          WOLFSSL_SMALL_STACK is defined.
5129
 */
5130
static int mlkem_get_noise_k4_avx512(MLKEM_PRF_T* prf, sword16* vec1,
5131
    sword16* vec2, sword16* poly, byte* seed)
5132
{
5133
    int ret;
5134
    WC_DECLARE_VAR(rand, byte, 8 * ETA2_RAND_SIZE, 0);
5135
5136
    (void)prf;
5137
5138
    WC_ALLOC_VAR_EX(rand, byte, 8 * ETA2_RAND_SIZE, NULL,
5139
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
5140
5141
    ret = mlkem_get_noise_x8_eta2_avx512(rand, seed);
5142
    if (ret == 0) {
5143
        mlkem_cbd_eta2_ins(vec1              , rand + 0 * ETA2_RAND_SIZE);
5144
        mlkem_cbd_eta2_ins(vec1 + 1 * MLKEM_N, rand + 1 * ETA2_RAND_SIZE);
5145
        mlkem_cbd_eta2_ins(vec1 + 2 * MLKEM_N, rand + 2 * ETA2_RAND_SIZE);
5146
        mlkem_cbd_eta2_ins(vec1 + 3 * MLKEM_N, rand + 3 * ETA2_RAND_SIZE);
5147
        mlkem_cbd_eta2_ins(vec2              , rand + 4 * ETA2_RAND_SIZE);
5148
        mlkem_cbd_eta2_ins(vec2 + 1 * MLKEM_N, rand + 5 * ETA2_RAND_SIZE);
5149
        mlkem_cbd_eta2_ins(vec2 + 2 * MLKEM_N, rand + 6 * ETA2_RAND_SIZE);
5150
        mlkem_cbd_eta2_ins(vec2 + 3 * MLKEM_N, rand + 7 * ETA2_RAND_SIZE);
5151
        if (poly != NULL) {
5152
            seed[WC_ML_KEM_SYM_SZ] = 8;
5153
            ret = mlkem_get_noise_eta2_avx2(prf, poly, seed);
5154
        }
5155
    }
5156
5157
    /* rand holds secret noise. */
5158
    ForceZero(rand, 8 * ETA2_RAND_SIZE);
5159
    WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER);
5160
    return ret;
5161
}
5162
#endif
5163
#endif
5164
#endif /* USE_INTEL_SPEEDUP */
5165
5166
#if defined(__aarch64__) && defined(WOLFSSL_ARMASM)
5167
5168
#define PRF_RAND_SZ   (2 * SHA3_256_BYTES)
5169
5170
/* Get the noise/error by calculating random bytes.
5171
 *
5172
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
5173
 *   ...
5174
 *  14:     e1[i] <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
5175
 *   ...
5176
 *  17:     e2 <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
5177
 *   ...
5178
 *
5179
 * @param  [out]  rand  Random number word64 array. Used as the SHAKE-256
5180
 *                      state - the random is squeezed into it in place.
5181
 * @param  [in]   seed  Seed to generate random from.
5182
 * @param  [in]   o     Offset of seed count.
5183
 */
5184
static void mlkem_get_noise_x3_eta2_aarch64(word64* rand, byte* seed, byte o)
5185
{
5186
    /* Only rand[i*25 + 4] is set here - the rest of the state is zeroed in
5187
     * registers by the assembly. */
5188
    rand[0*25 + 4] = 0x1f00 + 0 + o;
5189
    rand[1*25 + 4] = 0x1f00 + 1 + o;
5190
    rand[2*25 + 4] = 0x1f00 + 2 + o;
5191
5192
    mlkem_shake256_blocksx3_seed(rand, seed);
5193
}
5194
5195
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
5196
/* Get the noise/error by calculating random bytes.
5197
 *
5198
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
5199
 *   ...
5200
 *   9:     s[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
5201
 *   ...
5202
 *  13:     e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
5203
 *   ...
5204
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
5205
 *   ...
5206
 *  10:     y[i] <- SamplePolyCBD_eta_1(PRF_eta_1(r, N))
5207
 *   ...
5208
 *
5209
 * @param  [out]  rand  Random number byte array.
5210
 * @param  [in]   seed  Seed to generate random from.
5211
 * @param  [in]   o     Offset of seed count.
5212
 */
5213
static void mlkem_get_noise_x3_eta3_aarch64(byte* rand, byte* seed, byte o)
5214
{
5215
    /* Only state[i*25 + 4] is read by the assembly - the rest of the state is
5216
     * zeroed in registers there. */
5217
    word64 state[3 * 25];
5218
5219
    state[0*25 + 4] = 0x1f00 + 0 + o;
5220
    state[1*25 + 4] = 0x1f00 + 1 + o;
5221
    state[2*25 + 4] = 0x1f00 + 2 + o;
5222
5223
    mlkem_shake256_blocksx3_seed(state, seed);
5224
    XMEMCPY(rand + 0 * ETA3_RAND_SIZE, state + 0*25, SHA3_256_BYTES);
5225
    XMEMCPY(rand + 1 * ETA3_RAND_SIZE, state + 1*25, SHA3_256_BYTES);
5226
    XMEMCPY(rand + 2 * ETA3_RAND_SIZE, state + 2*25, SHA3_256_BYTES);
5227
    mlkem_sha3_blocksx3(state);
5228
    rand += SHA3_256_BYTES;
5229
    XMEMCPY(rand + 0 * ETA3_RAND_SIZE, state + 0*25,
5230
        ETA3_RAND_SIZE - SHA3_256_BYTES);
5231
    XMEMCPY(rand + 1 * ETA3_RAND_SIZE, state + 1*25,
5232
        ETA3_RAND_SIZE - SHA3_256_BYTES);
5233
    XMEMCPY(rand + 2 * ETA3_RAND_SIZE, state + 2*25,
5234
        ETA3_RAND_SIZE - SHA3_256_BYTES);
5235
5236
    /* state is secret-seeded; caller zeroizes rand. */
5237
#ifdef WOLFSSL_CHECK_MEM_ZERO
5238
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
5239
#endif
5240
    ForceZero(state, sizeof(state));
5241
#ifdef WOLFSSL_CHECK_MEM_ZERO
5242
    wc_MemZero_Check(state, sizeof(state));
5243
#endif
5244
}
5245
5246
/* Get the noise/error by calculating random bytes.
5247
 *
5248
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
5249
 *   ...
5250
 *  13:     e[i] <- SamplePolyCBD_eta_1(PRF_eta_1(sigma, N))
5251
 *   ...
5252
 *
5253
 * @param  [out]  rand  Random number byte array.
5254
 * @param  [in]   seed  Seed to generate random from.
5255
 * @param  [in]   o     Offset of seed count.
5256
 */
5257
static void mlkem_get_noise_eta3_aarch64(byte* rand, byte* seed, byte o)
5258
{
5259
    /* ETA3_RAND_SIZE is larger than the SHAKE-256 rate - two squeezes are
5260
     * needed, so the state cannot be squeezed in place over the output. */
5261
    word64 state[25];
5262
5263
    readUnalignedWords64(state, seed, 4);
5264
    state[4] = 0x1f00 + o;
5265
    XMEMSET(state + 5, 0, sizeof(*state) * (25 - 5));
5266
    state[16] = W64LIT(0x8000000000000000);
5267
    BlockSha3(state);
5268
    XMEMCPY(rand                 , state, SHA3_256_BYTES);
5269
    BlockSha3(state);
5270
    XMEMCPY(rand + SHA3_256_BYTES, state, ETA3_RAND_SIZE - SHA3_256_BYTES);
5271
5272
    /* state is secret-seeded; caller zeroizes rand. */
5273
#ifdef WOLFSSL_CHECK_MEM_ZERO
5274
    wc_MemZero_Add("mlkem_poly state", state, sizeof(state));
5275
#endif
5276
    ForceZero(state, sizeof(state));
5277
#ifdef WOLFSSL_CHECK_MEM_ZERO
5278
    wc_MemZero_Check(state, sizeof(state));
5279
#endif
5280
}
5281
5282
/* Get the noise/error by calculating random bytes and sampling to a binomial
5283
 * distribution.
5284
 *
5285
 * @param  [out]      vec1  First Vector of polynomials.
5286
 * @param  [out]      vec2  Second Vector of polynomials.
5287
 * @param  [out]      poly  Polynomial.
5288
 * @param  [in]       seed  Seed to use when calculating random.
5289
 * @return  0 on success.
5290
 */
5291
static int mlkem_get_noise_k2_aarch64(sword16* vec1, sword16* vec2,
5292
    sword16* poly, byte* seed)
5293
{
5294
    int ret = 0;
5295
    word64 rand[3 * 25];
5296
5297
    mlkem_get_noise_x3_eta3_aarch64((byte*)rand, seed, 0);
5298
    mlkem_cbd_eta3(vec1          , (byte*)rand + 0 * ETA3_RAND_SIZE);
5299
    mlkem_cbd_eta3(vec1 + MLKEM_N, (byte*)rand + 1 * ETA3_RAND_SIZE);
5300
    if (poly == NULL) {
5301
        mlkem_cbd_eta3(vec2          , (byte*)rand + 2 * ETA3_RAND_SIZE);
5302
        mlkem_get_noise_eta3_aarch64((byte*)rand, seed, 3);
5303
        mlkem_cbd_eta3(vec2 + MLKEM_N, (byte*)rand                     );
5304
    }
5305
    else {
5306
        mlkem_get_noise_x3_eta2_aarch64(rand, seed, 2);
5307
        mlkem_cbd_eta2(vec2          , (byte*)rand + 0 * 25 * 8);
5308
        mlkem_cbd_eta2(vec2 + MLKEM_N, (byte*)rand + 1 * 25 * 8);
5309
        mlkem_cbd_eta2(poly          , (byte*)rand + 2 * 25 * 8);
5310
    }
5311
5312
    /* rand holds secret noise. */
5313
#ifdef WOLFSSL_CHECK_MEM_ZERO
5314
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
5315
#endif
5316
    ForceZero(rand, sizeof(rand));
5317
#ifdef WOLFSSL_CHECK_MEM_ZERO
5318
    wc_MemZero_Check(rand, sizeof(rand));
5319
#endif
5320
    return ret;
5321
}
5322
#endif
5323
5324
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
5325
/* Get the noise/error by calculating random bytes.
5326
 *
5327
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r)
5328
 *   ...
5329
 *  14:     e1[i] <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
5330
 *   ...
5331
 *  17:     e2 <- SamplePolyCBD_eta_2(PRF_eta_2(r, N))
5332
 *   ...
5333
 *
5334
 * @param  [out]  rand  Random number word64 array.
5335
 * @param  [in]   seed  Seed to generate random from.
5336
 * @param  [in]   o     Offset of seed count.
5337
 */
5338
static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o)
5339
{
5340
    readUnalignedWords64(rand, seed, 4);
5341
    /* Transposed value same as not. */
5342
    rand[4] = 0x1f00 + o;
5343
    XMEMSET(rand + 5, 0, sizeof(*rand) * (25 - 5));
5344
    rand[16] = W64LIT(0x8000000000000000);
5345
    BlockSha3(rand);
5346
}
5347
5348
/* Get the noise/error by calculating random bytes and sampling to a binomial
5349
 * distribution.
5350
 *
5351
 * @param  [out]      vec1  First Vector of polynomials.
5352
 * @param  [out]      vec2  Second Vector of polynomials.
5353
 * @param  [out]      poly  Polynomial.
5354
 * @param  [in]       seed  Seed to use when calculating random.
5355
 * @return  0 on success.
5356
 */
5357
static int mlkem_get_noise_k3_aarch64(sword16* vec1, sword16* vec2,
5358
     sword16* poly, byte* seed)
5359
{
5360
    word64 rand[3 * 25];
5361
5362
    mlkem_get_noise_x3_eta2_aarch64(rand, seed, 0);
5363
    mlkem_cbd_eta2(vec1              , (byte*)rand + 0 * 25 * 8);
5364
    mlkem_cbd_eta2(vec1 + 1 * MLKEM_N, (byte*)rand + 1 * 25 * 8);
5365
    mlkem_cbd_eta2(vec1 + 2 * MLKEM_N, (byte*)rand + 2 * 25 * 8);
5366
    mlkem_get_noise_x3_eta2_aarch64(rand, seed, 3);
5367
    mlkem_cbd_eta2(vec2              , (byte*)rand + 0 * 25 * 8);
5368
    mlkem_cbd_eta2(vec2 + 1 * MLKEM_N, (byte*)rand + 1 * 25 * 8);
5369
    mlkem_cbd_eta2(vec2 + 2 * MLKEM_N, (byte*)rand + 2 * 25 * 8);
5370
    if (poly != NULL) {
5371
        mlkem_get_noise_eta2_aarch64(rand, seed, 6);
5372
        mlkem_cbd_eta2(poly              , (byte*)rand + 0 * 25 * 8);
5373
    }
5374
5375
    /* rand holds secret noise. */
5376
#ifdef WOLFSSL_CHECK_MEM_ZERO
5377
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
5378
#endif
5379
    ForceZero(rand, sizeof(rand));
5380
#ifdef WOLFSSL_CHECK_MEM_ZERO
5381
    wc_MemZero_Check(rand, sizeof(rand));
5382
#endif
5383
    return 0;
5384
}
5385
#endif
5386
5387
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
5388
/* Get the noise/error by calculating random bytes and sampling to a binomial
5389
 * distribution.
5390
 *
5391
 * @param  [out]      vec1  First Vector of polynomials.
5392
 * @param  [out]      vec2  Second Vector of polynomials.
5393
 * @param  [out]      poly  Polynomial.
5394
 * @param  [in]       seed  Seed to use when calculating random.
5395
 * @return  0 on success.
5396
 */
5397
static int mlkem_get_noise_k4_aarch64(sword16* vec1, sword16* vec2,
5398
    sword16* poly, byte* seed)
5399
{
5400
    int ret = 0;
5401
    word64 rand[3 * 25];
5402
5403
    mlkem_get_noise_x3_eta2_aarch64(rand, seed, 0);
5404
    mlkem_cbd_eta2(vec1              , (byte*)rand + 0 * 25 * 8);
5405
    mlkem_cbd_eta2(vec1 + 1 * MLKEM_N, (byte*)rand + 1 * 25 * 8);
5406
    mlkem_cbd_eta2(vec1 + 2 * MLKEM_N, (byte*)rand + 2 * 25 * 8);
5407
    mlkem_get_noise_x3_eta2_aarch64(rand, seed, 3);
5408
    mlkem_cbd_eta2(vec1 + 3 * MLKEM_N, (byte*)rand + 0 * 25 * 8);
5409
    mlkem_cbd_eta2(vec2              , (byte*)rand + 1 * 25 * 8);
5410
    mlkem_cbd_eta2(vec2 + 1 * MLKEM_N, (byte*)rand + 2 * 25 * 8);
5411
    mlkem_get_noise_x3_eta2_aarch64(rand, seed, 6);
5412
    mlkem_cbd_eta2(vec2 + 2 * MLKEM_N, (byte*)rand + 0 * 25 * 8);
5413
    mlkem_cbd_eta2(vec2 + 3 * MLKEM_N, (byte*)rand + 1 * 25 * 8);
5414
    if (poly != NULL) {
5415
        mlkem_cbd_eta2(poly,               (byte*)rand + 2 * 25 * 8);
5416
    }
5417
5418
    /* rand holds secret noise. */
5419
#ifdef WOLFSSL_CHECK_MEM_ZERO
5420
    wc_MemZero_Add("mlkem_poly rand", rand, sizeof(rand));
5421
#endif
5422
    ForceZero(rand, sizeof(rand));
5423
#ifdef WOLFSSL_CHECK_MEM_ZERO
5424
    wc_MemZero_Check(rand, sizeof(rand));
5425
#endif
5426
    return ret;
5427
}
5428
#endif
5429
#endif /* __aarch64__ && WOLFSSL_ARMASM */
5430
5431
#if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM))
5432
5433
/* Get the noise/error by calculating random bytes and sampling to a binomial
5434
 * distribution.
5435
 *
5436
 * @param  [in, out]  prf   Pseudo-random function object.
5437
 * @param  [in]       k     Number of polynomials in vector.
5438
 * @param  [out]      vec1  First Vector of polynomials.
5439
 * @param  [in]       eta1  Size of noise/error integers with first vector.
5440
 * @param  [out]      vec2  Second Vector of polynomials.
5441
 * @param  [in]       eta2  Size of noise/error integers with second vector.
5442
 * @param  [out]      poly  Polynomial.
5443
 * @param  [in, out]  seed  Seed to use when calculating random.
5444
 * @return  0 on success.
5445
 */
5446
static int mlkem_get_noise_c(MLKEM_PRF_T* prf, int k, sword16* vec1, int eta1,
5447
    sword16* vec2, int eta2, sword16* poly, byte* seed)
5448
4.49k
{
5449
4.49k
    int ret = 0;
5450
4.49k
    int i;
5451
5452
    /* First noise generation has a seed with 0x00 appended. */
5453
4.49k
    seed[WC_ML_KEM_SYM_SZ] = 0;
5454
    /* Generate noise as private key. */
5455
18.1k
    for (i = 0; (ret == 0) && (i < k); i++) {
5456
        /* Generate noise for each dimension of vector. */
5457
13.6k
        ret = mlkem_get_noise_eta1_c(prf, vec1 + i * MLKEM_N, seed, (byte)eta1);
5458
        /* Increment value of appended byte. */
5459
13.6k
        seed[WC_ML_KEM_SYM_SZ]++;
5460
13.6k
    }
5461
4.49k
    if ((ret == 0) && (vec2 != NULL)) {
5462
        /* Generate noise for error. */
5463
18.1k
        for (i = 0; (ret == 0) && (i < k); i++) {
5464
            /* Generate noise for each dimension of vector. */
5465
13.6k
            ret = mlkem_get_noise_eta1_c(prf, vec2 + i * MLKEM_N, seed,
5466
13.6k
                (byte)eta2);
5467
            /* Increment value of appended byte. */
5468
13.6k
            seed[WC_ML_KEM_SYM_SZ]++;
5469
13.6k
        }
5470
4.49k
    }
5471
0
    else {
5472
0
        seed[WC_ML_KEM_SYM_SZ] = WC_OCTET(2 * k);
5473
0
    }
5474
4.49k
    if ((ret == 0) && (poly != NULL)) {
5475
        /* Generating random error polynomial. */
5476
0
        ret = mlkem_get_noise_eta2_c(prf, poly, seed);
5477
0
    }
5478
5479
4.49k
    return ret;
5480
4.49k
}
5481
5482
#endif /* !(__aarch64__ && WOLFSSL_ARMASM) */
5483
5484
/* Get the noise/error by calculating random bytes and sampling to a binomial
5485
 * distribution.
5486
 *
5487
 * @param  [in, out]  prf   Pseudo-random function object.
5488
 * @param  [in]       k     Number of polynomials in vector.
5489
 * @param  [out]      vec1  First Vector of polynomials.
5490
 * @param  [out]      vec2  Second Vector of polynomials.
5491
 * @param  [out]      poly  Polynomial.
5492
 * @param  [in, out]  seed  Seed to use when calculating random.
5493
 * @return  0 on success.
5494
 */
5495
int mlkem_get_noise(MLKEM_PRF_T* prf, int k, sword16* vec1, sword16* vec2,
5496
    sword16* poly, byte* seed)
5497
4.49k
{
5498
4.49k
    int ret;
5499
5500
4.49k
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
5501
4.49k
    if (k == WC_ML_KEM_512_K) {
5502
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
5503
        ret = mlkem_get_noise_k2_aarch64(vec1, vec2, poly, seed);
5504
#else
5505
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
5506
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5507
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5508
            ret = mlkem_get_noise_k2_avx512(prf, vec1, vec2, poly, seed);
5509
            RESTORE_VECTOR_REGISTERS();
5510
        }
5511
        else
5512
    #endif
5513
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5514
            ret = mlkem_get_noise_k2_avx2(prf, vec1, vec2, poly, seed);
5515
            RESTORE_VECTOR_REGISTERS();
5516
        }
5517
        else
5518
    #endif
5519
0
        if (poly == NULL) {
5520
0
            ret = mlkem_get_noise_c(prf, k, vec1, MLKEM_CBD_ETA3, vec2,
5521
0
                MLKEM_CBD_ETA3, NULL, seed);
5522
0
        }
5523
0
        else {
5524
0
            ret = mlkem_get_noise_c(prf, k, vec1, MLKEM_CBD_ETA3, vec2,
5525
0
                MLKEM_CBD_ETA2, poly, seed);
5526
0
        }
5527
0
#endif
5528
0
    }
5529
4.49k
    else
5530
4.49k
#endif
5531
4.49k
#if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
5532
4.49k
    if (k == WC_ML_KEM_768_K) {
5533
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
5534
        ret = mlkem_get_noise_k3_aarch64(vec1, vec2, poly, seed);
5535
#else
5536
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
5537
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5538
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5539
            ret = mlkem_get_noise_k3_avx512(vec1, vec2, poly, seed);
5540
            RESTORE_VECTOR_REGISTERS();
5541
        }
5542
        else
5543
    #endif
5544
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5545
            ret = mlkem_get_noise_k3_avx2(vec1, vec2, poly, seed);
5546
            RESTORE_VECTOR_REGISTERS();
5547
        }
5548
        else
5549
    #endif
5550
4.34k
        {
5551
4.34k
            ret = mlkem_get_noise_c(prf, k, vec1, MLKEM_CBD_ETA2, vec2,
5552
4.34k
                MLKEM_CBD_ETA2, poly, seed);
5553
4.34k
        }
5554
4.34k
#endif
5555
4.34k
    }
5556
145
    else
5557
145
#endif
5558
145
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
5559
145
    if (k == WC_ML_KEM_1024_K) {
5560
#if defined(WOLFSSL_ARMASM) && defined(__aarch64__)
5561
        ret = mlkem_get_noise_k4_aarch64(vec1, vec2, poly, seed);
5562
#else
5563
    #if defined(USE_INTEL_SPEEDUP) && !defined(WC_SHA3_NO_ASM)
5564
    #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5565
        if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5566
            ret = mlkem_get_noise_k4_avx512(prf, vec1, vec2, poly, seed);
5567
            RESTORE_VECTOR_REGISTERS();
5568
        }
5569
        else
5570
    #endif
5571
        if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5572
            ret = mlkem_get_noise_k4_avx2(prf, vec1, vec2, poly, seed);
5573
            RESTORE_VECTOR_REGISTERS();
5574
        }
5575
        else
5576
    #endif
5577
145
        {
5578
145
            ret = mlkem_get_noise_c(prf, k, vec1, MLKEM_CBD_ETA2, vec2,
5579
145
                MLKEM_CBD_ETA2, poly, seed);
5580
145
        }
5581
145
#endif
5582
145
    }
5583
0
    else
5584
0
#endif
5585
0
    {
5586
0
        ret = BAD_STATE_E;
5587
0
    }
5588
5589
4.49k
    (void)prf;
5590
5591
4.49k
    return ret;
5592
4.49k
}
5593
5594
#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
5595
    defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
5596
/* Get the noise/error by calculating random bytes and sampling to a binomial
5597
 * distribution.
5598
 *
5599
 * @param  [in, out]  prf   Pseudo-random function object.
5600
 * @param  [in]       k     Number of polynomials in vector.
5601
 * @param  [out]      vec2  Second Vector of polynomials.
5602
 * @param  [in, out]  seed  Seed to use when calculating random.
5603
 * @param  [in]       i     Index of vector to generate.
5604
 * @param  [in]       make  Indicates generation is for making a key.
5605
 * @return  0 on success.
5606
 */
5607
static int mlkem_get_noise_i(MLKEM_PRF_T* prf, int k, sword16* vec2,
5608
    byte* seed, int i, int make)
5609
{
5610
    int ret;
5611
5612
    /* Initialize the PRF (generating matrix A leaves it in uninitialized
5613
     * state). */
5614
    mlkem_prf_init(prf);
5615
5616
    /* Set index of polynomial of second vector into seed. */
5617
    seed[WC_ML_KEM_SYM_SZ] = WC_OCTET(k + i);
5618
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
5619
    if ((k == WC_ML_KEM_512_K) && make) {
5620
        ret = mlkem_get_noise_eta1_c(prf, vec2, seed, MLKEM_CBD_ETA3);
5621
    }
5622
    else
5623
#endif
5624
    {
5625
        ret = mlkem_get_noise_eta1_c(prf, vec2, seed, MLKEM_CBD_ETA2);
5626
    }
5627
5628
    (void)make;
5629
    return ret;
5630
}
5631
#endif
5632
5633
/******************************************************************************/
5634
5635
#if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM))
5636
/* Compare two byte arrays of equal size.
5637
 *
5638
 * @param [in]  a   First array to compare.
5639
 * @param [in]  b   Second array to compare.
5640
 * @param [in]  sz  Size of arrays in bytes.
5641
 * @return  0 on success.
5642
 * @return  -1 on failure.
5643
 */
5644
static int mlkem_cmp_c(const byte* a, const byte* b, int sz)
5645
0
{
5646
0
    int i;
5647
0
    byte r = 0;
5648
5649
    /* Constant time comparison of the encapsulated message and cipher text. */
5650
0
    for (i = 0; i < sz; i++) {
5651
0
        r |= a[i] ^ b[i];
5652
0
    }
5653
0
    return (int)(0 - ((-(word32)r) >> 31));
5654
0
}
5655
#endif
5656
5657
/* Compare two byte arrays of equal size.
5658
 *
5659
 * @param [in]  a   First array to compare.
5660
 * @param [in]  b   Second array to compare.
5661
 * @param [in]  sz  Size of arrays in bytes.
5662
 * @return  0 on success.
5663
 * @return  -1 on failure.
5664
 */
5665
int mlkem_cmp(const byte* a, const byte* b, int sz)
5666
0
{
5667
#if defined(__aarch64__) && defined(WOLFSSL_ARMASM)
5668
    return mlkem_cmp_neon(a, b, sz);
5669
#else
5670
0
    int fail;
5671
5672
#ifdef USE_INTEL_SPEEDUP
5673
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5674
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5675
        fail = mlkem_cmp_avx512(a, b, sz);
5676
        RESTORE_VECTOR_REGISTERS();
5677
    }
5678
    else
5679
#endif
5680
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5681
        fail = mlkem_cmp_avx2(a, b, sz);
5682
        RESTORE_VECTOR_REGISTERS();
5683
    }
5684
    else
5685
#endif
5686
0
    {
5687
0
        fail = mlkem_cmp_c(a, b, sz);
5688
0
    }
5689
5690
0
    return fail;
5691
0
#endif
5692
0
}
5693
5694
/******************************************************************************/
5695
5696
#if !defined(WOLFSSL_ARMASM)
5697
5698
/* Conditional subtraction of q to each coefficient of a polynomial.
5699
 *
5700
 * FIPS 203, Section 4.2.1, Compression and decompression
5701
 *
5702
 * @param  [in, out]  p  Polynomial.
5703
 */
5704
static MLKEM_NOINLINE void mlkem_csubq_c(sword16* p)
5705
40.8k
{
5706
40.8k
    unsigned int i;
5707
5708
10.4M
    for (i = 0; i < MLKEM_N; ++i) {
5709
10.4M
        sword16 t = (sword16)(p[i] - MLKEM_Q);
5710
        /* When top bit set, -ve number - need to add q back. */
5711
10.4M
        p[i] = (sword16)(((word16)(-((word16)t >> 15)) & MLKEM_Q) +
5712
10.4M
            (word16)t);
5713
10.4M
    }
5714
40.8k
}
5715
5716
#elif defined(__aarch64__)
5717
5718
/* Conditional subtraction of q to each coefficient of a polynomial.
5719
 *
5720
 * FIPS 203, Section 4.2.1, Compression and decompression
5721
 *
5722
 * @param  [in, out]  p  Polynomial.
5723
 */
5724
#define mlkem_csubq_c   mlkem_csubq_neon
5725
5726
#elif defined(WOLFSSL_ARMASM_THUMB2)
5727
5728
/* Conditional subtraction of q to each coefficient of a polynomial.
5729
 *
5730
 * FIPS 203, Section 4.2.1, Compression and decompression
5731
 *
5732
 * @param  [in, out]  p  Polynomial.
5733
 */
5734
#define mlkem_csubq_c   mlkem_thumb2_csubq
5735
5736
#else
5737
5738
/* Conditional subtraction of q to each coefficient of a polynomial.
5739
 *
5740
 * FIPS 203, Section 4.2.1, Compression and decompression
5741
 *
5742
 * @param  [in, out]  p  Polynomial.
5743
 */
5744
#define mlkem_csubq_c   mlkem_arm32_csubq
5745
5746
#endif
5747
5748
/******************************************************************************/
5749
5750
#if defined(CONV_WITH_DIV) || !defined(WORD64_AVAILABLE)
5751
5752
/* Compress value.
5753
 *
5754
 * Uses div operator that may be slow and not constant-time.
5755
 *
5756
 * FIPS 203, Section 4.2.1, Compression and decompression
5757
 *
5758
 * @param  [in]  v  Vector of polynomials.
5759
 * @param  [in]  i  Index of polynomial in vector.
5760
 * @param  [in]  j  Index into polynomial.
5761
 * @param  [in]  k  Offset from indices.
5762
 * @param  [in]  s  Shift amount to apply to value being compressed.
5763
 * @param  [in]  m  Mask to apply get the required number of bits.
5764
 * @return  Compressed value.
5765
 */
5766
#define TO_COMP_WORD_VEC(v, i, j, k, s, m) \
5767
    ((((word32)v[i * MLKEM_N + j + k] << s) + MLKEM_Q_HALF) / MLKEM_Q) & m
5768
5769
/* Compress value to 10 bits.
5770
 *
5771
 * Uses div operator that may be slow and not constant-time.
5772
 *
5773
 * FIPS 203, Section 4.2.1, Compression and decompression
5774
 *
5775
 * @param  [in]  v  Vector of polynomials.
5776
 * @param  [in]  i  Index of polynomial in vector.
5777
 * @param  [in]  j  Index into polynomial.
5778
 * @param  [in]  k  Offset from indices.
5779
 * @return  Compressed value.
5780
 */
5781
#define TO_COMP_WORD_10(v, i, j, k) \
5782
    TO_COMP_WORD_VEC(v, i, j, k, 10, 0x3ff)
5783
5784
/* Compress value to 11 bits.
5785
 *
5786
 * Uses div operator that may be slow and not constant-time.
5787
 *
5788
 * FIPS 203, Section 4.2.1, Compression and decompression
5789
 *
5790
 * @param  [in]  v  Vector of polynomials.
5791
 * @param  [in]  i  Index of polynomial in vector.
5792
 * @param  [in]  j  Index into polynomial.
5793
 * @param  [in]  k  Offset from indices.
5794
 * @return  Compressed value.
5795
 */
5796
#define TO_COMP_WORD_11(v, i, j, k) \
5797
    TO_COMP_WORD_VEC(v, i, j, k, 11, 0x7ff)
5798
5799
#else
5800
5801
/* Multiplier that does div q.
5802
 * ((1 << 53) + MLKEM_Q_HALF) / MLKEM_Q
5803
 */
5804
0
#define MLKEM_V53         0x275f6ed0176UL
5805
/* Multiplier times half of q.
5806
 * MLKEM_V53 * (MLKEM_Q_HALF + 1)
5807
 */
5808
0
#define MLKEM_V53_HALF    0x10013afb768076UL
5809
5810
/* Multiplier that does div q.
5811
 * ((1 << 54) + MLKEM_Q_HALF) / MLKEM_Q
5812
 */
5813
0
#define MLKEM_V54         0x4ebedda02ecUL
5814
/* Multiplier times half of q.
5815
 * MLKEM_V54 * (MLKEM_Q_HALF + 1)
5816
 */
5817
0
#define MLKEM_V54_HALF    0x200275f6ed00ecUL
5818
5819
/* Compress value to 10 bits.
5820
 *
5821
 * Uses mul instead of div.
5822
 *
5823
 * FIPS 203, Section 4.2.1, Compression and decompression
5824
 *
5825
 * @param  [in]  v  Vector of polynomials.
5826
 * @param  [in]  i  Index of polynomial in vector.
5827
 * @param  [in]  j  Index into polynomial.
5828
 * @param  [in]  k  Offset from indices.
5829
 * @return  Compressed value.
5830
 */
5831
#define TO_COMP_WORD_10(v, i, j, k) \
5832
0
    (sword16)((((MLKEM_V54 << 10) * (word64)(v)[(i) * MLKEM_N + (j) + (k)]) + \
5833
0
               MLKEM_V54_HALF) >> 54)
5834
5835
/* Compress value to 11 bits.
5836
 *
5837
 * Uses mul instead of div.
5838
 * Only works for values in range: 0..3228
5839
 *
5840
 * FIPS 203, Section 4.2.1, Compression and decompression
5841
 *
5842
 * @param  [in]  v  Vector of polynomials.
5843
 * @param  [in]  i  Index of polynomial in vector.
5844
 * @param  [in]  j  Index into polynomial.
5845
 * @param  [in]  k  Offset from indices.
5846
 * @return  Compressed value.
5847
 */
5848
#define TO_COMP_WORD_11(v, i, j, k) \
5849
0
    (sword16)((((MLKEM_V53 << 11) * (word64)(v)[(i) * MLKEM_N + (j) + (k)]) + \
5850
0
               MLKEM_V53_HALF) >> 53)
5851
5852
#endif /* CONV_WITH_DIV */
5853
5854
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
5855
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
5856
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \
5857
    defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
5858
/* Compress the vector of polynomials into a byte array with 10 bits each.
5859
 *
5860
 * FIPS 203, Section 4.2.1, Compression and decompression
5861
 *
5862
 * @param  [out]      r  Array of bytes.
5863
 * @param  [in, out]  v  Vector of polynomials.
5864
 * @param  [in]       k  Number of polynomials in vector.
5865
 */
5866
static void mlkem_vec_compress_10_c(byte* r, sword16* v, unsigned int k)
5867
0
{
5868
0
    unsigned int i;
5869
0
    unsigned int j;
5870
5871
0
    for (i = 0; i < k; i++) {
5872
        /* Reduce each coefficient to mod q. */
5873
0
        mlkem_csubq_c(v + i * MLKEM_N);
5874
        /* All values are now positive. */
5875
0
    }
5876
5877
    /* Each polynomial. */
5878
0
    for (i = 0; i < k; i++) {
5879
0
#if defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_MLKEM_NO_LARGE_CODE) || \
5880
0
    defined(BIG_ENDIAN_ORDER) || defined(WOLFSSL_WIDE_BYTE)
5881
        /* Each 4 polynomial coefficients. */
5882
0
        for (j = 0; j < MLKEM_N; j += 4) {
5883
        #ifdef WOLFSSL_MLKEM_SMALL
5884
            unsigned int l;
5885
            sword16 t[4];
5886
            /* Compress four polynomial values to 10 bits each. */
5887
            for (l = 0; l < 4; l++) {
5888
                t[l] = TO_COMP_WORD_10(v, i, j, l);
5889
            }
5890
5891
            /* Pack four 10-bit values into byte array. */
5892
            r[ 0] = WC_OCTET( t[0] >> 0);
5893
            r[ 1] = WC_OCTET((t[0] >> 8) | (t[1] << 2));
5894
            r[ 2] = WC_OCTET((t[1] >> 6) | (t[2] << 4));
5895
            r[ 3] = WC_OCTET((t[2] >> 4) | (t[3] << 6));
5896
            r[ 4] = WC_OCTET( t[3] >> 2);
5897
        #else
5898
            /* Compress four polynomial values to 10 bits each. */
5899
0
            sword16 t0 = TO_COMP_WORD_10(v, i, j, 0);
5900
0
            sword16 t1 = TO_COMP_WORD_10(v, i, j, 1);
5901
0
            sword16 t2 = TO_COMP_WORD_10(v, i, j, 2);
5902
0
            sword16 t3 = TO_COMP_WORD_10(v, i, j, 3);
5903
5904
            /* Pack four 10-bit values into byte array. */
5905
0
            r[ 0] = WC_OCTET( t0 >> 0);
5906
0
            r[ 1] = WC_OCTET((t0 >> 8) | (t1 << 2));
5907
0
            r[ 2] = WC_OCTET((t1 >> 6) | (t2 << 4));
5908
0
            r[ 3] = WC_OCTET((t2 >> 4) | (t3 << 6));
5909
0
            r[ 4] = WC_OCTET( t3 >> 2);
5910
0
        #endif
5911
5912
            /* Move over set bytes. */
5913
0
            r += 5;
5914
0
        }
5915
#else
5916
        /* Each 16 polynomial coefficients. */
5917
        for (j = 0; j < MLKEM_N; j += 16) {
5918
            /* Compress four polynomial values to 10 bits each. */
5919
            sword16 t0  = TO_COMP_WORD_10(v, i, j, 0);
5920
            sword16 t1  = TO_COMP_WORD_10(v, i, j, 1);
5921
            sword16 t2  = TO_COMP_WORD_10(v, i, j, 2);
5922
            sword16 t3  = TO_COMP_WORD_10(v, i, j, 3);
5923
            sword16 t4  = TO_COMP_WORD_10(v, i, j, 4);
5924
            sword16 t5  = TO_COMP_WORD_10(v, i, j, 5);
5925
            sword16 t6  = TO_COMP_WORD_10(v, i, j, 6);
5926
            sword16 t7  = TO_COMP_WORD_10(v, i, j, 7);
5927
            sword16 t8  = TO_COMP_WORD_10(v, i, j, 8);
5928
            sword16 t9  = TO_COMP_WORD_10(v, i, j, 9);
5929
            sword16 t10 = TO_COMP_WORD_10(v, i, j, 10);
5930
            sword16 t11 = TO_COMP_WORD_10(v, i, j, 11);
5931
            sword16 t12 = TO_COMP_WORD_10(v, i, j, 12);
5932
            sword16 t13 = TO_COMP_WORD_10(v, i, j, 13);
5933
            sword16 t14 = TO_COMP_WORD_10(v, i, j, 14);
5934
            sword16 t15 = TO_COMP_WORD_10(v, i, j, 15);
5935
5936
            /* Pack sixteen 10-bit values into byte array. */
5937
            writeUnalignedWord32(r +  0,
5938
                 (word32)t0         | ((word32)t1  << 10) |
5939
                ((word32)t2  << 20) | ((word32)t3  << 30));
5940
            writeUnalignedWord32(r +  4,
5941
                ((word32)t3  >>  2) | ((word32)t4  <<  8) |
5942
                ((word32)t5  << 18) | ((word32)t6  << 28));
5943
            writeUnalignedWord32(r +  8,
5944
                ((word32)t6  >>  4) | ((word32)t7  <<  6) |
5945
                ((word32)t8  << 16) | ((word32)t9  << 26));
5946
            writeUnalignedWord32(r + 12,
5947
                ((word32)t9  >>  6) | ((word32)t10 <<  4) |
5948
                ((word32)t11 << 14) | ((word32)t12 << 24));
5949
            writeUnalignedWord32(r + 16,
5950
                ((word32)t12 >>  8) | ((word32)t13 <<  2) |
5951
                ((word32)t14 << 12) | ((word32)t15 << 22));
5952
5953
            /* Move over set bytes. */
5954
            r += 20;
5955
        }
5956
#endif
5957
0
    }
5958
0
}
5959
5960
/* Compress the vector of polynomials into a byte array with 10 bits each.
5961
 *
5962
 * FIPS 203, Section 4.2.1, Compression and decompression
5963
 *
5964
 * @param  [out]      r  Array of bytes.
5965
 * @param  [in, out]  v  Vector of polynomials.
5966
 * @param  [in]       k  Number of polynomials in vector.
5967
 */
5968
void mlkem_vec_compress_10(byte* r, sword16* v, unsigned int k)
5969
0
{
5970
#ifdef USE_INTEL_SPEEDUP
5971
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
5972
    if (USE_INTEL_AVX512(cpuid_flags) &&
5973
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
5974
            (SAVE_VECTOR_REGISTERS2() == 0)) {
5975
        mlkem_compress_10_avx512_vbmi(r, v, (int)k);
5976
        RESTORE_VECTOR_REGISTERS();
5977
    }
5978
    else
5979
#endif
5980
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
5981
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5982
        mlkem_compress_10_avx512(r, v, (int)k);
5983
        RESTORE_VECTOR_REGISTERS();
5984
    }
5985
    else
5986
#endif
5987
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
5988
        mlkem_compress_10_avx2(r, v, (int)k);
5989
        RESTORE_VECTOR_REGISTERS();
5990
    }
5991
    else
5992
#endif
5993
0
    {
5994
0
        mlkem_vec_compress_10_c(r, v, k);
5995
0
    }
5996
0
}
5997
#endif
5998
5999
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
6000
/* Compress the vector of polynomials into a byte array with 11 bits each.
6001
 *
6002
 * FIPS 203, Section 4.2.1, Compression and decompression
6003
 *
6004
 * @param  [out]      r  Array of bytes.
6005
 * @param  [in, out]  v  Vector of polynomials.
6006
 */
6007
static void mlkem_vec_compress_11_c(byte* r, sword16* v)
6008
0
{
6009
0
    unsigned int i;
6010
0
    unsigned int j;
6011
#ifdef WOLFSSL_MLKEM_SMALL
6012
    unsigned int k;
6013
#endif
6014
6015
0
    for (i = 0; i < 4; i++) {
6016
        /* Reduce each coefficient to mod q. */
6017
0
        mlkem_csubq_c(v + i * MLKEM_N);
6018
        /* All values are now positive. */
6019
0
    }
6020
6021
    /* Each polynomial. */
6022
0
    for (i = 0; i < 4; i++) {
6023
        /* Each 8 polynomial coefficients. */
6024
0
        for (j = 0; j < MLKEM_N; j += 8) {
6025
        #ifdef WOLFSSL_MLKEM_SMALL
6026
            sword16 t[8];
6027
            /* Compress eight polynomial values to 11 bits each. */
6028
            for (k = 0; k < 8; k++) {
6029
                t[k] = TO_COMP_WORD_11(v, i, j, k);
6030
            }
6031
6032
            /* Pack eight 11-bit values into byte array. */
6033
            r[ 0] = WC_OCTET( t[0] >>  0);
6034
            r[ 1] = WC_OCTET((t[0] >>  8) | (t[1] << 3));
6035
            r[ 2] = WC_OCTET((t[1] >>  5) | (t[2] << 6));
6036
            r[ 3] = WC_OCTET( t[2] >>  2);
6037
            r[ 4] = WC_OCTET((t[2] >> 10) | (t[3] << 1));
6038
            r[ 5] = WC_OCTET((t[3] >>  7) | (t[4] << 4));
6039
            r[ 6] = WC_OCTET((t[4] >>  4) | (t[5] << 7));
6040
            r[ 7] = WC_OCTET( t[5] >>  1);
6041
            r[ 8] = WC_OCTET((t[5] >>  9) | (t[6] << 2));
6042
            r[ 9] = WC_OCTET((t[6] >>  6) | (t[7] << 5));
6043
            r[10] = WC_OCTET( t[7] >>  3);
6044
        #else
6045
            /* Compress eight polynomial values to 11 bits each. */
6046
0
            sword16 t0 = TO_COMP_WORD_11(v, i, j, 0);
6047
0
            sword16 t1 = TO_COMP_WORD_11(v, i, j, 1);
6048
0
            sword16 t2 = TO_COMP_WORD_11(v, i, j, 2);
6049
0
            sword16 t3 = TO_COMP_WORD_11(v, i, j, 3);
6050
0
            sword16 t4 = TO_COMP_WORD_11(v, i, j, 4);
6051
0
            sword16 t5 = TO_COMP_WORD_11(v, i, j, 5);
6052
0
            sword16 t6 = TO_COMP_WORD_11(v, i, j, 6);
6053
0
            sword16 t7 = TO_COMP_WORD_11(v, i, j, 7);
6054
6055
            /* Pack eight 11-bit values into byte array. */
6056
0
            r[ 0] = WC_OCTET( t0 >>  0);
6057
0
            r[ 1] = WC_OCTET((t0 >>  8) | (t1 << 3));
6058
0
            r[ 2] = WC_OCTET((t1 >>  5) | (t2 << 6));
6059
0
            r[ 3] = WC_OCTET( t2 >>  2);
6060
0
            r[ 4] = WC_OCTET((t2 >> 10) | (t3 << 1));
6061
0
            r[ 5] = WC_OCTET((t3 >>  7) | (t4 << 4));
6062
0
            r[ 6] = WC_OCTET((t4 >>  4) | (t5 << 7));
6063
0
            r[ 7] = WC_OCTET( t5 >>  1);
6064
0
            r[ 8] = WC_OCTET((t5 >>  9) | (t6 << 2));
6065
0
            r[ 9] = WC_OCTET((t6 >>  6) | (t7 << 5));
6066
0
            r[10] = WC_OCTET( t7 >>  3);
6067
0
        #endif
6068
6069
            /* Move over set bytes. */
6070
0
            r += 11;
6071
0
        }
6072
0
    }
6073
0
}
6074
6075
/* Compress the vector of polynomials into a byte array with 11 bits each.
6076
 *
6077
 * FIPS 203, Section 4.2.1, Compression and decompression
6078
 *
6079
 * @param  [out]      r  Array of bytes.
6080
 * @param  [in, out]  v  Vector of polynomials.
6081
 */
6082
void mlkem_vec_compress_11(byte* r, sword16* v)
6083
0
{
6084
#ifdef USE_INTEL_SPEEDUP
6085
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6086
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6087
        mlkem_compress_11_avx512(r, v, 4);
6088
        RESTORE_VECTOR_REGISTERS();
6089
    }
6090
    else
6091
#endif
6092
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6093
        mlkem_compress_11_avx2(r, v, 4);
6094
        RESTORE_VECTOR_REGISTERS();
6095
    }
6096
    else
6097
#endif
6098
0
    {
6099
0
        mlkem_vec_compress_11_c(r, v);
6100
0
    }
6101
0
}
6102
#endif
6103
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE || !WOLFSSL_MLKEM_NO_DECAPSULATE */
6104
6105
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
6106
/* Decompress a 10 bit value.
6107
 *
6108
 * FIPS 203, Section 4.2.1, Compression and decompression
6109
 *
6110
 * @param  [out]  v  Vector of polynomials.
6111
 * @param  [in]   i  Index of polynomial in vector.
6112
 * @param  [in]   j  Index into polynomial.
6113
 * @param  [in]   k  Offset from indices.
6114
 * @param  [in]   t  Value to decompress.
6115
 */
6116
#define DECOMP_10(v, i, j, k, t) \
6117
0
    v[(i) * MLKEM_N + 4 * (j) + (k)] = \
6118
0
        (sword16)((((word32)((t) & 0x3ff) * MLKEM_Q) + 512) >> 10)
6119
6120
/* Decompress an 11 bit value.
6121
 *
6122
 * FIPS 203, Section 4.2.1, Compression and decompression
6123
 *
6124
 * @param  [out]  v  Vector of polynomials.
6125
 * @param  [in]   i  Index of polynomial in vector.
6126
 * @param  [in]   j  Index into polynomial.
6127
 * @param  [in]   k  Offset from indices.
6128
 * @param  [in]   t  Value to decompress.
6129
 */
6130
#define DECOMP_11(v, i, j, k, t) \
6131
0
    v[(i) * MLKEM_N + 8 * (j) + (k)] = \
6132
0
        (sword16)((((word32)((t) & 0x7ff) * MLKEM_Q) + 1024) >> 11)
6133
6134
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \
6135
    defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
6136
/* Decompress the byte array of packed 10 bits into vector of polynomials.
6137
 *
6138
 * FIPS 203, Section 4.2.1, Compression and decompression
6139
 *
6140
 * @param  [out]  v  Vector of polynomials.
6141
 * @param  [in]   b  Array of bytes.
6142
 * @param  [in]   k  Number of polynomials in vector.
6143
 */
6144
static void mlkem_vec_decompress_10_c(sword16* v, const byte* b, unsigned int k)
6145
0
{
6146
0
    unsigned int i;
6147
0
    unsigned int j;
6148
#ifdef WOLFSSL_MLKEM_SMALL
6149
    unsigned int l;
6150
#endif
6151
6152
    /* Each polynomial. */
6153
0
    for (i = 0; i < k; i++) {
6154
        /* Each 4 polynomial coefficients. */
6155
0
        for (j = 0; j < MLKEM_N / 4; j++) {
6156
        #ifdef WOLFSSL_MLKEM_SMALL
6157
            word16 t[4];
6158
            /* Extract out 4 values of 10 bits each. */
6159
            t[0] = (word16)((b[0] >> 0) | ((word16)b[ 1] << 8));
6160
            t[1] = (word16)((b[1] >> 2) | ((word16)b[ 2] << 6));
6161
            t[2] = (word16)((b[2] >> 4) | ((word16)b[ 3] << 4));
6162
            t[3] = (word16)((b[3] >> 6) | ((word16)b[ 4] << 2));
6163
            b += 5;
6164
6165
            /* Decompress 4 values. */
6166
            for (l = 0; l < 4; l++) {
6167
                DECOMP_10(v, i, j, l, t[l]);
6168
            }
6169
        #else
6170
            /* Extract out 4 values of 10 bits each. */
6171
0
            word16 t0 = (word16)((b[0] >> 0) | ((word16)b[ 1] << 8));
6172
0
            word16 t1 = (word16)((b[1] >> 2) | ((word16)b[ 2] << 6));
6173
0
            word16 t2 = (word16)((b[2] >> 4) | ((word16)b[ 3] << 4));
6174
0
            word16 t3 = (word16)((b[3] >> 6) | ((word16)b[ 4] << 2));
6175
0
            b += 5;
6176
6177
            /* Decompress 4 values. */
6178
0
            DECOMP_10(v, i, j, 0, t0);
6179
0
            DECOMP_10(v, i, j, 1, t1);
6180
0
            DECOMP_10(v, i, j, 2, t2);
6181
0
            DECOMP_10(v, i, j, 3, t3);
6182
0
        #endif
6183
0
        }
6184
0
    }
6185
0
}
6186
6187
/* Decompress the byte array of packed 10 bits into vector of polynomials.
6188
 *
6189
 * FIPS 203, Section 4.2.1, Compression and decompression
6190
 *
6191
 * @param  [out]  v  Vector of polynomials.
6192
 * @param  [in]   b  Array of bytes.
6193
 * @param  [in]   k  Number of polynomials in vector.
6194
 */
6195
void mlkem_vec_decompress_10(sword16* v, const byte* b, unsigned int k)
6196
0
{
6197
#ifdef USE_INTEL_SPEEDUP
6198
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
6199
    if (USE_INTEL_AVX512(cpuid_flags) &&
6200
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
6201
            (SAVE_VECTOR_REGISTERS2() == 0)) {
6202
        mlkem_decompress_10_avx512_vbmi(v, b, (int)k);
6203
        RESTORE_VECTOR_REGISTERS();
6204
    }
6205
    else
6206
#endif
6207
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6208
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6209
        mlkem_decompress_10_avx512(v, b, (int)k);
6210
        RESTORE_VECTOR_REGISTERS();
6211
    }
6212
    else
6213
#endif
6214
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6215
        mlkem_decompress_10_avx2(v, b, (int)k);
6216
        RESTORE_VECTOR_REGISTERS();
6217
    }
6218
    else
6219
#endif
6220
0
    {
6221
0
        mlkem_vec_decompress_10_c(v, b, k);
6222
0
    }
6223
0
}
6224
#endif
6225
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
6226
/* Decompress the byte array of packed 11 bits into vector of polynomials.
6227
 *
6228
 * FIPS 203, Section 4.2.1, Compression and decompression
6229
 *
6230
 * @param  [out]  v       Vector of polynomials.
6231
 * @param  [in]   b       Array of bytes.
6232
 */
6233
static void mlkem_vec_decompress_11_c(sword16* v, const byte* b)
6234
0
{
6235
0
    unsigned int i;
6236
0
    unsigned int j;
6237
#ifdef WOLFSSL_MLKEM_SMALL
6238
    unsigned int l;
6239
#endif
6240
6241
    /* Each polynomial. */
6242
0
    for (i = 0; i < 4; i++) {
6243
        /* Each 8 polynomial coefficients. */
6244
0
        for (j = 0; j < MLKEM_N / 8; j++) {
6245
        #ifdef WOLFSSL_MLKEM_SMALL
6246
            word16 t[8];
6247
            /* Extract out 8 values of 11 bits each. */
6248
            t[0] = (word16)((b[0] >> 0) | ((word16)b[ 1] << 8));
6249
            t[1] = (word16)((b[1] >> 3) | ((word16)b[ 2] << 5));
6250
            t[2] = (word16)((b[2] >> 6) | ((word16)b[ 3] << 2) |
6251
                   ((word16)b[4] << 10));
6252
            t[3] = (word16)((b[4] >> 1) | ((word16)b[ 5] << 7));
6253
            t[4] = (word16)((b[5] >> 4) | ((word16)b[ 6] << 4));
6254
            t[5] = (word16)((b[6] >> 7) | ((word16)b[ 7] << 1) |
6255
                   ((word16)b[8] <<  9));
6256
            t[6] = (word16)((b[8] >> 2) | ((word16)b[ 9] << 6));
6257
            t[7] = (word16)((b[9] >> 5) | ((word16)b[10] << 3));
6258
            b += 11;
6259
6260
            /* Decompress 8 values. */
6261
            for (l = 0; l < 8; l++) {
6262
                DECOMP_11(v, i, j, l, t[l]);
6263
            }
6264
        #else
6265
            /* Extract out 8 values of 11 bits each. */
6266
0
            word16 t0 = (word16)((b[0] >> 0) | ((word16)b[ 1] << 8));
6267
0
            word16 t1 = (word16)((b[1] >> 3) | ((word16)b[ 2] << 5));
6268
0
            word16 t2 = (word16)((b[2] >> 6) | ((word16)b[ 3] << 2) |
6269
0
                   ((word16)b[4] << 10));
6270
0
            word16 t3 = (word16)((b[4] >> 1) | ((word16)b[ 5] << 7));
6271
0
            word16 t4 = (word16)((b[5] >> 4) | ((word16)b[ 6] << 4));
6272
0
            word16 t5 = (word16)((b[6] >> 7) | ((word16)b[ 7] << 1) |
6273
0
                   ((word16)b[8] <<  9));
6274
0
            word16 t6 = (word16)((b[8] >> 2) | ((word16)b[ 9] << 6));
6275
0
            word16 t7 = (word16)((b[9] >> 5) | ((word16)b[10] << 3));
6276
0
            b += 11;
6277
6278
            /* Decompress 8 values. */
6279
0
            DECOMP_11(v, i, j, 0, t0);
6280
0
            DECOMP_11(v, i, j, 1, t1);
6281
0
            DECOMP_11(v, i, j, 2, t2);
6282
0
            DECOMP_11(v, i, j, 3, t3);
6283
0
            DECOMP_11(v, i, j, 4, t4);
6284
0
            DECOMP_11(v, i, j, 5, t5);
6285
0
            DECOMP_11(v, i, j, 6, t6);
6286
0
            DECOMP_11(v, i, j, 7, t7);
6287
0
        #endif
6288
0
        }
6289
0
    }
6290
0
}
6291
6292
/* Decompress the byte array of packed 11 bits into vector of polynomials.
6293
 *
6294
 * FIPS 203, Section 4.2.1, Compression and decompression
6295
 *
6296
 * @param  [out]  v       Vector of polynomials.
6297
 * @param  [in]   b       Array of bytes.
6298
 */
6299
void mlkem_vec_decompress_11(sword16* v, const byte* b)
6300
0
{
6301
#ifdef USE_INTEL_SPEEDUP
6302
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
6303
    if (USE_INTEL_AVX512(cpuid_flags) &&
6304
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
6305
            (SAVE_VECTOR_REGISTERS2() == 0)) {
6306
        mlkem_decompress_11_avx512_vbmi(v, b, 4);
6307
        RESTORE_VECTOR_REGISTERS();
6308
    }
6309
    else
6310
#endif
6311
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6312
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6313
        mlkem_decompress_11_avx512(v, b, 4);
6314
        RESTORE_VECTOR_REGISTERS();
6315
    }
6316
    else
6317
#endif
6318
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6319
        mlkem_decompress_11_avx2(v, b, 4);
6320
        RESTORE_VECTOR_REGISTERS();
6321
    }
6322
    else
6323
#endif
6324
0
    {
6325
0
        mlkem_vec_decompress_11_c(v, b);
6326
0
    }
6327
0
}
6328
#endif
6329
#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */
6330
6331
#ifdef CONV_WITH_DIV
6332
6333
/* Compress value.
6334
 *
6335
 * Uses div operator that may be slow and not constant-time.
6336
 *
6337
 * FIPS 203, Section 4.2.1, Compression and decompression
6338
 *
6339
 * @param  [in]  v  Vector of polynomials.
6340
 * @param  [in]  i  Index into polynomial.
6341
 * @param  [in]  j  Offset from indices.
6342
 * @param  [in]  s  Shift amount to apply to value being compressed.
6343
 * @param  [in]  m  Mask to apply to get the required number of bits.
6344
 * @return  Compressed value.
6345
 */
6346
#define TO_COMP_WORD(v, i, j, s, m) \
6347
    ((((word32)v[i + j] << s) + MLKEM_Q_HALF) / MLKEM_Q) & m
6348
6349
/* Compress value to 4 bits.
6350
 *
6351
 * Uses div operator that may be slow and not constant-time.
6352
 *
6353
 * FIPS 203, Section 4.2.1, Compression and decompression
6354
 *
6355
 * @param  [in]  p  Polynomial.
6356
 * @param  [in]  i  Index into polynomial.
6357
 * @param  [in]  j  Offset from indices.
6358
 * @return  Compressed value.
6359
 */
6360
#define TO_COMP_WORD_4(p, i, j) \
6361
    TO_COMP_WORD(p, i, j, 4, 0xf)
6362
6363
/* Compress value to 5 bits.
6364
 *
6365
 * Uses div operator that may be slow and not constant-time.
6366
 *
6367
 * FIPS 203, Section 4.2.1, Compression and decompression
6368
 *
6369
 * @param  [in]  p  Polynomial.
6370
 * @param  [in]  i  Index into polynomial.
6371
 * @param  [in]  j  Offset from indices.
6372
 * @return  Compressed value.
6373
 */
6374
#define TO_COMP_WORD_5(p, i, j) \
6375
    TO_COMP_WORD(p, i, j, 5, 0x1f)
6376
6377
#else
6378
6379
/* Multiplier that does div q. */
6380
0
#define MLKEM_V28         ((word32)(((1UL << 28) + MLKEM_Q_HALF)) / MLKEM_Q)
6381
/* Multiplier times half of q plus one. */
6382
0
#define MLKEM_V28_HALF    ((word32)(MLKEM_V28 * (MLKEM_Q_HALF + 1)))
6383
6384
/* Multiplier that does div q. */
6385
0
#define MLKEM_V27         ((word32)(((1UL << 27) + MLKEM_Q_HALF)) / MLKEM_Q)
6386
/* Multiplier times half of q. */
6387
0
#define MLKEM_V27_HALF    ((word32)(MLKEM_V27 * MLKEM_Q_HALF))
6388
6389
/* Compress value to 4 bits.
6390
 *
6391
 * Uses mul instead of div.
6392
 *
6393
 * FIPS 203, Section 4.2.1, Compression and decompression
6394
 *
6395
 * @param  [in]  p  Polynomial.
6396
 * @param  [in]  i  Index into polynomial.
6397
 * @param  [in]  j  Offset from indices.
6398
 * @return  Compressed value.
6399
 */
6400
#define TO_COMP_WORD_4(p, i, j) \
6401
0
    (byte)((((MLKEM_V28 << 4) * (word32)(p)[(i) + (j)]) + MLKEM_V28_HALF) >> 28)
6402
6403
/* Compress value to 5 bits.
6404
 *
6405
 * Uses mul instead of div.
6406
 *
6407
 * FIPS 203, Section 4.2.1, Compression and decompression
6408
 *
6409
 * @param  [in]  p  Polynomial.
6410
 * @param  [in]  i  Index into polynomial.
6411
 * @param  [in]  j  Offset from indices.
6412
 * @return  Compressed value.
6413
 */
6414
#define TO_COMP_WORD_5(p, i, j) \
6415
0
    (byte)((((MLKEM_V27 << 5) * (word32)(p)[(i) + (j)]) + MLKEM_V27_HALF) >> 27)
6416
6417
#endif /* CONV_WITH_DIV */
6418
6419
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
6420
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
6421
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \
6422
    defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
6423
/* Compress a polynomial into byte array with coefficients of 4 bits.
6424
 *
6425
 * FIPS 203, Section 4.2.1, Compression and decompression
6426
 *
6427
 * @param  [out]      b  Array of bytes.
6428
 * @param  [in, out]  p  Polynomial.
6429
 */
6430
static void mlkem_compress_4_c(byte* b, sword16* p)
6431
0
{
6432
0
    unsigned int i;
6433
#ifdef WOLFSSL_MLKEM_SMALL
6434
    unsigned int j;
6435
    byte t[8];
6436
#endif
6437
6438
    /* Reduce each coefficient to mod q. */
6439
0
    mlkem_csubq_c(p);
6440
    /* All values are now positive. */
6441
6442
    /* Each 8 polynomial coefficients. */
6443
0
    for (i = 0; i < MLKEM_N; i += 8) {
6444
    #ifdef WOLFSSL_MLKEM_SMALL
6445
        /* Compress eight polynomial values to 4 bits each. */
6446
        for (j = 0; j < 8; j++) {
6447
            t[j] = TO_COMP_WORD_4(p, i, j);
6448
        }
6449
6450
        b[0] = WC_OCTET(t[0] | (t[1] << 4));
6451
        b[1] = WC_OCTET(t[2] | (t[3] << 4));
6452
        b[2] = WC_OCTET(t[4] | (t[5] << 4));
6453
        b[3] = WC_OCTET(t[6] | (t[7] << 4));
6454
    #else
6455
        /* Compress eight polynomial values to 4 bits each. */
6456
0
        byte t0 = TO_COMP_WORD_4(p, i, 0);
6457
0
        byte t1 = TO_COMP_WORD_4(p, i, 1);
6458
0
        byte t2 = TO_COMP_WORD_4(p, i, 2);
6459
0
        byte t3 = TO_COMP_WORD_4(p, i, 3);
6460
0
        byte t4 = TO_COMP_WORD_4(p, i, 4);
6461
0
        byte t5 = TO_COMP_WORD_4(p, i, 5);
6462
0
        byte t6 = TO_COMP_WORD_4(p, i, 6);
6463
0
        byte t7 = TO_COMP_WORD_4(p, i, 7);
6464
6465
        /* Pack eight 4-bit values into byte array. */
6466
0
        b[0] = WC_OCTET(t0 | (t1 << 4));
6467
0
        b[1] = WC_OCTET(t2 | (t3 << 4));
6468
0
        b[2] = WC_OCTET(t4 | (t5 << 4));
6469
0
        b[3] = WC_OCTET(t6 | (t7 << 4));
6470
0
    #endif
6471
6472
        /* Move over set bytes. */
6473
0
        b += 4;
6474
0
    }
6475
0
}
6476
6477
/* Compress a polynomial into byte array with coefficients of 4 bits.
6478
 *
6479
 * FIPS 203, Section 4.2.1, Compression and decompression
6480
 *
6481
 * @param  [out]      b  Array of bytes.
6482
 * @param  [in, out]  p  Polynomial.
6483
 */
6484
void mlkem_compress_4(byte* b, sword16* p)
6485
0
{
6486
#ifdef USE_INTEL_SPEEDUP
6487
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
6488
    if (USE_INTEL_AVX512(cpuid_flags) &&
6489
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
6490
            (SAVE_VECTOR_REGISTERS2() == 0)) {
6491
        mlkem_compress_4_avx512_vbmi(b, p);
6492
        RESTORE_VECTOR_REGISTERS();
6493
    }
6494
    else
6495
#endif
6496
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6497
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6498
        mlkem_compress_4_avx512(b, p);
6499
        RESTORE_VECTOR_REGISTERS();
6500
    }
6501
    else
6502
#endif
6503
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6504
        mlkem_compress_4_avx2(b, p);
6505
        RESTORE_VECTOR_REGISTERS();
6506
    }
6507
    else
6508
#endif
6509
0
    {
6510
0
        mlkem_compress_4_c(b, p);
6511
0
    }
6512
0
}
6513
#endif
6514
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
6515
/* Compress a polynomial into byte array with coefficients of 5 bits.
6516
 *
6517
 * FIPS 203, Section 4.2.1, Compression and decompression
6518
 *
6519
 * @param  [out]      b  Array of bytes.
6520
 * @param  [in, out]  p  Polynomial.
6521
 */
6522
static void mlkem_compress_5_c(byte* b, sword16* p)
6523
0
{
6524
0
    unsigned int i;
6525
#ifdef WOLFSSL_MLKEM_SMALL
6526
    unsigned int j;
6527
    byte t[8];
6528
#endif
6529
6530
    /* Reduce each coefficient to mod q. */
6531
0
    mlkem_csubq_c(p);
6532
    /* All values are now positive. */
6533
6534
0
    for (i = 0; i < MLKEM_N; i += 8) {
6535
    #ifdef WOLFSSL_MLKEM_SMALL
6536
        /* Compress eight polynomial values to 5 bits each. */
6537
        for (j = 0; j < 8; j++) {
6538
            t[j] = TO_COMP_WORD_5(p, i, j);
6539
        }
6540
6541
        /* Pack 5 bits into byte array. */
6542
        b[0] = WC_OCTET((t[0] >> 0) | (t[1] << 5));
6543
        b[1] = WC_OCTET((t[1] >> 3) | (t[2] << 2) | (t[3] << 7));
6544
        b[2] = WC_OCTET((t[3] >> 1) | (t[4] << 4));
6545
        b[3] = WC_OCTET((t[4] >> 4) | (t[5] << 1) | (t[6] << 6));
6546
        b[4] = WC_OCTET((t[6] >> 2) | (t[7] << 3));
6547
    #else
6548
        /* Compress eight polynomial values to 5 bits each. */
6549
0
        byte t0 = TO_COMP_WORD_5(p, i, 0);
6550
0
        byte t1 = TO_COMP_WORD_5(p, i, 1);
6551
0
        byte t2 = TO_COMP_WORD_5(p, i, 2);
6552
0
        byte t3 = TO_COMP_WORD_5(p, i, 3);
6553
0
        byte t4 = TO_COMP_WORD_5(p, i, 4);
6554
0
        byte t5 = TO_COMP_WORD_5(p, i, 5);
6555
0
        byte t6 = TO_COMP_WORD_5(p, i, 6);
6556
0
        byte t7 = TO_COMP_WORD_5(p, i, 7);
6557
6558
        /* Pack eight 5-bit values into byte array. */
6559
0
        b[0] = WC_OCTET((t0 >> 0) | (t1 << 5));
6560
0
        b[1] = WC_OCTET((t1 >> 3) | (t2 << 2) | (t3 << 7));
6561
0
        b[2] = WC_OCTET((t3 >> 1) | (t4 << 4));
6562
0
        b[3] = WC_OCTET((t4 >> 4) | (t5 << 1) | (t6 << 6));
6563
0
        b[4] = WC_OCTET((t6 >> 2) | (t7 << 3));
6564
0
    #endif
6565
6566
        /* Move over set bytes. */
6567
0
        b += 5;
6568
0
    }
6569
0
}
6570
6571
/* Compress a polynomial into byte array with coefficients of 5 bits.
6572
 *
6573
 * FIPS 203, Section 4.2.1, Compression and decompression
6574
 *
6575
 * @param  [out]      b  Array of bytes.
6576
 * @param  [in, out]  p  Polynomial.
6577
 */
6578
void mlkem_compress_5(byte* b, sword16* p)
6579
0
{
6580
#ifdef USE_INTEL_SPEEDUP
6581
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
6582
    if (USE_INTEL_AVX512(cpuid_flags) &&
6583
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
6584
            (SAVE_VECTOR_REGISTERS2() == 0)) {
6585
        mlkem_compress_5_avx512_vbmi(b, p);
6586
        RESTORE_VECTOR_REGISTERS();
6587
    }
6588
    else
6589
#endif
6590
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6591
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6592
        mlkem_compress_5_avx512(b, p);
6593
        RESTORE_VECTOR_REGISTERS();
6594
    }
6595
    else
6596
#endif
6597
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6598
        mlkem_compress_5_avx2(b, p);
6599
        RESTORE_VECTOR_REGISTERS();
6600
    }
6601
    else
6602
#endif
6603
0
    {
6604
0
        mlkem_compress_5_c(b, p);
6605
0
    }
6606
0
}
6607
#endif
6608
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE || !WOLFSSL_MLKEM_NO_DECAPSULATE */
6609
6610
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
6611
/* Decompress a 4 bit value.
6612
 *
6613
 * FIPS 203, Section 4.2.1, Compression and decompression
6614
 *
6615
 * @param  [out]  p  Polynomial.
6616
 * @param  [in]   i  Index into polynomial.
6617
 * @param  [in]   j  Offset from indices.
6618
 * @param  [in]   t  Value to decompress.
6619
 */
6620
#define DECOMP_4(p, i, j, t) \
6621
0
    p[(i) + (j)] = (sword16)(((word16)((t) * MLKEM_Q) + 8) >> 4)
6622
6623
/* Decompress a 5 bit value.
6624
 *
6625
 * FIPS 203, Section 4.2.1, Compression and decompression
6626
 *
6627
 * @param  [out]  p  Polynomial.
6628
 * @param  [in]   i  Index into polynomial.
6629
 * @param  [in]   j  Offset from indices.
6630
 * @param  [in]   t  Value to decompress.
6631
 */
6632
#define DECOMP_5(p, i, j, t) \
6633
0
    p[(i) + (j)] = (sword16)((((word32)((t) & 0x1f) * MLKEM_Q) + 16) >> 5)
6634
6635
#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \
6636
    defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
6637
/* Decompress the byte array of packed 4 bits into polynomial.
6638
 *
6639
 * FIPS 203, Section 4.2.1, Compression and decompression
6640
 *
6641
 * @param  [out]  p       Polynomial.
6642
 * @param  [in]   b       Array of bytes.
6643
 */
6644
static void mlkem_decompress_4_c(sword16* p, const byte* b)
6645
0
{
6646
0
    unsigned int i;
6647
6648
    /* 2 coefficients at a time. */
6649
0
    for (i = 0; i < MLKEM_N; i += 2) {
6650
        /* 2 coefficients decompressed from one byte. */
6651
0
        DECOMP_4(p, i, 0, b[0] & 0xf);
6652
0
        DECOMP_4(p, i, 1, b[0] >>  4);
6653
0
        b += 1;
6654
0
    }
6655
0
}
6656
6657
/* Decompress the byte array of packed 4 bits into polynomial.
6658
 *
6659
 * FIPS 203, Section 4.2.1, Compression and decompression
6660
 *
6661
 * @param  [out]  p       Polynomial.
6662
 * @param  [in]   b       Array of bytes.
6663
 */
6664
void mlkem_decompress_4(sword16* p, const byte* b)
6665
0
{
6666
#ifdef USE_INTEL_SPEEDUP
6667
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6668
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6669
        mlkem_decompress_4_avx512(p, b);
6670
        RESTORE_VECTOR_REGISTERS();
6671
    }
6672
    else
6673
#endif
6674
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6675
        mlkem_decompress_4_avx2(p, b);
6676
        RESTORE_VECTOR_REGISTERS();
6677
    }
6678
    else
6679
#endif
6680
0
    {
6681
0
        mlkem_decompress_4_c(p, b);
6682
0
    }
6683
0
}
6684
#endif
6685
#if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
6686
/* Decompress the byte array of packed 5 bits into polynomial.
6687
 *
6688
 * FIPS 203, Section 4.2.1, Compression and decompression
6689
 *
6690
 * @param  [out]  p       Polynomial.
6691
 * @param  [in]   b       Array of bytes.
6692
 */
6693
static void mlkem_decompress_5_c(sword16* p, const byte* b)
6694
0
{
6695
0
    unsigned int i;
6696
6697
    /* Each 8 polynomial coefficients. */
6698
0
    for (i = 0; i < MLKEM_N; i += 8) {
6699
    #ifdef WOLFSSL_MLKEM_SMALL
6700
        unsigned int j;
6701
        byte t[8];
6702
6703
        /* Extract out 8 values of 5 bits each. */
6704
        t[0] = (b[0] >> 0);
6705
        t[1] = WC_OCTET((b[0] >> 5) | (b[1] << 3));
6706
        t[2] = (b[1] >> 2);
6707
        t[3] = WC_OCTET((b[1] >> 7) | (b[2] << 1));
6708
        t[4] = WC_OCTET((b[2] >> 4) | (b[3] << 4));
6709
        t[5] = (b[3] >> 1);
6710
        t[6] = WC_OCTET((b[3] >> 6) | (b[4] << 2));
6711
        t[7] = (b[4] >> 3);
6712
        b += 5;
6713
6714
        /* Decompress 8 values. */
6715
        for (j = 0; j < 8; j++) {
6716
            DECOMP_5(p, i, j, t[j]);
6717
        }
6718
    #else
6719
        /* Extract out 8 values of 5 bits each. */
6720
0
        byte t0 = (b[0] >> 0);
6721
0
        byte t1 = (byte)((b[0] >> 5) | (b[1] << 3));
6722
0
        byte t2 = (b[1] >> 2);
6723
0
        byte t3 = (byte)((b[1] >> 7) | (b[2] << 1));
6724
0
        byte t4 = (byte)((b[2] >> 4) | (b[3] << 4));
6725
0
        byte t5 = (b[3] >> 1);
6726
0
        byte t6 = (byte)((b[3] >> 6) | (b[4] << 2));
6727
0
        byte t7 = (b[4] >> 3);
6728
0
        b += 5;
6729
6730
        /* Decompress 8 values. */
6731
0
        DECOMP_5(p, i, 0, t0);
6732
0
        DECOMP_5(p, i, 1, t1);
6733
0
        DECOMP_5(p, i, 2, t2);
6734
0
        DECOMP_5(p, i, 3, t3);
6735
0
        DECOMP_5(p, i, 4, t4);
6736
0
        DECOMP_5(p, i, 5, t5);
6737
0
        DECOMP_5(p, i, 6, t6);
6738
0
        DECOMP_5(p, i, 7, t7);
6739
0
    #endif
6740
0
    }
6741
0
}
6742
6743
/* Decompress the byte array of packed 5 bits into polynomial.
6744
 *
6745
 * FIPS 203, Section 4.2.1, Compression and decompression
6746
 *
6747
 * @param  [out]  p       Polynomial.
6748
 * @param  [in]   b       Array of bytes.
6749
 */
6750
void mlkem_decompress_5(sword16* p, const byte* b)
6751
0
{
6752
#ifdef USE_INTEL_SPEEDUP
6753
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6754
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6755
        mlkem_decompress_5_avx512(p, b);
6756
        RESTORE_VECTOR_REGISTERS();
6757
    }
6758
    else
6759
#endif
6760
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6761
        mlkem_decompress_5_avx2(p, b);
6762
        RESTORE_VECTOR_REGISTERS();
6763
    }
6764
    else
6765
#endif
6766
0
    {
6767
0
        mlkem_decompress_5_c(p, b);
6768
0
    }
6769
0
}
6770
#endif
6771
#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */
6772
6773
/******************************************************************************/
6774
6775
#if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM))
6776
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
6777
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
6778
/* Convert bit from byte to 0 or (MLKEM_Q + 1) / 2.
6779
 *
6780
 * Constant time implementation.
6781
 * XOR in wc_mlkem_opt_blocker() to ensure optimizer doesn't know what will be
6782
 * ANDed with MLKEM_Q_1_HALF and can't optimize to non-constant time code.
6783
 *
6784
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
6785
 *
6786
 * @param  [out]  p    Polynomial to hold converted value.
6787
 * @param  [in]   msg  Message to get bit from byte.
6788
 * @param  [in]   i    Index of byte from message.
6789
 * @param  [in]   j    Index of bit in byte.
6790
 */
6791
#define FROM_MSG_BIT(p, msg, i, j) \
6792
0
    ((p)[8 * (i) + (j)] = (((sword16)0 - (sword16)(((msg)[i] >> (j)) & 1)) ^ \
6793
0
                          wc_mlkem_opt_blocker()) & MLKEM_Q_1_HALF)
6794
6795
/* Convert message to polynomial.
6796
 *
6797
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
6798
 *
6799
 * @param  [out]  p    Polynomial.
6800
 * @param  [in]   msg  Message as a byte array.
6801
 */
6802
static void mlkem_from_msg_c(sword16* p, const byte* msg)
6803
0
{
6804
0
    unsigned int i;
6805
6806
    /* For each byte of the message. */
6807
0
    for (i = 0; i < MLKEM_N / 8; i++) {
6808
    #ifdef WOLFSSL_MLKEM_SMALL
6809
        unsigned int j;
6810
        /* For each bit of the message. */
6811
        for (j = 0; j < 8; j++) {
6812
            FROM_MSG_BIT(p, msg, i, j);
6813
        }
6814
    #else
6815
0
        FROM_MSG_BIT(p, msg, i, 0);
6816
0
        FROM_MSG_BIT(p, msg, i, 1);
6817
0
        FROM_MSG_BIT(p, msg, i, 2);
6818
0
        FROM_MSG_BIT(p, msg, i, 3);
6819
0
        FROM_MSG_BIT(p, msg, i, 4);
6820
0
        FROM_MSG_BIT(p, msg, i, 5);
6821
0
        FROM_MSG_BIT(p, msg, i, 6);
6822
0
        FROM_MSG_BIT(p, msg, i, 7);
6823
0
    #endif
6824
0
    }
6825
0
}
6826
6827
/* Convert message to polynomial.
6828
 *
6829
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
6830
 *
6831
 * @param  [out]  p    Polynomial.
6832
 * @param  [in]   msg  Message as a byte array.
6833
 */
6834
void mlkem_from_msg(sword16* p, const byte* msg)
6835
0
{
6836
#ifdef USE_INTEL_SPEEDUP
6837
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6838
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6839
        mlkem_from_msg_avx512(p, msg);
6840
        RESTORE_VECTOR_REGISTERS();
6841
    }
6842
    else
6843
#endif
6844
    if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6845
        mlkem_from_msg_avx2(p, msg);
6846
        RESTORE_VECTOR_REGISTERS();
6847
    }
6848
    else
6849
#endif
6850
0
    {
6851
0
        mlkem_from_msg_c(p, msg);
6852
0
    }
6853
0
}
6854
#endif
6855
6856
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
6857
#ifdef CONV_WITH_DIV
6858
6859
/* Convert value to bit.
6860
 *
6861
 * Uses div operator that may be slow.
6862
 *
6863
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
6864
 *
6865
 * @param  [in, out]  m   Message.
6866
 * @param  [in]       p   Polynomial.
6867
 * @param  [in]       i   Index of byte in message.
6868
 * @param  [in]       j   Index of bit in byte.
6869
 */
6870
#define TO_MSG_BIT(m, p, i, j) \
6871
    m[i] |= (((((sword16)p[8 * i + j] << 1) + MLKEM_Q_HALF) / MLKEM_Q) & 1) << j
6872
6873
#else
6874
6875
/* Multiplier that does div q. */
6876
#define MLKEM_V31       (((1UL << 31) + (MLKEM_Q / 2)) / MLKEM_Q)
6877
/* 2 * multiplier that does div q. Only need bit 32 of result. */
6878
#define MLKEM_V31_2     ((word32)(MLKEM_V31 * 2))
6879
/* Multiplier times half of q. */
6880
#define MLKEM_V31_HALF    ((word32)(MLKEM_V31 * MLKEM_Q_HALF))
6881
6882
/* Convert value to bit.
6883
 *
6884
 * Uses mul instead of div.
6885
 *
6886
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
6887
 *
6888
 * @param  [in, out]  m   Message.
6889
 * @param  [in]       p   Polynomial.
6890
 * @param  [in]       i   Index of byte in message.
6891
 * @param  [in]       j   Index of bit in byte.
6892
 */
6893
#define TO_MSG_BIT(m, p, i, j) \
6894
0
    (m)[i] |= WC_OCTET((((MLKEM_V31_2 * (word16)(p)[8 * (i) + (j)]) + \
6895
0
                       MLKEM_V31_HALF) >> 31) << (j))
6896
6897
#endif /* CONV_WITH_DIV */
6898
6899
/* Convert polynomial to message.
6900
 *
6901
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
6902
 *
6903
 * @param  [out]      msg  Message as a byte array.
6904
 * @param  [in, out]  p    Polynomial.
6905
 */
6906
static void mlkem_to_msg_c(byte* msg, sword16* p)
6907
0
{
6908
0
    unsigned int i;
6909
6910
    /* Reduce each coefficient to mod q. */
6911
0
    mlkem_csubq_c(p);
6912
    /* All values are now in range. */
6913
6914
0
    for (i = 0; i < MLKEM_N / 8; i++) {
6915
    #ifdef WOLFSSL_MLKEM_SMALL
6916
        unsigned int j;
6917
        msg[i] = 0;
6918
        for (j = 0; j < 8; j++) {
6919
            TO_MSG_BIT(msg, p, i, j);
6920
        }
6921
    #else
6922
0
        msg[i] = 0;
6923
0
        TO_MSG_BIT(msg, p, i, 0);
6924
0
        TO_MSG_BIT(msg, p, i, 1);
6925
0
        TO_MSG_BIT(msg, p, i, 2);
6926
0
        TO_MSG_BIT(msg, p, i, 3);
6927
0
        TO_MSG_BIT(msg, p, i, 4);
6928
0
        TO_MSG_BIT(msg, p, i, 5);
6929
0
        TO_MSG_BIT(msg, p, i, 6);
6930
0
        TO_MSG_BIT(msg, p, i, 7);
6931
0
    #endif
6932
0
    }
6933
0
}
6934
6935
/* Convert polynomial to message.
6936
 *
6937
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
6938
 *
6939
 * @param  [out]      msg  Message as a byte array.
6940
 * @param  [in, out]  p    Polynomial.
6941
 */
6942
void mlkem_to_msg(byte* msg, sword16* p)
6943
0
{
6944
#ifdef USE_INTEL_SPEEDUP
6945
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
6946
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6947
        /* Convert the polynomial into an array of bytes (message). */
6948
        mlkem_to_msg_avx512(msg, p);
6949
        RESTORE_VECTOR_REGISTERS();
6950
    }
6951
    else
6952
#endif
6953
     if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
6954
        /* Convert the polynomial into an array of bytes (message). */
6955
        mlkem_to_msg_avx2(msg, p);
6956
        RESTORE_VECTOR_REGISTERS();
6957
    }
6958
    else
6959
#endif
6960
0
    {
6961
0
        mlkem_to_msg_c(msg, p);
6962
0
    }
6963
0
}
6964
#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */
6965
#else
6966
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
6967
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
6968
/* Convert message to polynomial.
6969
 *
6970
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
6971
 *
6972
 * @param  [out]  p    Polynomial.
6973
 * @param  [in]   msg  Message as a byte array.
6974
 */
6975
void mlkem_from_msg(sword16* p, const byte* msg)
6976
{
6977
    mlkem_from_msg_neon(p, msg);
6978
}
6979
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE || !WOLFSSL_MLKEM_NO_DECAPSULATE */
6980
6981
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
6982
/* Convert polynomial to message.
6983
 *
6984
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
6985
 *
6986
 * @param  [out]      msg  Message as a byte array.
6987
 * @param  [in, out]  p    Polynomial.
6988
 */
6989
void mlkem_to_msg(byte* msg, sword16* p)
6990
{
6991
    mlkem_to_msg_neon(msg, p);
6992
}
6993
#endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */
6994
#endif /* !(__aarch64__ && WOLFSSL_ARMASM) */
6995
6996
/******************************************************************************/
6997
6998
/* Convert bytes to polynomial.
6999
 *
7000
 * Consecutive 12 bits hold each coefficient of polynomial.
7001
 * Used in decoding private and public keys.
7002
 *
7003
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
7004
 *
7005
 * @param  [out]  p  Vector of polynomials.
7006
 * @param  [in]   b  Array of bytes.
7007
 * @param  [in]   k  Number of polynomials in vector.
7008
 */
7009
static void mlkem_from_bytes_c(sword16* p, const byte* b, int k)
7010
0
{
7011
0
    int i;
7012
0
    int j;
7013
7014
0
    for (j = 0; j < k; j++) {
7015
0
        for (i = 0; i < MLKEM_N / 2; i++) {
7016
0
            p[2 * i + 0] = ((b[3 * i + 0] >> 0) |
7017
0
                            ((word16)b[3 * i + 1] << 8)) & 0xfff;
7018
0
            p[2 * i + 1] = ((b[3 * i + 1] >> 4) |
7019
0
                            ((word16)b[3 * i + 2] << 4)) & 0xfff;
7020
0
        }
7021
0
        p += MLKEM_N;
7022
0
        b += WC_ML_KEM_POLY_SIZE;
7023
0
    }
7024
0
}
7025
7026
/* Convert bytes to polynomial.
7027
 *
7028
 * Consecutive 12 bits hold each coefficient of polynomial.
7029
 * Used in decoding private and public keys.
7030
 *
7031
 * FIPS 203, Algorithm 6: ByteDecode_d(B)
7032
 *
7033
 * @param  [out]  p  Vector of polynomials.
7034
 * @param  [in]   b  Array of bytes.
7035
 * @param  [in]   k  Number of polynomials in vector.
7036
 */
7037
void mlkem_from_bytes(sword16* p, const byte* b, int k)
7038
0
{
7039
#ifdef USE_INTEL_SPEEDUP
7040
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
7041
    if (USE_INTEL_AVX512(cpuid_flags) &&
7042
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
7043
            (SAVE_VECTOR_REGISTERS2() == 0)) {
7044
        int i;
7045
7046
        for (i = 0; i < k; i++) {
7047
            mlkem_from_bytes_avx512_vbmi(p, b);
7048
            p += MLKEM_N;
7049
            b += WC_ML_KEM_POLY_SIZE;
7050
        }
7051
7052
        RESTORE_VECTOR_REGISTERS();
7053
    }
7054
    else
7055
#endif
7056
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
7057
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
7058
        int i;
7059
7060
        for (i = 0; i < k; i++) {
7061
            mlkem_from_bytes_avx512(p, b);
7062
            p += MLKEM_N;
7063
            b += WC_ML_KEM_POLY_SIZE;
7064
        }
7065
7066
        RESTORE_VECTOR_REGISTERS();
7067
    }
7068
    else
7069
#endif
7070
     if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
7071
        int i;
7072
7073
        for (i = 0; i < k; i++) {
7074
            mlkem_from_bytes_avx2(p, b);
7075
            p += MLKEM_N;
7076
            b += WC_ML_KEM_POLY_SIZE;
7077
        }
7078
7079
        RESTORE_VECTOR_REGISTERS();
7080
    }
7081
    else
7082
#endif
7083
0
    {
7084
0
        mlkem_from_bytes_c(p, b, k);
7085
0
    }
7086
0
}
7087
7088
/* Convert polynomial to bytes.
7089
 *
7090
 * Consecutive 12 bits hold each coefficient of polynomial.
7091
 * Used in encoding private and public keys.
7092
 *
7093
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
7094
 *
7095
 * @param  [out]      b  Array of bytes.
7096
 * @param  [in, out]  p  Polynomial.
7097
 * @param  [in]       k  Number of polynomials in vector.
7098
 */
7099
static void mlkem_to_bytes_c(byte* b, sword16* p, int k)
7100
13.4k
{
7101
13.4k
    int i;
7102
13.4k
    int j;
7103
7104
54.3k
    for (j = 0; j < k; j++) {
7105
        /* Reduce each coefficient to mod q. */
7106
40.8k
        mlkem_csubq_c(p);
7107
        /* All values are now positive. */
7108
7109
5.26M
        for (i = 0; i < MLKEM_N / 2; i++) {
7110
5.22M
            word16 t0 = (word16)p[2 * i];
7111
5.22M
            word16 t1 = (word16)p[2 * i + 1];
7112
5.22M
            b[3 * i + 0] = WC_OCTET(t0 >> 0);
7113
5.22M
            b[3 * i + 1] = WC_OCTET((t0 >> 8) | (t1 << 4));
7114
5.22M
            b[3 * i + 2] = WC_OCTET(t1 >> 4);
7115
5.22M
        }
7116
40.8k
        p += MLKEM_N;
7117
40.8k
        b += WC_ML_KEM_POLY_SIZE;
7118
40.8k
    }
7119
13.4k
}
7120
7121
/* Convert polynomial to bytes.
7122
 *
7123
 * Consecutive 12 bits hold each coefficient of polynomial.
7124
 * Used in encoding private and public keys.
7125
 *
7126
 * FIPS 203, Algorithm 5: ByteEncode_d(F)
7127
 *
7128
 * @param  [out]      b  Array of bytes.
7129
 * @param  [in, out]  p  Polynomial.
7130
 * @param  [in]       k  Number of polynomials in vector.
7131
 */
7132
void mlkem_to_bytes(byte* b, sword16* p, int k)
7133
13.4k
{
7134
#ifdef USE_INTEL_SPEEDUP
7135
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI
7136
    if (USE_INTEL_AVX512(cpuid_flags) &&
7137
            IS_INTEL_AVX512_VBMI(cpuid_flags) &&
7138
            (SAVE_VECTOR_REGISTERS2() == 0)) {
7139
        int i;
7140
7141
        for (i = 0; i < k; i++) {
7142
            mlkem_to_bytes_avx512_vbmi(b, p);
7143
            p += MLKEM_N;
7144
            b += WC_ML_KEM_POLY_SIZE;
7145
        }
7146
7147
        RESTORE_VECTOR_REGISTERS();
7148
    }
7149
    else
7150
#endif
7151
#ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512
7152
    if (USE_INTEL_AVX512(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
7153
        int i;
7154
7155
        for (i = 0; i < k; i++) {
7156
            mlkem_to_bytes_avx512(b, p);
7157
            p += MLKEM_N;
7158
            b += WC_ML_KEM_POLY_SIZE;
7159
        }
7160
7161
        RESTORE_VECTOR_REGISTERS();
7162
    }
7163
    else
7164
#endif
7165
     if (IS_INTEL_AVX2(cpuid_flags) && (SAVE_VECTOR_REGISTERS2() == 0)) {
7166
        int i;
7167
7168
        for (i = 0; i < k; i++) {
7169
            mlkem_to_bytes_avx2(b, p);
7170
            p += MLKEM_N;
7171
            b += WC_ML_KEM_POLY_SIZE;
7172
        }
7173
7174
        RESTORE_VECTOR_REGISTERS();
7175
    }
7176
    else
7177
#endif
7178
13.4k
    {
7179
13.4k
        mlkem_to_bytes_c(b, p, k);
7180
13.4k
    }
7181
13.4k
}
7182
7183
/**
7184
 * Check the vector coefficients are reduced modulo q.
7185
 *
7186
 * FIPS 203, Sections 7.2 and 7.3: encapsulation and decapsulation keys must
7187
 * decode to coefficients in Z_q; reject any that are not reduced.
7188
 *
7189
 * @param [in] p  Key - vector of polynomials.
7190
 * @param [in] k  Number of polynomials in vector.
7191
 * @return  0 when all values are in range.
7192
 * @return  PUBLIC_KEY_E when at least one value is out of range.
7193
 */
7194
int mlkem_check_reduced(const sword16* p, int k)
7195
0
{
7196
0
    int ret = 0;
7197
0
    int i;
7198
7199
0
    for (i = 0; i < k * MLKEM_N; i++) {
7200
0
        if (p[i] >= MLKEM_Q) {
7201
0
            ret = PUBLIC_KEY_E;
7202
0
            break;
7203
0
        }
7204
0
    }
7205
7206
0
    return ret;
7207
0
}
7208
7209
#endif /* WOLFSSL_HAVE_MLKEM */