Coverage Report

Created: 2026-11-12 06:59

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