Coverage Report

Created: 2026-09-20 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wolfssl-sp-math/wolfcrypt/src/wc_mlkem.c
Line
Count
Source
1
/* wc_mlkem.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
/* Possible ML-KEM options:
32
 *
33
 * WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM                                 Default: OFF
34
 *   Uses less dynamic memory to perform key generation.
35
 *   Has a small performance trade-off.
36
 *   Only usable with C implementation.
37
 *
38
 * WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM                             Default: OFF
39
 *   Uses less dynamic memory to perform encapsulation.
40
 *   Affects decapsulation too as encapsulation called.
41
 *   Has a small performance trade-off.
42
 *   Only usable with C implementation.
43
 *
44
 * WOLFSSL_MLKEM_NO_MAKE_KEY                                       Default: OFF
45
 *   Disable the make key or key generation API.
46
 *   Reduces the code size.
47
 *   Turn on when only doing encapsulation.
48
 *
49
 * WOLFSSL_MLKEM_NO_ENCAPSULATE                                    Default: OFF
50
 *   Disable the encapsulation API.
51
 *   Reduces the code size.
52
 *   Turn on when doing make key/decapsulation.
53
 *
54
 * WOLFSSL_MLKEM_NO_DECAPSULATE                                    Default: OFF
55
 *   Disable the decapsulation API.
56
 *   Reduces the code size.
57
 *   Turn on when only doing encapsulation.
58
 *
59
 * WOLFSSL_MLKEM_CACHE_A                                           Default: OFF
60
 *   Stores the matrix A during key generation for use in encapsulation when
61
 *   performing decapsulation.
62
 *   MlKemKey is 8KB larger but decapsulation is significantly faster.
63
 *   Turn on when performing make key and decapsulation with same object.
64
 *
65
 * WOLFSSL_MLKEM_DYNAMIC_KEYS                                      Default: OFF
66
 *   Dynamically allocates private and public key buffers instead of using
67
 *   static arrays in the MlKemKey struct. Right-sizes buffers to the actual
68
 *   ML-KEM level and only allocates the needed key parts (e.g., no private
69
 *   key buffer for encapsulate-only use).
70
 *   Cannot be used with WOLFSSL_NO_MALLOC.
71
 */
72
73
#define WC_FIPS_LL_CRYPTO
74
#define _WC_BUILDING_WC_MLKEM_C
75
76
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
77
78
#ifdef WOLFSSL_HAVE_MLKEM
79
80
#if FIPS_VERSION3_GE(7,0,0)
81
    #ifdef USE_WINDOWS_API
82
        #pragma code_seg(".fipsA$na")
83
        #pragma const_seg(".fipsB$na")
84
    #endif
85
#endif
86
87
#ifdef WC_MLKEM_NO_ASM
88
    #undef USE_INTEL_SPEEDUP
89
    #undef WOLFSSL_ARMASM
90
    #undef WOLFSSL_RISCV_ASM
91
#endif
92
93
#include <wolfssl/wolfcrypt/wc_mlkem.h>
94
#include <wolfssl/wolfcrypt/hash.h>
95
#include <wolfssl/wolfcrypt/memory.h>
96
#ifdef WOLF_CRYPTO_CB
97
    #include <wolfssl/wolfcrypt/cryptocb.h>
98
#endif
99
100
#ifdef NO_INLINE
101
    #include <wolfssl/wolfcrypt/misc.h>
102
#else
103
    #define WOLFSSL_MISC_INCLUDED
104
    #include <wolfcrypt/src/misc.c>
105
#endif
106
107
#if defined(USE_INTEL_SPEEDUP) || \
108
    (defined(__aarch64__) && defined(WOLFSSL_ARMASM))
109
    #if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
110
        defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
111
        #error "Can't use small memory with assembly optimized code"
112
    #endif
113
#endif
114
#if defined(WOLFSSL_MLKEM_CACHE_A)
115
    #if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \
116
        defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM)
117
        #error "Can't cache A with small memory code"
118
    #endif
119
#endif
120
121
#if defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
122
    defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
123
    defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
124
    #error "No ML-KEM operations to be built."
125
#endif
126
#if defined(WOLFSSL_MLKEM_DYNAMIC_KEYS) && defined(WOLFSSL_NO_MALLOC)
127
    #error "Cannot use dynamic key buffers without malloc"
128
#endif
129
130
#if FIPS_VERSION3_GE(7,0,0)
131
    const unsigned int wolfCrypt_FIPS_mlkem_ro_sanity[2] =
132
                                                     { 0x1a2b3c4d, 0x00000019 };
133
    int wolfCrypt_FIPS_MLKEM_sanity(void)
134
    {
135
        return 0;
136
    }
137
#endif
138
139
#ifdef DEBUG_MLKEM
140
void print_polys(const char* name, const sword16* a, int d1, int d2);
141
void print_polys(const char* name, const sword16* a, int d1, int d2)
142
{
143
    int i;
144
    int j;
145
    int k;
146
147
    fprintf(stderr, "%s: %d %d\n", name, d1, d2);
148
    for (i = 0; i < d1; i++) {
149
        for (j = 0; j < d2; j++) {
150
            for (k = 0; k < 256; k++) {
151
                fprintf(stderr, "%9d,", a[(i*d2*256) + (j*256) + k]);
152
                if ((k % 8) == 7) fprintf(stderr, "\n");
153
            }
154
            fprintf(stderr, "\n");
155
        }
156
    }
157
}
158
#endif
159
160
#ifdef DEBUG_MLKEM
161
void print_data(const char* name, const byte* d, int len);
162
void print_data(const char* name, const byte* d, int len)
163
{
164
    int i;
165
166
    fprintf(stderr, "%s\n", name);
167
    for (i = 0; i < len; i++) {
168
        fprintf(stderr, "0x%02x,", d[i]);
169
        if ((i % 16) == 15) fprintf(stderr, "\n");
170
    }
171
    fprintf(stderr, "\n");
172
}
173
#endif
174
175
/******************************************************************************/
176
177
/* Use SHA3-256 to generate 32-bytes of hash. */
178
4.48k
#define MLKEM_HASH_H            mlkem_hash256
179
/* Use SHA3-512 to generate 64-bytes of hash. */
180
4.49k
#define MLKEM_HASH_G            mlkem_hash512
181
/* Use SHAKE-256 as a key derivation function (KDF). */
182
#if defined(USE_INTEL_SPEEDUP) || \
183
        (defined(WOLFSSL_ARMASM) && defined(__aarch64__))
184
    #define MLKEM_KDF               mlkem_kdf
185
#else
186
    #define MLKEM_KDF               wc_Shake256Hash
187
#endif
188
189
/******************************************************************************/
190
191
/* Helper function with volatile variable, to force compiler not to optimize
192
 * code in mlkem_from_msg().
193
 */
194
sword16 wc_mlkem_opt_blocker(void);
195
0
sword16 wc_mlkem_opt_blocker(void) {
196
0
    static volatile sword16 static_mlkem_opt_blocker = 0;
197
0
    return static_mlkem_opt_blocker;
198
0
}
199
200
/******************************************************************************/
201
202
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
203
/* Get the k value (number of polynomials in a vector) from the key type.
204
 *
205
 * @param  [in]  key  ML-KEM key object.
206
 * @return  k value for the key type, or 0 if not recognized.
207
 */
208
static int mlkemkey_get_k(const MlKemKey* key)
209
4.50k
{
210
4.50k
    switch (key->type) {
211
0
#ifndef WOLFSSL_NO_ML_KEM
212
0
    #ifdef WOLFSSL_WC_ML_KEM_512
213
0
        case WC_ML_KEM_512:
214
0
            return WC_ML_KEM_512_K;
215
0
    #endif
216
0
    #ifdef WOLFSSL_WC_ML_KEM_768
217
4.35k
        case WC_ML_KEM_768:
218
4.35k
            return WC_ML_KEM_768_K;
219
0
    #endif
220
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
221
142
        case WC_ML_KEM_1024:
222
142
            return WC_ML_KEM_1024_K;
223
0
    #endif
224
0
#endif
225
#ifdef WOLFSSL_MLKEM_KYBER
226
    #ifdef WOLFSSL_KYBER512
227
        case KYBER512:
228
            return KYBER512_K;
229
    #endif
230
    #ifdef WOLFSSL_KYBER768
231
        case KYBER768:
232
            return KYBER768_K;
233
    #endif
234
    #ifdef WOLFSSL_KYBER1024
235
        case KYBER1024:
236
            return KYBER1024_K;
237
    #endif
238
#endif
239
0
        default:
240
0
            return 0;
241
4.50k
    }
242
4.50k
}
243
#endif
244
245
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
246
/* Allocate (or reallocate) the private key buffer, right-sized for k.
247
 *
248
 * @param  [in, out]  key  ML-KEM key object.
249
 * @param  [in]       k    Number of polynomials in a vector.
250
 * @return  0 on success.
251
 * @return  MEMORY_E when dynamic memory allocation fails.
252
 */
253
static int mlkemkey_alloc_priv(MlKemKey* key, unsigned int k)
254
{
255
    word32 sz = (word32)(k * MLKEM_N * sizeof(sword16));
256
    if (key->priv != NULL) {
257
        ForceZero(key->priv, key->privAllocSz);
258
        XFREE(key->priv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
259
        key->priv = NULL;
260
        key->privAllocSz = 0;
261
    }
262
    key->priv = (sword16*)XMALLOC(sz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
263
    if (key->priv == NULL) {
264
        return MEMORY_E;
265
    }
266
    key->privAllocSz = sz;
267
    return 0;
268
}
269
270
/* Allocate (or reallocate) the public key buffer, right-sized for k.
271
 *
272
 * @param  [in, out]  key  ML-KEM key object.
273
 * @param  [in]       k    Number of polynomials in a vector.
274
 * @return  0 on success.
275
 * @return  MEMORY_E when dynamic memory allocation fails.
276
 */
277
static int mlkemkey_alloc_pub(MlKemKey* key, unsigned int k)
278
{
279
    if (key->pub != NULL) {
280
        XFREE(key->pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
281
        key->pub = NULL;
282
    }
283
    key->pub = (sword16*)XMALLOC(k * MLKEM_N * sizeof(sword16), key->heap,
284
        DYNAMIC_TYPE_TMP_BUFFER);
285
    if (key->pub == NULL) {
286
        return MEMORY_E;
287
    }
288
    return 0;
289
}
290
291
#ifdef WOLFSSL_MLKEM_CACHE_A
292
/* Allocate (or reallocate) the A matrix buffer, right-sized for k.
293
 *
294
 * @param  [in, out]  key  ML-KEM key object.
295
 * @param  [in]       k    Number of polynomials in a vector.
296
 * @return  0 on success.
297
 * @return  MEMORY_E when dynamic memory allocation fails.
298
 */
299
static int mlkemkey_alloc_a(MlKemKey* key, unsigned int k)
300
{
301
    int ret = 0;
302
303
    if (key->a != NULL) {
304
        XFREE(key->a, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
305
        key->a = NULL;
306
    }
307
    key->a = (sword16*)XMALLOC(k * k * MLKEM_N * sizeof(sword16), key->heap,
308
        DYNAMIC_TYPE_TMP_BUFFER);
309
    if (key->a == NULL) {
310
        ret = MEMORY_E;
311
    }
312
313
    return ret;
314
}
315
#endif /* WOLFSSL_MLKEM_CACHE_A */
316
#endif /* WOLFSSL_MLKEM_DYNAMIC_KEYS */
317
318
/******************************************************************************/
319
320
#ifndef WC_NO_CONSTRUCTORS
321
/**
322
 * Create a new ML-KEM key object.
323
 *
324
 * Allocates and initializes a ML-KEM key object.
325
 *
326
 * @param  [in]  type   Type of key:
327
 *                        WC_ML_KEM_512, WC_ML_KEM_768, WC_ML_KEM_1024,
328
 *                        KYBER512, KYBER768, KYBER1024.
329
 * @param  [in]  heap   Dynamic memory hint.
330
 * @param  [in]  devId  Device Id.
331
 * @return  Pointer to new MlKemKey object on success.
332
 * @return  NULL on failure.
333
 */
334
MlKemKey* wc_MlKemKey_New(int type, void* heap, int devId)
335
0
{
336
0
    int ret;
337
0
    MlKemKey* key;
338
339
0
    key = (MlKemKey*)XMALLOC(sizeof(MlKemKey), heap, DYNAMIC_TYPE_TMP_BUFFER);
340
0
    if (key != NULL) {
341
0
        ret = wc_MlKemKey_Init(key, type, heap, devId);
342
0
        if (ret != 0) {
343
0
            XFREE(key, heap, DYNAMIC_TYPE_TMP_BUFFER);
344
0
            key = NULL;
345
0
        }
346
0
    }
347
348
0
    return key;
349
0
}
350
351
/**
352
 * Delete and free a ML-KEM key object.
353
 *
354
 * Frees resources associated with a ML-KEM key object and sets pointer to NULL.
355
 *
356
 * @param  [in]       key    ML-KEM key object to delete.
357
 * @param  [in, out]  key_p  Pointer to key pointer to set to NULL.
358
 * @return  0 on success.
359
 * @return  BAD_FUNC_ARG when key is NULL.
360
 */
361
int wc_MlKemKey_Delete(MlKemKey* key, MlKemKey** key_p)
362
0
{
363
0
    int ret = 0;
364
365
0
    if (key == NULL) {
366
0
        ret = BAD_FUNC_ARG;
367
0
    }
368
0
    else {
369
0
        void* heap = key->heap;
370
371
0
        wc_MlKemKey_Free(key);
372
0
        XFREE(key, heap, DYNAMIC_TYPE_TMP_BUFFER);
373
0
        if (key_p != NULL) {
374
0
            *key_p = NULL;
375
0
        }
376
0
    }
377
378
0
    return ret;
379
0
}
380
#endif /* !WC_NO_CONSTRUCTORS */
381
382
/**
383
 * Initialize the ML-KEM key.
384
 *
385
 * @param  [out]  key    ML-KEM key object to initialize.
386
 * @param  [in]   type   Type of key:
387
 *                         WC_ML_KEM_512, WC_ML_KEM_768, WC_ML_KEM_1024,
388
 *                         KYBER512, KYBER768, KYBER1024.
389
 * @param  [in]   heap   Dynamic memory hint.
390
 * @param  [in]   devId  Device Id.
391
 * @return  0 on success.
392
 * @return  BAD_FUNC_ARG when key is NULL or type is unrecognized.
393
 * @return  NOT_COMPILED_IN when key type is not supported.
394
 */
395
int wc_MlKemKey_Init(MlKemKey* key, int type, void* heap, int devId)
396
4.51k
{
397
4.51k
    int ret = 0;
398
399
    /* Validate key. */
400
4.51k
    if (key == NULL) {
401
0
        ret = BAD_FUNC_ARG;
402
0
    }
403
4.51k
    if (ret == 0) {
404
        /* Validate type. */
405
4.51k
        switch (type) {
406
0
    #ifndef WOLFSSL_NO_ML_KEM
407
0
        case WC_ML_KEM_512:
408
        #ifndef WOLFSSL_WC_ML_KEM_512
409
            /* Code not compiled in for ML-KEM-512. */
410
            ret = NOT_COMPILED_IN;
411
        #endif
412
0
            break;
413
4.36k
        case WC_ML_KEM_768:
414
        #ifndef WOLFSSL_WC_ML_KEM_768
415
            /* Code not compiled in for ML-KEM-768. */
416
            ret = NOT_COMPILED_IN;
417
        #endif
418
4.36k
            break;
419
142
        case WC_ML_KEM_1024:
420
        #ifndef WOLFSSL_WC_ML_KEM_1024
421
            /* Code not compiled in for ML-KEM-1024. */
422
            ret = NOT_COMPILED_IN;
423
        #endif
424
142
            break;
425
0
    #endif
426
    #ifdef WOLFSSL_MLKEM_KYBER
427
        case KYBER512:
428
        #ifndef WOLFSSL_KYBER512
429
            /* Code not compiled in for Kyber-512. */
430
            ret = NOT_COMPILED_IN;
431
        #endif
432
            break;
433
        case KYBER768:
434
        #ifndef WOLFSSL_KYBER768
435
            /* Code not compiled in for Kyber-768. */
436
            ret = NOT_COMPILED_IN;
437
        #endif
438
            break;
439
        case KYBER1024:
440
        #ifndef WOLFSSL_KYBER1024
441
            /* Code not compiled in for Kyber-1024. */
442
            ret = NOT_COMPILED_IN;
443
        #endif
444
            break;
445
    #endif
446
0
        default:
447
            /* No other values supported. */
448
0
            ret = BAD_FUNC_ARG;
449
0
            break;
450
4.51k
        }
451
4.51k
    }
452
4.51k
    if (ret == 0) {
453
        /* Keep type for parameters. */
454
4.51k
        key->type = type;
455
        /* Cache heap pointer. */
456
4.51k
        key->heap = heap;
457
4.51k
    #ifdef WOLF_CRYPTO_CB
458
4.51k
        key->devCtx = NULL;
459
4.51k
        key->devId = devId;
460
4.51k
    #endif
461
4.51k
#ifdef WOLF_PRIVATE_KEY_ID
462
4.51k
        key->idLen = 0;
463
4.51k
        key->labelLen = 0;
464
4.51k
#endif
465
4.51k
        key->flags = 0;
466
467
    #ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
468
        key->priv = NULL;
469
        key->pub = NULL;
470
        key->privAllocSz = 0;
471
    #ifdef WOLFSSL_MLKEM_CACHE_A
472
        key->a = NULL;
473
    #endif
474
    #endif
475
476
        /* Zero out the PRF object. */
477
4.51k
        XMEMSET(&key->prf, 0, sizeof(key->prf));
478
479
        /* Initialize the hash algorithm object. */
480
4.51k
        ret = mlkem_hash_new(&key->hash, heap, devId);
481
4.51k
    }
482
4.51k
    if (ret == 0) {
483
        /* Initialize the PRF algorithm object. */
484
4.51k
        ret = mlkem_prf_new(&key->prf, heap, devId);
485
4.51k
    }
486
4.51k
    if (ret == 0) {
487
4.51k
        mlkem_init();
488
4.51k
    }
489
490
4.51k
    (void)devId;
491
492
4.51k
    return ret;
493
4.51k
}
494
495
#ifdef WOLF_PRIVATE_KEY_ID
496
/**
497
 * Initialize the ML-KEM key with an id.
498
 *
499
 * @param  [out]  key    ML-KEM key object to initialize.
500
 * @param  [in]   type   Type of key:
501
 *                         WC_ML_KEM_512, WC_ML_KEM_768, WC_ML_KEM_1024,
502
 *                         KYBER512, KYBER768, KYBER1024.
503
 * @param  [in]   id     Identifier of key.
504
 * @param  [in]   len    Length of key identifier in bytes.
505
 * @param  [in]   heap   Dynamic memory hint.
506
 * @param  [in]   devId  Device Id.
507
 * @return  0 on success.
508
 * @return  BAD_FUNC_ARG when key is NULL, id is NULL but len is not zero, or
509
 *          type is unrecognized.
510
 * @return  BUFFER_E when len is out of range.
511
 * @return  NOT_COMPILED_IN when key type is not supported.
512
 */
513
int wc_MlKemKey_Init_Id(MlKemKey* key, int type, const unsigned char* id,
514
    int len, void* heap, int devId)
515
0
{
516
0
    int ret = 0;
517
518
    /* Validate parameters. */
519
0
    if ((key == NULL) || (id == NULL && len != 0)) {
520
0
        ret = BAD_FUNC_ARG;
521
0
    }
522
0
    if ((ret == 0) && ((len < 0) || (len > MLKEM_MAX_ID_LEN))) {
523
0
        ret = BUFFER_E;
524
0
    }
525
526
0
    if (ret == 0) {
527
        /* Initialize key. */
528
0
        ret = wc_MlKemKey_Init(key, type, heap, devId);
529
0
    }
530
0
    if ((ret == 0) && (id != NULL) && (len != 0)) {
531
        /* Store key identifier. */
532
0
        XMEMCPY(key->id, id, (size_t)len);
533
0
        key->idLen = len;
534
0
    }
535
536
0
    return ret;
537
0
}
538
539
/**
540
 * Initialize the ML-KEM key with a label.
541
 *
542
 * @param  [out]  key    ML-KEM key object to initialize.
543
 * @param  [in]   type   Type of key:
544
 *                         WC_ML_KEM_512, WC_ML_KEM_768, WC_ML_KEM_1024,
545
 *                         KYBER512, KYBER768, KYBER1024.
546
 * @param  [in]   label  Label of key. Must be a null-terminated string.
547
 * @param  [in]   heap   Dynamic memory hint.
548
 * @param  [in]   devId  Device Id.
549
 * @return  0 on success.
550
 * @return  BAD_FUNC_ARG when key or label is NULL, or type is unrecognized.
551
 * @return  BUFFER_E when label is too small or big.
552
 * @return  NOT_COMPILED_IN when key type is not supported.
553
 */
554
int wc_MlKemKey_Init_Label(MlKemKey* key, int type, const char* label,
555
    void* heap, int devId)
556
0
{
557
0
    int ret = 0;
558
0
    int labelLen = 0;
559
560
    /* Validate parameters. */
561
0
    if ((key == NULL) || (label == NULL)) {
562
0
        ret = BAD_FUNC_ARG;
563
0
    }
564
0
    if (ret == 0) {
565
        /* Validate label length. */
566
0
        labelLen = (int)XSTRLEN(label);
567
0
        if ((labelLen == 0) || (labelLen > MLKEM_MAX_LABEL_LEN)) {
568
0
            ret = BUFFER_E;
569
0
        }
570
0
    }
571
572
0
    if (ret == 0) {
573
        /* Initialize key. */
574
0
        ret = wc_MlKemKey_Init(key, type, heap, devId);
575
0
    }
576
0
    if (ret == 0) {
577
        /* Don't save string in key->label with null terminator.
578
         * Use key->labelLen to get the length if required. */
579
0
        XMEMCPY(key->label, label, (size_t)labelLen);
580
0
        key->labelLen = labelLen;
581
0
    }
582
583
0
    return ret;
584
0
}
585
#endif
586
587
/**
588
 * Free the ML-KEM key object.
589
 *
590
 * @param  [in, out]  key   ML-KEM key object to dispose of.
591
 * @return  0 on success.
592
 */
593
int wc_MlKemKey_Free(MlKemKey* key)
594
8.81k
{
595
8.81k
    if (key != NULL) {
596
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
597
        if (key->devId != INVALID_DEVID) {
598
            (void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK,
599
                WC_PK_TYPE_PQC_KEM_KEYGEN, WC_PQC_KEM_TYPE_MLKEM, (void*)key);
600
            /* always continue to software cleanup */
601
        }
602
#endif
603
        /* Dispose of PRF object. */
604
4.51k
        mlkem_prf_free(&key->prf);
605
        /* Dispose of hash object. */
606
4.51k
        mlkem_hash_free(&key->hash);
607
        /* Ensure all private data is zeroed. */
608
4.51k
        ForceZero(&key->hash, sizeof(key->hash));
609
4.51k
        ForceZero(&key->prf, sizeof(key->prf));
610
4.51k
#ifdef WOLF_CRYPTO_CB
611
4.51k
        key->hash.devId = INVALID_DEVID;
612
4.51k
        key->prf.devId = INVALID_DEVID;
613
4.51k
#endif
614
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
615
        if (key->priv != NULL) {
616
            ForceZero(key->priv, key->privAllocSz);
617
            XFREE(key->priv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
618
            key->priv = NULL;
619
            key->privAllocSz = 0;
620
        }
621
        if (key->pub != NULL) {
622
            XFREE(key->pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
623
            key->pub = NULL;
624
        }
625
    #ifdef WOLFSSL_MLKEM_CACHE_A
626
        if (key->a != NULL) {
627
            XFREE(key->a, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
628
            key->a = NULL;
629
        }
630
    #endif
631
#else
632
4.51k
        ForceZero(key->priv, sizeof(key->priv));
633
4.51k
#endif
634
4.51k
        ForceZero(key->z, sizeof(key->z));
635
636
        /* Clear flags as values are no longer set. */
637
4.51k
        key->flags = 0;
638
4.51k
#ifdef WOLF_CRYPTO_CB
639
        /* Mark the key as having no device so a second free does not call
640
         * out to it again. */
641
4.51k
        key->devCtx = NULL;
642
4.51k
        key->devId = INVALID_DEVID;
643
4.51k
#endif
644
4.51k
    }
645
646
8.81k
    return 0;
647
8.81k
}
648
649
/******************************************************************************/
650
651
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
652
/**
653
 * Make a ML-KEM key object using a random number generator.
654
 *
655
 * FIPS 203 - Algorithm 19: ML-KEM.KeyGen()
656
 * Generates an encapsulation key and a corresponding decapsulation key.
657
 *   1: d <- B_32                                        >  d is 32 random bytes
658
 *   2: z <- B_32                                        >  z is 32 random bytes
659
 *   3: if d == NULL or z == NULL then
660
 *   4:   return falsum
661
 *                  > return an error indication if random bit generation failed
662
 *   5: end if
663
 *   6: (ek,dk) <- ML-KEM.KeyGen_Internal(d, z)
664
 *                                       > run internal key generation algorithm
665
 *   7: return (ek,dk)
666
 *
667
 * @param  [in, out]  key   ML-KEM key object.
668
 * @param  [in]       rng   Random number generator.
669
 * @return  0 on success.
670
 * @return  BAD_FUNC_ARG when key or rng is NULL.
671
 * @return  MEMORY_E when dynamic memory allocation failed.
672
 * @return  RNG_FAILURE_E when generating random numbers failed.
673
 * @return  DRBG_CONT_FAILURE when random number generator health check fails.
674
 * @return  ML_KEM_PCT_E when pairwise consistency test fails. FIPS only.
675
 * @return  BAD_COND_E when fault attack detected.
676
 * @return  NOT_COMPILED_IN when no random number generator is compiled in or
677
 *          key type is not supported.
678
 */
679
int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng)
680
4.26k
{
681
4.26k
#ifndef WC_NO_RNG
682
4.26k
    int ret = 0;
683
4.26k
    unsigned char rand[WC_ML_KEM_MAKEKEY_RAND_SZ];
684
685
    /* Validate parameters. */
686
4.26k
    if ((key == NULL) || (rng == NULL)) {
687
0
        ret = BAD_FUNC_ARG;
688
0
    }
689
690
4.26k
#ifdef WOLF_CRYPTO_CB
691
4.26k
#ifndef WOLF_CRYPTO_CB_FIND
692
4.26k
    if ((ret == 0) && (key->devId != INVALID_DEVID)) {
693
#else
694
    if (ret == 0) {
695
#endif
696
0
        ret = wc_CryptoCb_MakePqcKemKey(rng, WC_PQC_KEM_TYPE_MLKEM, key->type,
697
0
            key);
698
0
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
699
0
            ret = BAD_STATE_E; /* async unsupported for KEM keygen */
700
0
        if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
701
0
            return ret;
702
        /* fall-through when unavailable */
703
0
        ret = 0;
704
0
    }
705
4.26k
#endif
706
707
4.26k
    if (ret == 0) {
708
        /* Generate random to use with PRFs.
709
         * Step 1: d is 32 random bytes
710
         * Step 2: z is 32 random bytes
711
         */
712
4.26k
        ret = wc_RNG_GenerateBlock(rng, rand, WC_ML_KEM_SYM_SZ * 2);
713
        /* Step 3: ret is not zero when d == NULL or z == NULL. */
714
        /* rand now holds the secret seeds d||z; register before key gen /
715
         * PCT so any future early-exit before the ForceZero is caught. */
716
#ifdef WOLFSSL_CHECK_MEM_ZERO
717
        wc_MemZero_Add("mlkem makekey rand", (void*)rand, (word32)sizeof(rand));
718
#endif
719
4.26k
    }
720
4.26k
    if (ret == 0) {
721
        /* Make a key pair from the random.
722
         * Step 6. run internal key generation algorithm
723
         * Step 7. public and private key are stored in key
724
         */
725
4.26k
        ret = wc_MlKemKey_MakeKeyWithRandom(key, rand, sizeof(rand));
726
4.26k
    }
727
728
    /* No key-pair test here: wc_MlKemKey_MakeKeyWithRandom(), called above,
729
     * already runs it on every generation path.  Guarded on the version, not
730
     * HAVE_FIPS: src/include.am only compiles this file under
731
     * BUILD_FIPS_V7_PLUS, so the two are equivalent here. */
732
733
    /* Ensure seeds are zeroized. */
734
4.26k
    ForceZero((void*)rand, (word32)sizeof(rand));
735
#ifdef WOLFSSL_CHECK_MEM_ZERO
736
    wc_MemZero_Check((void*)rand, (word32)sizeof(rand));
737
#endif
738
739
    /* Step 4: return ret != 0 on falsum or internal key generation failure. */
740
4.26k
    return ret;
741
#else
742
    (void)key;
743
    (void)rng;
744
    return NOT_COMPILED_IN;
745
#endif /* WC_NO_RNG */
746
4.26k
}
747
748
/**
749
 * Make a ML-KEM key object using random data.
750
 *
751
 * FIPS 203 - Algorithm 16: ML-KEM.KeyGen_internal(d,z)
752
 * Uses randomness to generate an encapsulation key and a corresponding
753
 * decapsulation key.
754
 *   1: (ek_PKE,dk_PKE) <- K-PKE.KeyGen(d)        > run key generation for K-PKE
755
 *   ...
756
 *
757
 * FIPS 203 - Algorithm 13: K-PKE.KeyGen(d)
758
 * Uses randomness to generate an encryption key and a corresponding decryption
759
 * key.
760
 *   1: (rho,sigma) <- G(d||k)
761
 *                         > expand 32+1 bytes to two pseudorandom 32-byte seeds
762
 *   2: N <- 0
763
 *   3-7: generate matrix A_hat
764
 *   8-11: generate s
765
 *   12-15: generate e
766
 *   16-18: calculate t_hat from A_hat, s and e
767
 *   ...
768
 *
769
 * @param  [in, out]  key   ML-KEM key object.
770
 * @param  [in]       rand  Random data.
771
 * @param  [in]       len   Length of random data in bytes.
772
 * @return  0 on success.
773
 * @return  BAD_FUNC_ARG when key or rand is NULL.
774
 * @return  BUFFER_E when length is not WC_ML_KEM_MAKEKEY_RAND_SZ.
775
 * @return  NOT_COMPILED_IN when key type is not supported.
776
 * @return  MEMORY_E when dynamic memory allocation failed.
777
 * @return  BAD_COND_E when fault attack detected.
778
 * @return  ML_KEM_PCT_E when the key pair fails its consistency test.  The
779
 *          key is freed in that case and must be re-initialised before reuse.
780
 */
781
int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand,
782
    int len)
783
4.50k
{
784
4.50k
    byte buf[2 * WC_ML_KEM_SYM_SZ + 1];
785
4.50k
    byte* rho = buf;
786
4.50k
#ifndef WC_MLKEM_FAULT_HARDEN
787
4.50k
    byte* sigma = buf + WC_ML_KEM_SYM_SZ;
788
#else
789
    byte sigma[WC_ML_KEM_SYM_SZ + 1];
790
#endif
791
4.50k
#ifndef WOLFSSL_NO_MALLOC
792
4.50k
    sword16* e = NULL;
793
#else
794
#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM
795
#ifndef WOLFSSL_MLKEM_CACHE_A
796
    sword16 e[(WC_ML_KEM_MAX_K + 1) * WC_ML_KEM_MAX_K * MLKEM_N];
797
#else
798
    sword16 e[WC_ML_KEM_MAX_K * MLKEM_N];
799
#endif
800
#else
801
    sword16 e[WC_ML_KEM_MAX_K * MLKEM_N];
802
#endif
803
#endif
804
4.50k
#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM
805
4.50k
    sword16* a = NULL;
806
4.50k
#endif
807
4.50k
    sword16* s = NULL;
808
4.50k
    sword16* t = NULL;
809
4.50k
    int ret = 0;
810
4.50k
    int k = 0;
811
812
#ifdef WOLFSSL_CHECK_MEM_ZERO
813
    /* buf is only conditionally filled by G() below; define it on all paths so
814
     * the later registration/Check are safe. */
815
    XMEMSET(buf, 0, sizeof(buf));
816
#endif
817
818
    /* Validate parameters. */
819
4.50k
    if ((key == NULL) || (rand == NULL)) {
820
0
        ret = BAD_FUNC_ARG;
821
0
    }
822
4.50k
    if ((ret == 0) && (len != WC_ML_KEM_MAKEKEY_RAND_SZ)) {
823
0
        ret = BUFFER_E;
824
0
    }
825
826
4.50k
    if (ret == 0) {
827
4.50k
        key->flags = 0;
828
829
        /* Establish parameters based on key type. */
830
4.50k
        k = mlkemkey_get_k(key);
831
4.50k
        if (k == 0) {
832
0
            ret = NOT_COMPILED_IN;
833
0
        }
834
4.50k
    }
835
836
4.50k
#ifndef WOLFSSL_NO_MALLOC
837
4.50k
    if (ret == 0) {
838
        /* Allocate dynamic memory for matrix and error vector. */
839
4.50k
#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM
840
4.50k
#ifndef WOLFSSL_MLKEM_CACHE_A
841
        /* e (v) | a (m) */
842
4.50k
        e = (sword16*)XMALLOC((size_t)((k + 1) * k * MLKEM_N) * sizeof(sword16),
843
4.50k
            key->heap, DYNAMIC_TYPE_TMP_BUFFER);
844
#else
845
        /* e (v) */
846
        e = (sword16*)XMALLOC((size_t)(k * MLKEM_N) * sizeof(sword16),
847
            key->heap, DYNAMIC_TYPE_TMP_BUFFER);
848
#endif
849
#else
850
        /* e (v) */
851
        e = (sword16*)XMALLOC((size_t)(k * MLKEM_N) * sizeof(sword16),
852
            key->heap, DYNAMIC_TYPE_TMP_BUFFER);
853
#endif
854
4.50k
        if (e == NULL) {
855
8
            ret = MEMORY_E;
856
8
        }
857
4.50k
    }
858
4.50k
#endif
859
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
860
    if (ret == 0) {
861
        ret = mlkemkey_alloc_priv(key, (unsigned int)k);
862
    }
863
    if (ret == 0) {
864
        ret = mlkemkey_alloc_pub(key, (unsigned int)k);
865
    }
866
#ifdef WOLFSSL_MLKEM_CACHE_A
867
    if (ret == 0) {
868
        ret = mlkemkey_alloc_a(key, (unsigned int)k);
869
    }
870
#endif
871
#endif
872
4.50k
    if (ret == 0) {
873
4.49k
        const byte* d = rand;
874
875
#ifdef WOLFSSL_MLKEM_CACHE_A
876
        a = key->a;
877
#elif !defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM)
878
        /* Matrix A allocated at end of error vector. */
879
4.49k
        a = e + (k * MLKEM_N);
880
4.49k
#endif
881
882
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
883
        if (key->type & MLKEM_KYBER)
884
#endif
885
#ifdef WOLFSSL_MLKEM_KYBER
886
        {
887
            /* Expand 32 bytes of random to 64. */
888
            ret = MLKEM_HASH_G(&key->hash, d, WC_ML_KEM_SYM_SZ, NULL, 0, buf);
889
        }
890
#endif
891
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
892
        else
893
#endif
894
4.49k
#ifndef WOLFSSL_NO_ML_KEM
895
4.49k
        {
896
4.49k
            buf[0] = (byte)k;
897
            /* Expand 33 bytes of random to 64.
898
             * Alg 13: Step 1: (rho,sigma) <- G(d||k)
899
             */
900
4.49k
            ret = MLKEM_HASH_G(&key->hash, d, WC_ML_KEM_SYM_SZ, buf, 1, buf);
901
4.49k
        }
902
4.49k
#endif
903
4.49k
    }
904
#ifdef WC_MLKEM_FAULT_HARDEN
905
    if (ret == 0) {
906
        XMEMCPY(sigma, buf + WC_ML_KEM_SYM_SZ, WC_ML_KEM_SYM_SZ);
907
        /* sigma now holds the secret noise seed; register it (FAULT_HARDEN
908
         * build only, where sigma is its own stack buffer). */
909
#ifdef WOLFSSL_CHECK_MEM_ZERO
910
        wc_MemZero_Add("mlkem keygen sigma", sigma, sizeof(sigma));
911
#endif
912
        /* Check that correct data was copied and pointer was not faulted. */
913
        if (XMEMCMP(sigma, rho, WC_ML_KEM_SYM_SZ) == 0) {
914
            ret = BAD_COND_E;
915
        }
916
        /* Check that sigma is after rho - rho pointer may have been modified.
917
         */
918
        if (XMEMCMP(sigma, rho + WC_ML_KEM_SYM_SZ, WC_ML_KEM_SYM_SZ) != 0) {
919
            ret = BAD_COND_E;
920
        }
921
    }
922
#endif
923
    /* buf holds rho||sigma; sigma is the secret noise seed. Now that G() has
924
     * filled it, register buf before key generation so any later exit before
925
     * the ForceZero is covered. */
926
#ifdef WOLFSSL_CHECK_MEM_ZERO
927
    wc_MemZero_Add("mlkem keygen buf", buf, sizeof(buf));
928
#endif
929
4.50k
    if (ret == 0) {
930
4.49k
        const byte* z = rand + WC_ML_KEM_SYM_SZ;
931
4.49k
        s = key->priv;
932
4.49k
        t = key->pub;
933
934
        /* Cache the public seed for use in encapsulation and encoding public
935
         * key. */
936
4.49k
        XMEMCPY(key->pubSeed, rho, WC_ML_KEM_SYM_SZ);
937
        /* Cache the z value for decapsulation and encoding private key. */
938
4.49k
        XMEMCPY(key->z, z, sizeof(key->z));
939
940
        /* Initialize PRF for use in noise generation. */
941
4.49k
        mlkem_prf_init(&key->prf);
942
4.49k
#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM
943
        /* Generate noise using PRF.
944
         * Alg 13: Steps 8-15: generate s and e
945
         */
946
4.49k
        ret = mlkem_get_noise(&key->prf, k, s, e, NULL, sigma);
947
4.49k
    }
948
4.50k
    if (ret == 0) {
949
        /* Generate the matrix A.
950
         * Alg 13: Steps 3-7
951
         */
952
4.49k
        ret = mlkem_gen_matrix(&key->prf, a, k, rho, 0);
953
4.49k
    }
954
4.50k
    if (ret == 0) {
955
        /* Generate key pair from random data.
956
         * Alg 13: Steps 16-18.
957
         */
958
4.48k
        mlkem_keygen(s, t, e, a, k);
959
#else
960
        /* Generate noise using PRF.
961
         * Alg 13: Steps 8-11: generate s
962
         */
963
        ret = mlkem_get_noise(&key->prf, k, s, NULL, NULL, sigma);
964
    }
965
    if (ret == 0) {
966
        /* Generate key pair from private vector and seeds.
967
         * Alg 13: Steps 3-7: generate matrix A_hat
968
         * Alg 13: Steps 12-15: generate e
969
         * Alg 13: Steps 16-18: calculate t_hat from A_hat, s and e
970
         */
971
        ret = mlkem_keygen_seeds(s, t, &key->prf, e, k, rho, sigma);
972
    }
973
    if (ret == 0) {
974
#endif
975
        /* Private and public key are set/available. */
976
4.48k
        key->flags |= MLKEM_FLAG_PRIV_SET | MLKEM_FLAG_PUB_SET;
977
#ifdef WOLFSSL_MLKEM_CACHE_A
978
        key->flags |= MLKEM_FLAG_A_SET;
979
#endif
980
4.48k
    }
981
982
    /* Zeroize the secret seed material in rho||sigma (sigma) before return. */
983
4.50k
    ForceZero(buf, sizeof(buf));
984
#ifdef WOLFSSL_CHECK_MEM_ZERO
985
    wc_MemZero_Check(buf, sizeof(buf));
986
#endif
987
#ifdef WC_MLKEM_FAULT_HARDEN
988
    ForceZero(sigma, sizeof(sigma));
989
#ifdef WOLFSSL_CHECK_MEM_ZERO
990
    wc_MemZero_Check(sigma, sizeof(sigma));
991
#endif
992
#endif
993
994
4.50k
#ifndef WOLFSSL_NO_MALLOC
995
    /* Free dynamic memory allocated in function. */
996
4.50k
    if (e != NULL) {
997
        /* e holds the secret noise vector; zeroize before release. The
998
         * (public) matrix A may follow it in the same allocation but does
999
         * not need clearing. */
1000
4.49k
        ForceZero(e, (size_t)(k * MLKEM_N) * sizeof(sword16));
1001
4.49k
        XFREE(e, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1002
4.49k
    }
1003
#else
1004
    /* e is a stack buffer holding the secret noise vector; zeroize it. */
1005
#ifdef WOLFSSL_CHECK_MEM_ZERO
1006
    wc_MemZero_Add("mlkem keygen e", e, (size_t)(k * MLKEM_N) * sizeof(sword16));
1007
#endif
1008
    ForceZero(e, (size_t)(k * MLKEM_N) * sizeof(sword16));
1009
#ifdef WOLFSSL_CHECK_MEM_ZERO
1010
    wc_MemZero_Check(e, (size_t)(k * MLKEM_N) * sizeof(sword16));
1011
#endif
1012
#endif
1013
1014
/* ML-KEM, ML-DSA, SLH-DSA, LMS and XMSS were never FIPS approved before the v7
1015
 * module, so this test stays gated on v7 and must never be widened to plain
1016
 * HAVE_FIPS.  WOLFSSL_VALIDATE_MLKEM_KEYGEN opts a non-FIPS build in, off by
1017
 * default. */
1018
#if FIPS_VERSION3_GE(7,0,0) || defined(WOLFSSL_VALIDATE_MLKEM_KEYGEN)
1019
#if defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
1020
    #error "ML-KEM key generation needs encapsulate and decapsulate for the \
1021
key-pair test required by ISO/IEC 19790:2012 sec 7.10.3.3"
1022
#endif
1023
    /* Test every new key pair: encapsulate with it, decapsulate with it, and
1024
     * check the shared secrets match.  ISO/IEC 19790:2012 sec 7.10.3.3;
1025
     * FIPS 140-3 IG 10.3.A Additional Comment 1 spells this test out for
1026
     * FIPS 203.  Fixed `m` because this path takes no RNG, and a self-test
1027
     * needs a working round trip, not an unpredictable one. */
1028
    if (ret == 0) {
1029
        WC_DECLARE_VAR(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
1030
            key->heap);
1031
        byte pct_ss1[WC_ML_KEM_SS_SZ];
1032
        byte pct_ss2[WC_ML_KEM_SS_SZ];
1033
        word32 pct_ctSz = 0;
1034
        /* Fixed test pattern for the FIPS 203 Alg 17 `m` input; the value is
1035
         * arbitrary - a PCT roundtrip does not require unpredictability. */
1036
        static const byte pct_m[WC_ML_KEM_ENC_RAND_SZ] = {
1037
            0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
1038
            0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
1039
            0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
1040
            0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB
1041
        };
1042
1043
        WC_ALLOC_VAR_EX(pct_ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE,
1044
            key->heap, DYNAMIC_TYPE_TMP_BUFFER, ret = MEMORY_E);
1045
1046
        /* Zero and register the shared secrets up front so the leak checker
1047
         * covers them for the whole block. */
1048
#ifdef WOLFSSL_CHECK_MEM_ZERO
1049
        XMEMSET(pct_ss1, 0, sizeof(pct_ss1));
1050
        XMEMSET(pct_ss2, 0, sizeof(pct_ss2));
1051
        wc_MemZero_Add("mlkem pct ss1", pct_ss1, sizeof(pct_ss1));
1052
        wc_MemZero_Add("mlkem pct ss2", pct_ss2, sizeof(pct_ss2));
1053
        /* Register the ciphertext too, so an early exit added later between
1054
         * here and the ForceZero below is caught the same way. */
1055
        if (WC_VAR_OK(pct_ct))
1056
            wc_MemZero_Add("mlkem pct ct", pct_ct,
1057
                WC_ML_KEM_MAX_CIPHER_TEXT_SIZE);
1058
#endif
1059
        if (ret == 0)
1060
            ret = wc_MlKemKey_CipherTextSize(key, &pct_ctSz);
1061
1062
        if (ret == 0)
1063
            ret = wc_MlKemKey_EncapsulateWithRandom(key, pct_ct, pct_ss1,
1064
                pct_m, (int)sizeof(pct_m));
1065
1066
        if (ret == 0)
1067
            ret = wc_MlKemKey_Decapsulate(key, pct_ss2, pct_ct, pct_ctSz);
1068
1069
        if (ret == 0) {
1070
            if (XMEMCMP(pct_ss1, pct_ss2, WC_ML_KEM_SS_SZ) != 0)
1071
                ret = ML_KEM_PCT_E;
1072
        }
1073
1074
        ForceZero(pct_ss1, sizeof(pct_ss1));
1075
        ForceZero(pct_ss2, sizeof(pct_ss2));
1076
#ifdef WOLFSSL_CHECK_MEM_ZERO
1077
        wc_MemZero_Check(pct_ss1, sizeof(pct_ss1));
1078
        wc_MemZero_Check(pct_ss2, sizeof(pct_ss2));
1079
#endif
1080
        if (WC_VAR_OK(pct_ct)) {
1081
            ForceZero(pct_ct, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE);
1082
        #ifdef WOLFSSL_CHECK_MEM_ZERO
1083
            /* Must run before the free, or the registration outlives the
1084
             * allocation. */
1085
            wc_MemZero_Check(pct_ct, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE);
1086
        #endif
1087
        }
1088
1089
        WC_FREE_VAR_EX(pct_ct, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1090
1091
        /* Free a key that failed the test, so a caller ignoring the return
1092
         * value cannot use it.  ISO/IEC 19790:2012 sec 7.10.1 forbids using
1093
         * anything that failed a self-test.  MEMORY_E is excluded: it
1094
         * means the test never ran, so the key is not implicated. */
1095
        if ((ret != 0) && (ret != WC_NO_ERR_TRACE(MEMORY_E))) {
1096
            wc_MlKemKey_Free(key);
1097
        }
1098
    }
1099
#endif /* FIPS v7 or WOLFSSL_VALIDATE_MLKEM_KEYGEN */
1100
1101
4.50k
    return ret;
1102
4.50k
}
1103
#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY */
1104
1105
/******************************************************************************/
1106
1107
/**
1108
 * Get the size in bytes of cipher text for key.
1109
 *
1110
 * @param  [in]   key  ML-KEM key object.
1111
 * @param  [out]  len  Length of cipher text in bytes.
1112
 * @return  0 on success.
1113
 * @return  BAD_FUNC_ARG when key or len is NULL.
1114
 * @return  NOT_COMPILED_IN when key type is not supported.
1115
 */
1116
int wc_MlKemKey_CipherTextSize(MlKemKey* key, word32* len)
1117
1
{
1118
1
    int ret = 0;
1119
1120
    /* Validate parameters. */
1121
1
    if ((key == NULL) || (len == NULL)) {
1122
0
        ret = BAD_FUNC_ARG;
1123
0
    }
1124
1125
1
    if (ret == 0) {
1126
        /* Return in 'len' size of the cipher text for the type of this key. */
1127
1
        switch (key->type) {
1128
0
#ifndef WOLFSSL_NO_ML_KEM
1129
0
    #ifdef WOLFSSL_WC_ML_KEM_512
1130
0
        case WC_ML_KEM_512:
1131
0
            *len = WC_ML_KEM_512_CIPHER_TEXT_SIZE;
1132
0
            break;
1133
0
    #endif
1134
0
    #ifdef WOLFSSL_WC_ML_KEM_768
1135
1
        case WC_ML_KEM_768:
1136
1
            *len = WC_ML_KEM_768_CIPHER_TEXT_SIZE;
1137
1
            break;
1138
0
    #endif
1139
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
1140
0
        case WC_ML_KEM_1024:
1141
0
            *len = WC_ML_KEM_1024_CIPHER_TEXT_SIZE;
1142
0
            break;
1143
0
    #endif
1144
0
#endif
1145
#ifdef WOLFSSL_MLKEM_KYBER
1146
    #ifdef WOLFSSL_KYBER512
1147
        case KYBER512:
1148
            *len = KYBER512_CIPHER_TEXT_SIZE;
1149
            break;
1150
    #endif
1151
    #ifdef WOLFSSL_KYBER768
1152
        case KYBER768:
1153
            *len = KYBER768_CIPHER_TEXT_SIZE;
1154
            break;
1155
    #endif
1156
    #ifdef WOLFSSL_KYBER1024
1157
        case KYBER1024:
1158
            *len = KYBER1024_CIPHER_TEXT_SIZE;
1159
            break;
1160
    #endif
1161
#endif
1162
0
        default:
1163
            /* No other values supported. */
1164
0
            ret = NOT_COMPILED_IN;
1165
0
            break;
1166
1
        }
1167
1
    }
1168
1169
1
    return ret;
1170
1
}
1171
1172
/**
1173
 * Size of a shared secret in bytes. Always WC_ML_KEM_SS_SZ.
1174
 *
1175
 * @param  [in]   key  ML-KEM key object. Not used.
1176
 * @param  [out]  len  Size of the shared secret created with a ML-KEM key.
1177
 * @return  0 on success.
1178
 * @return  BAD_FUNC_ARG when len is NULL.
1179
 */
1180
int wc_MlKemKey_SharedSecretSize(MlKemKey* key, word32* len)
1181
1
{
1182
1
    int ret = 0;
1183
1184
1
    if (len == NULL) {
1185
0
        ret = BAD_FUNC_ARG;
1186
0
    }
1187
1
    else {
1188
1
        *len = WC_ML_KEM_SS_SZ;
1189
1
    }
1190
1191
1
    (void)key;
1192
1
    return ret;
1193
1
}
1194
1195
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
1196
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
1197
/* Encrypt a message to cipher text with the encryption key.
1198
 *
1199
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE, m, r)
1200
 * Uses the encryption key to encrypt a plaintext message using the randomness
1201
 * r.
1202
 *   1: N <- 0
1203
 *   2: t_hat <- ByteDecode_12(ek_PKE[0:384k])
1204
 *                                   > run ByteDecode_12 k times to decode t_hat
1205
 *   3: rho <- ek_PKE[384k : 384k + 32]
1206
 *                                            > extract 32-byte seed from ek_PKE
1207
 *   4-8: generate matrix A_hat
1208
 *   9-12: generate y
1209
 *   13-16: generate e_1
1210
 *   17: generate e_2
1211
 *   18-19: calculate u
1212
 *   20: mu <- Decompress_1(ByteDecode_1(m))
1213
 *   21: calculate v
1214
 *   22: c_1 <- ByteEncode_d_u(Compress_d_u(u))
1215
 *                                 > run ByteEncode_d_u and Compress_d_u k times
1216
 *   23: c_2 <- ByteEncode_d_v(Compress_d_v(v))
1217
 *   24: return c <- (c_1||c_2)
1218
 *
1219
 * @param  [in]  key  ML-KEM key object.
1220
 * @param  [in]  m    Random bytes.
1221
 * @param  [in]  r    Seed to feed to PRF when generating y, e1 and e2.
1222
 * @param  [out] c    Calculated cipher text.
1223
 * @return  0 on success.
1224
 * @return  NOT_COMPILED_IN when key type is not supported.
1225
 */
1226
static int mlkemkey_encapsulate(MlKemKey* key, const byte* m, byte* r, byte* c)
1227
0
{
1228
0
    int ret = 0;
1229
0
    sword16* a = NULL;
1230
0
#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM
1231
0
    sword16* mu = NULL;
1232
0
    sword16* e1 = NULL;
1233
0
    sword16* e2 = NULL;
1234
0
#endif
1235
0
    unsigned int k = 0;
1236
0
    unsigned int compVecSz = 0;
1237
0
#ifndef WOLFSSL_NO_MALLOC
1238
0
    sword16* y = NULL;
1239
0
    size_t yAllocSz = 0;
1240
#else
1241
#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM
1242
    sword16 y[((WC_ML_KEM_MAX_K + 3) * WC_ML_KEM_MAX_K + 3) * MLKEM_N];
1243
#else
1244
    sword16 y[3 * WC_ML_KEM_MAX_K * MLKEM_N];
1245
#endif
1246
#endif
1247
0
    sword16* u = 0;
1248
0
    sword16* v = 0;
1249
1250
    /* Establish parameters based on key type. */
1251
0
    switch (key->type) {
1252
0
#ifndef WOLFSSL_NO_ML_KEM
1253
0
#ifdef WOLFSSL_WC_ML_KEM_512
1254
0
    case WC_ML_KEM_512:
1255
0
        k = WC_ML_KEM_512_K;
1256
0
        compVecSz = WC_ML_KEM_512_POLY_VEC_COMPRESSED_SZ;
1257
0
        break;
1258
0
#endif
1259
0
#ifdef WOLFSSL_WC_ML_KEM_768
1260
0
    case WC_ML_KEM_768:
1261
0
        k = WC_ML_KEM_768_K;
1262
0
        compVecSz = WC_ML_KEM_768_POLY_VEC_COMPRESSED_SZ;
1263
0
        break;
1264
0
#endif
1265
0
#ifdef WOLFSSL_WC_ML_KEM_1024
1266
0
    case WC_ML_KEM_1024:
1267
0
        k = WC_ML_KEM_1024_K;
1268
0
        compVecSz = WC_ML_KEM_1024_POLY_VEC_COMPRESSED_SZ;
1269
0
        break;
1270
0
#endif
1271
0
#endif
1272
#ifdef WOLFSSL_MLKEM_KYBER
1273
#ifdef WOLFSSL_KYBER512
1274
    case KYBER512:
1275
        k = KYBER512_K;
1276
        compVecSz = KYBER512_POLY_VEC_COMPRESSED_SZ;
1277
        break;
1278
#endif
1279
#ifdef WOLFSSL_KYBER768
1280
    case KYBER768:
1281
        k = KYBER768_K;
1282
        compVecSz = KYBER768_POLY_VEC_COMPRESSED_SZ;
1283
        break;
1284
#endif
1285
#ifdef WOLFSSL_KYBER1024
1286
    case KYBER1024:
1287
        k = KYBER1024_K;
1288
        compVecSz = KYBER1024_POLY_VEC_COMPRESSED_SZ;
1289
        break;
1290
#endif
1291
#endif
1292
0
    default:
1293
        /* No other values supported. */
1294
0
        ret = NOT_COMPILED_IN;
1295
0
        break;
1296
0
    }
1297
1298
0
#ifndef WOLFSSL_NO_MALLOC
1299
0
    if (ret == 0) {
1300
        /* Allocate dynamic memory for all matrices, vectors and polynomials. */
1301
0
#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM
1302
0
        yAllocSz = ((k + 3) * k + 3) * MLKEM_N * sizeof(sword16);
1303
#else
1304
        yAllocSz = 3 * k * MLKEM_N * sizeof(sword16);
1305
#endif
1306
0
        y = (sword16*)XMALLOC(yAllocSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1307
0
        if (y == NULL) {
1308
0
            ret = MEMORY_E;
1309
0
        }
1310
0
    }
1311
0
#endif
1312
1313
0
#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM
1314
0
    if (ret == 0) {
1315
        /* Assign allocated dynamic memory to pointers.
1316
         * y (b) | a (m) | mu (p) | e1 (p) | e2 (v) | u (v) | v (p) */
1317
0
        a  = y  + MLKEM_N * k;
1318
0
        mu = a  + MLKEM_N * k * k;
1319
0
        e1 = mu + MLKEM_N;
1320
0
        e2 = e1 + MLKEM_N * k;
1321
1322
        /* Convert msg to a polynomial.
1323
         * Step 20: mu <- Decompress_1(ByteDecode_1(m)) */
1324
0
        mlkem_from_msg(mu, m);
1325
1326
        /* Initialize the PRF for use in the noise generation. */
1327
0
        mlkem_prf_init(&key->prf);
1328
        /* Generate noise using PRF.
1329
         * Steps 9-17: generate y, e_1, e_2
1330
         */
1331
0
        ret = mlkem_get_noise(&key->prf, (int)k, y, e1, e2, r);
1332
0
    }
1333
    #ifdef WOLFSSL_MLKEM_CACHE_A
1334
    if ((ret == 0) && ((key->flags & MLKEM_FLAG_A_SET) != 0)) {
1335
        unsigned int i;
1336
        /* Transpose matrix.
1337
         *   Steps 4-8: generate matrix A_hat (from original) */
1338
        for (i = 0; i < k; i++) {
1339
            unsigned int j;
1340
            for (j = 0; j < k; j++) {
1341
                XMEMCPY(&a[(i * k + j) * MLKEM_N],
1342
                        &key->a[(j * k + i) * MLKEM_N],
1343
                        MLKEM_N * 2);
1344
            }
1345
        }
1346
    }
1347
    else
1348
    #endif /* WOLFSSL_MLKEM_CACHE_A */
1349
0
    if (ret == 0) {
1350
        /* Generate the transposed matrix.
1351
         *   Step 4-8: generate matrix A_hat */
1352
0
        ret = mlkem_gen_matrix(&key->prf, a, (int)k, key->pubSeed, 1);
1353
0
    }
1354
0
    if (ret == 0) {
1355
        /* Assign remaining allocated dynamic memory to pointers.
1356
         * y (b) | a (m) | mu (p) | e1 (p) | e2 (v) | u (v) | v (p) */
1357
0
        u  = e2 + MLKEM_N;
1358
0
        v  = u  + MLKEM_N * k;
1359
1360
        /* Perform encapsulation maths.
1361
         *   Steps 18-19, 21: calculate u and v */
1362
0
        mlkem_encapsulate(key->pub, u, v, a, y, e1, e2, mu, (int)k);
1363
0
    }
1364
#else /* WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM */
1365
    if (ret == 0) {
1366
        /* Assign allocated dynamic memory to pointers.
1367
         * y (v) | a (v) | u (v) */
1368
        a = y + MLKEM_N * k;
1369
1370
        /* Initialize the PRF for use in the noise generation. */
1371
        mlkem_prf_init(&key->prf);
1372
        /* Generate noise using PRF.
1373
         * Steps 9-12: generate y */
1374
        ret = mlkem_get_noise(&key->prf, (int)k, y, NULL, NULL, r);
1375
    }
1376
    if (ret == 0) {
1377
        /* Assign remaining allocated dynamic memory to pointers.
1378
         * y (v) | at (v) | u (v) */
1379
        u  = a + MLKEM_N * k;
1380
        v  = a;
1381
1382
        /* Perform encapsulation maths.
1383
         *   Steps 13-17: generate e_1 and e_2
1384
         *   Steps 18-19, 21: calculate u and v */
1385
        ret = mlkem_encapsulate_seeds(key->pub, &key->prf, u, a, y, (int)k, m,
1386
            key->pubSeed, r);
1387
    }
1388
#endif /* WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM */
1389
1390
0
    if (ret == 0) {
1391
0
        byte* c1 = c;
1392
0
        byte* c2 = c + compVecSz;
1393
1394
0
    #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
1395
0
        if (k == WC_ML_KEM_512_K) {
1396
            /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */
1397
0
            mlkem_vec_compress_10(c1, u, k);
1398
            /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */
1399
0
            mlkem_compress_4(c2, v);
1400
            /* Step 24: return c <- (c_1||c_2) */
1401
0
        }
1402
0
    #endif
1403
0
    #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
1404
0
        if (k == WC_ML_KEM_768_K) {
1405
            /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */
1406
0
            mlkem_vec_compress_10(c1, u, k);
1407
            /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */
1408
0
            mlkem_compress_4(c2, v);
1409
            /* Step 24: return c <- (c_1||c_2) */
1410
0
        }
1411
0
    #endif
1412
0
    #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
1413
0
        if (k == WC_ML_KEM_1024_K) {
1414
            /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */
1415
0
            mlkem_vec_compress_11(c1, u);
1416
            /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */
1417
0
            mlkem_compress_5(c2, v);
1418
            /* Step 24: return c <- (c_1||c_2) */
1419
0
        }
1420
0
    #endif
1421
0
    }
1422
1423
0
#ifndef WOLFSSL_NO_MALLOC
1424
    /* Dispose of dynamic memory allocated in function. The buffer holds secret
1425
     * material: y (ephemeral noise) and, in the default layout, mu (message
1426
     * polynomial) and e1/e2 (noise vectors). Zeroize the whole allocation
1427
     * before release - FIPS 203 section 3.3. */
1428
0
    if (y != NULL) {
1429
0
        ForceZero(y, yAllocSz);
1430
0
        XFREE(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1431
0
    }
1432
#else
1433
    /* y is a stack buffer holding secret noise/message material; zeroize it. */
1434
#ifdef WOLFSSL_CHECK_MEM_ZERO
1435
    wc_MemZero_Add("mlkem encrypt y", y, sizeof(y));
1436
#endif
1437
    ForceZero(y, sizeof(y));
1438
#ifdef WOLFSSL_CHECK_MEM_ZERO
1439
    wc_MemZero_Check(y, sizeof(y));
1440
#endif
1441
#endif
1442
1443
0
    return ret;
1444
0
}
1445
#endif
1446
1447
#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \
1448
    !defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
1449
static int wc_mlkemkey_check_h(MlKemKey* key)
1450
0
{
1451
0
    int ret = 0;
1452
1453
    /* If public hash (h) is not stored against key, calculate it
1454
     * (fields set explicitly instead of using decode).
1455
     * Step 1: ... H(ek)...
1456
     */
1457
0
    if ((key->flags & MLKEM_FLAG_H_SET) == 0) {
1458
0
    #ifndef WOLFSSL_NO_MALLOC
1459
0
        byte* pubKey = NULL;
1460
0
        word32 pubKeyLen;
1461
    #else
1462
        byte pubKey[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE];
1463
        word32 pubKeyLen;
1464
    #endif
1465
1466
        /* Determine how big an encoded public key will be. */
1467
0
        ret = wc_MlKemKey_PublicKeySize(key, &pubKeyLen);
1468
0
        if (ret == 0) {
1469
0
    #ifndef WOLFSSL_NO_MALLOC
1470
            /* Allocate dynamic memory for encoded public key. */
1471
0
            pubKey = (byte*)XMALLOC(pubKeyLen, key->heap,
1472
0
                DYNAMIC_TYPE_TMP_BUFFER);
1473
0
            if (pubKey == NULL) {
1474
0
                ret = MEMORY_E;
1475
0
            }
1476
0
        }
1477
0
        if (ret == 0) {
1478
0
    #endif
1479
            /* Encode public key - h is hash of encoded public key. */
1480
0
            ret = wc_MlKemKey_EncodePublicKey(key, pubKey, pubKeyLen);
1481
0
        }
1482
0
    #ifndef WOLFSSL_NO_MALLOC
1483
        /* Dispose of encoded public key. */
1484
0
        XFREE(pubKey, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1485
0
    #endif
1486
0
    }
1487
1488
0
    return ret;
1489
0
}
1490
#endif
1491
1492
#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE
1493
/**
1494
 * Encapsulate with random number generator and derive secret.
1495
 *
1496
 * FIPS 203, Algorithm 20: ML-KEM.Encaps(ek)
1497
 * Uses the encapsulation key to generate a shared secret key and an associated
1498
 * ciphertext.
1499
 *   1: m <- B_32                                         > m is 32 random bytes
1500
 *   2: if m == NULL then
1501
 *   3:     return falsum
1502
 *   4: end if
1503
 *   5: (K,c) <- ML-KEM.Encaps_internal(ek,m)
1504
 *                                        > run internal encapsulation algorithm
1505
 *   6: return (K,c)
1506
 *
1507
 * @param  [in]   key  ML-KEM key object.
1508
 * @param  [out]  ct   Cipher text.
1509
 * @param  [out]  ss   Shared secret generated.
1510
 * @param  [in]   rng  Random number generator.
1511
 * @return  0 on success.
1512
 * @return  BAD_FUNC_ARG when key, ct, ss or rng is NULL.
1513
 * @return  BAD_STATE_E when public key not set.
1514
 * @return  NOT_COMPILED_IN when key type is not supported.
1515
 * @return  MEMORY_E when dynamic memory allocation failed.
1516
 */
1517
int wc_MlKemKey_Encapsulate(MlKemKey* key, unsigned char* ct, unsigned char* ss,
1518
    WC_RNG* rng)
1519
0
{
1520
0
#ifndef WC_NO_RNG
1521
0
    int ret = 0;
1522
0
    unsigned char m[WC_ML_KEM_ENC_RAND_SZ];
1523
0
#ifdef WOLF_CRYPTO_CB
1524
0
    word32 ctlen = 0;
1525
0
#endif
1526
1527
    /* Validate parameters. */
1528
0
    if ((key == NULL) || (ct == NULL) || (ss == NULL) || (rng == NULL)) {
1529
0
        ret = BAD_FUNC_ARG;
1530
0
    }
1531
    /* Check the public key has been set. */
1532
0
    else if ((key->flags & MLKEM_FLAG_PUB_SET) == 0) {
1533
0
        ret = BAD_STATE_E;
1534
0
    }
1535
1536
0
#ifdef WOLF_CRYPTO_CB
1537
0
    if (ret == 0) {
1538
0
        ret = wc_MlKemKey_CipherTextSize(key, &ctlen);
1539
0
    }
1540
0
#ifndef WOLF_CRYPTO_CB_FIND
1541
0
    if ((ret == 0) && (key->devId != INVALID_DEVID)) {
1542
#else
1543
    if (ret == 0) {
1544
#endif
1545
0
        ret = wc_CryptoCb_PqcEncapsulate(ct, ctlen, ss, WC_ML_KEM_SS_SZ, rng,
1546
0
            WC_PQC_KEM_TYPE_MLKEM, key);
1547
0
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
1548
0
            ret = BAD_STATE_E; /* async unsupported for KEM encaps */
1549
0
        if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
1550
0
            return ret;
1551
        /* fall-through when unavailable */
1552
0
        ret = 0;
1553
0
    }
1554
0
#endif
1555
1556
0
    if (ret == 0) {
1557
        /* Generate seed for use with PRFs.
1558
         * Step 1: m is 32 random bytes
1559
         */
1560
0
        ret = wc_RNG_GenerateBlock(rng, m, sizeof(m));
1561
        /* m now holds the encapsulation randomness (the shared secret is
1562
         * derived from it); register before the encapsulate call. */
1563
#ifdef WOLFSSL_CHECK_MEM_ZERO
1564
        wc_MemZero_Add("mlkem encapsulate m", m, sizeof(m));
1565
#endif
1566
        /* Step 2: ret is not zero when m == NULL. */
1567
0
    }
1568
0
    if (ret == 0) {
1569
        /* Encapsulate with the random.
1570
         * Step 5: run internal encapsulation algorithm
1571
         */
1572
0
        ret = wc_MlKemKey_EncapsulateWithRandom(key, ct, ss, m, sizeof(m));
1573
0
    }
1574
1575
    /* Zeroize the random message seed before return - it is the encapsulation
1576
     * randomness from which the shared secret is derived (FIPS 203 Alg 17). */
1577
0
    ForceZero(m, sizeof(m));
1578
#ifdef WOLFSSL_CHECK_MEM_ZERO
1579
    wc_MemZero_Check(m, sizeof(m));
1580
#endif
1581
1582
    /* Step 3: return ret != 0 on falsum or internal key generation failure. */
1583
0
    return ret;
1584
#else
1585
    (void)key;
1586
    (void)ct;
1587
    (void)ss;
1588
    (void)rng;
1589
    return NOT_COMPILED_IN;
1590
#endif /* WC_NO_RNG */
1591
0
}
1592
1593
/**
1594
 * Encapsulate with random data and derive secret.
1595
 *
1596
 * FIPS 203, Algorithm 17: ML-KEM.Encaps_internal(ek, m)
1597
 * Uses the encapsulation key and randomness to generate a key and an associated
1598
 * ciphertext.
1599
 *   Step 1: (K,r) <- G(m||H(ek))
1600
 *                                 > derive shared secret key K and randomness r
1601
 *   Step 2: c <- K-PKE.Encrypt(ek, m, r)
1602
 *                                     > encrypt m using K-PKE with randomness r
1603
 *   Step 3: return (K,c)
1604
 *
1605
 * @param  [in]   key   ML-KEM key object.
1606
 * @param  [out]  ct    Cipher text.
1607
 * @param  [out]  ss    Shared secret generated.
1608
 * @param  [in]   rand  Random bytes.
1609
 * @param  [in]   len   Length of random bytes.
1610
 * @return  0 on success.
1611
 * @return  BAD_FUNC_ARG when key, ct, ss or rand is NULL.
1612
 * @return  BUFFER_E when len is not WC_ML_KEM_ENC_RAND_SZ.
1613
 * @return  BAD_STATE_E when public key not set.
1614
 * @return  NOT_COMPILED_IN when key type is not supported.
1615
 * @return  MEMORY_E when dynamic memory allocation failed.
1616
 */
1617
int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct,
1618
    unsigned char* ss, const unsigned char* rand, int len)
1619
0
{
1620
#ifdef WOLFSSL_MLKEM_KYBER
1621
    byte msg[WC_ML_KEM_SYM_SZ];
1622
#endif
1623
0
    byte kr[2 * WC_ML_KEM_SYM_SZ + 1];
1624
0
    int ret = 0;
1625
#ifdef WOLFSSL_MLKEM_KYBER
1626
    unsigned int cSz = 0;
1627
#endif
1628
1629
    /* msg (Kyber only) and kr hold secret encapsulation material; baseline-zero
1630
     * and register up front (single-exit function) so any later exit before the
1631
     * ForceZero is covered. */
1632
#if defined(WOLFSSL_MLKEM_KYBER) && defined(WOLFSSL_CHECK_MEM_ZERO)
1633
    XMEMSET(msg, 0, sizeof(msg));
1634
    wc_MemZero_Add("mlkem encapsulate msg", msg, sizeof(msg));
1635
#endif
1636
#ifdef WOLFSSL_CHECK_MEM_ZERO
1637
    XMEMSET(kr, 0, sizeof(kr));
1638
    wc_MemZero_Add("mlkem encapsulate kr", kr, sizeof(kr));
1639
#endif
1640
    /* Validate parameters. */
1641
0
    if ((key == NULL) || (ct == NULL) || (ss == NULL) || (rand == NULL)) {
1642
0
        ret = BAD_FUNC_ARG;
1643
0
    }
1644
0
    if ((ret == 0) && (len != WC_ML_KEM_ENC_RAND_SZ)) {
1645
0
        ret = BUFFER_E;
1646
0
    }
1647
    /* Check the public key has been set. */
1648
0
    if ((ret == 0) && ((key->flags & MLKEM_FLAG_PUB_SET) == 0)) {
1649
0
        ret = BAD_STATE_E;
1650
0
    }
1651
1652
#ifdef WOLFSSL_MLKEM_KYBER
1653
    if (ret == 0) {
1654
        /* Establish parameters based on key type. */
1655
        switch (key->type) {
1656
#ifndef WOLFSSL_NO_ML_KEM
1657
    #ifdef WOLFSSL_WC_ML_KEM_512
1658
        case WC_ML_KEM_512:
1659
    #endif
1660
    #ifdef WOLFSSL_WC_ML_KEM_768
1661
        case WC_ML_KEM_768:
1662
    #endif
1663
    #ifdef WOLFSSL_WC_ML_KEM_1024
1664
        case WC_ML_KEM_1024:
1665
    #endif
1666
            break;
1667
#endif
1668
    #ifdef WOLFSSL_KYBER512
1669
        case KYBER512:
1670
            cSz = KYBER512_CIPHER_TEXT_SIZE;
1671
            break;
1672
    #endif
1673
    #ifdef WOLFSSL_KYBER768
1674
        case KYBER768:
1675
            cSz = KYBER768_CIPHER_TEXT_SIZE;
1676
            break;
1677
    #endif
1678
    #ifdef WOLFSSL_KYBER1024
1679
        case KYBER1024:
1680
            cSz = KYBER1024_CIPHER_TEXT_SIZE;
1681
            break;
1682
    #endif
1683
        default:
1684
            /* No other values supported. */
1685
            ret = NOT_COMPILED_IN;
1686
            break;
1687
        }
1688
    }
1689
#endif
1690
1691
0
    if (ret == 0) {
1692
0
        ret = wc_mlkemkey_check_h(key);
1693
0
    }
1694
1695
#ifdef WOLFSSL_MLKEM_KYBER
1696
    if (ret == 0) {
1697
#ifndef WOLFSSL_NO_ML_KEM
1698
        if (key->type & MLKEM_KYBER)
1699
#endif
1700
        {
1701
            /* Hash random to anonymize as seed data. */
1702
            ret = MLKEM_HASH_H(&key->hash, rand, WC_ML_KEM_SYM_SZ, msg);
1703
        }
1704
    }
1705
#endif
1706
0
    if (ret == 0) {
1707
        /* Hash message into seed buffer. */
1708
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1709
        if (key->type & MLKEM_KYBER)
1710
#endif
1711
#ifdef WOLFSSL_MLKEM_KYBER
1712
        {
1713
            ret = MLKEM_HASH_G(&key->hash, msg, WC_ML_KEM_SYM_SZ, key->h,
1714
                WC_ML_KEM_SYM_SZ, kr);
1715
        }
1716
#endif
1717
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1718
        else
1719
#endif
1720
0
#ifndef WOLFSSL_NO_ML_KEM
1721
0
        {
1722
            /* Step 1: (K,r) <- G(m||H(ek)) */
1723
0
            ret = MLKEM_HASH_G(&key->hash, rand, WC_ML_KEM_SYM_SZ, key->h,
1724
0
                WC_ML_KEM_SYM_SZ, kr);
1725
0
        }
1726
0
#endif
1727
0
    }
1728
1729
0
    if (ret == 0) {
1730
        /* Encapsulate the message using the key and the seed. */
1731
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1732
        if (key->type & MLKEM_KYBER)
1733
#endif
1734
#ifdef WOLFSSL_MLKEM_KYBER
1735
        {
1736
            ret = mlkemkey_encapsulate(key, msg, kr + WC_ML_KEM_SYM_SZ, ct);
1737
        }
1738
#endif
1739
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1740
        else
1741
#endif
1742
0
#ifndef WOLFSSL_NO_ML_KEM
1743
0
        {
1744
            /* Step 2: c <- K-PKE.Encrypt(ek,m,r) */
1745
0
            ret = mlkemkey_encapsulate(key, rand, kr + WC_ML_KEM_SYM_SZ, ct);
1746
0
        }
1747
0
#endif
1748
0
    }
1749
1750
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1751
    if (key->type & MLKEM_KYBER)
1752
#endif
1753
#ifdef WOLFSSL_MLKEM_KYBER
1754
    {
1755
        if (ret == 0) {
1756
            /* Hash the cipher text after the seed. */
1757
            ret = MLKEM_HASH_H(&key->hash, ct, cSz, kr + WC_ML_KEM_SYM_SZ);
1758
        }
1759
        if (ret == 0) {
1760
            /* Derive the secret from the seed and hash of cipher text. */
1761
            ret = MLKEM_KDF(kr, 2 * WC_ML_KEM_SYM_SZ, ss, WC_ML_KEM_SS_SZ);
1762
        }
1763
    }
1764
#endif
1765
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
1766
    else
1767
#endif
1768
0
#ifndef WOLFSSL_NO_ML_KEM
1769
0
    {
1770
0
        if (ret == 0) {
1771
            /* return (K,c) */
1772
0
            XMEMCPY(ss, kr, WC_ML_KEM_SS_SZ);
1773
0
        }
1774
0
    }
1775
0
#endif
1776
1777
#ifdef WOLFSSL_MLKEM_KYBER
1778
    /* msg holds the secret message H(rand) used for Kyber encapsulation;
1779
     * zeroize it before return (the ML-KEM path uses the caller's rand). */
1780
    ForceZero(msg, sizeof(msg));
1781
#ifdef WOLFSSL_CHECK_MEM_ZERO
1782
    wc_MemZero_Check(msg, sizeof(msg));
1783
#endif
1784
#endif
1785
0
    ForceZero(kr, sizeof(kr));
1786
#ifdef WOLFSSL_CHECK_MEM_ZERO
1787
    wc_MemZero_Check(kr, sizeof(kr));
1788
#endif
1789
1790
0
    return ret;
1791
0
}
1792
#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE */
1793
1794
/******************************************************************************/
1795
1796
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
1797
/* Decapsulate cipher text to the message using key.
1798
 *
1799
 * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c)
1800
 * Uses the decryption key to decrypt a ciphertext.
1801
 *   1: c1 <- c[0 : 32.d_u.k]
1802
 *   2: c2 <- c[32.d_u.k : 32(d_u.k + d_v)]
1803
 *   3: u' <- Decompress_d_u(ByteDecode_d_u(c1))
1804
 *   4: v' <- Decompress_d_v(ByteDecode_d_v(c2))
1805
 *   ...
1806
 *   6: w <- v' - InvNTT(s_hat_trans o NTT(u'))
1807
 *   7: m <- ByteEncode_1(Compress_1(w))
1808
 *   8: return m
1809
 *
1810
 * @param  [in]   key  ML-KEM key object.
1811
 * @param  [out]  m    Message that was encapsulated.
1812
 * @param  [in]   c    Cipher text.
1813
 * @return  0 on success.
1814
 * @return  NOT_COMPILED_IN when key type is not supported.
1815
 * @return  MEMORY_E when dynamic memory allocation failed.
1816
 */
1817
static MLKEM_NOINLINE int mlkemkey_decapsulate(MlKemKey* key, byte* m,
1818
    const byte* c)
1819
0
{
1820
0
    int ret = 0;
1821
0
    sword16* v;
1822
0
    sword16* w;
1823
0
    unsigned int k = 0;
1824
0
    unsigned int compVecSz = 0;
1825
0
#if defined(WOLFSSL_SMALL_STACK) || \
1826
0
    (!defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC))
1827
0
    sword16* u = NULL;
1828
0
    size_t uAllocSz = 0;
1829
#else
1830
    sword16 u[(WC_ML_KEM_MAX_K + 1) * MLKEM_N];
1831
#endif
1832
1833
    /* Establish parameters based on key type. */
1834
0
    switch (key->type) {
1835
0
#ifndef WOLFSSL_NO_ML_KEM
1836
0
#ifdef WOLFSSL_WC_ML_KEM_512
1837
0
    case WC_ML_KEM_512:
1838
0
        k = WC_ML_KEM_512_K;
1839
0
        compVecSz = WC_ML_KEM_512_POLY_VEC_COMPRESSED_SZ;
1840
0
        break;
1841
0
#endif
1842
0
#ifdef WOLFSSL_WC_ML_KEM_768
1843
0
    case WC_ML_KEM_768:
1844
0
        k = WC_ML_KEM_768_K;
1845
0
        compVecSz = WC_ML_KEM_768_POLY_VEC_COMPRESSED_SZ;
1846
0
        break;
1847
0
#endif
1848
0
#ifdef WOLFSSL_WC_ML_KEM_1024
1849
0
    case WC_ML_KEM_1024:
1850
0
        k = WC_ML_KEM_1024_K;
1851
0
        compVecSz = WC_ML_KEM_1024_POLY_VEC_COMPRESSED_SZ;
1852
0
        break;
1853
0
#endif
1854
0
#endif
1855
#ifdef WOLFSSL_MLKEM_KYBER
1856
#ifdef WOLFSSL_KYBER512
1857
    case KYBER512:
1858
        k = KYBER512_K;
1859
        compVecSz = KYBER512_POLY_VEC_COMPRESSED_SZ;
1860
        break;
1861
#endif
1862
#ifdef WOLFSSL_KYBER768
1863
    case KYBER768:
1864
        k = KYBER768_K;
1865
        compVecSz = KYBER768_POLY_VEC_COMPRESSED_SZ;
1866
        break;
1867
#endif
1868
#ifdef WOLFSSL_KYBER1024
1869
    case KYBER1024:
1870
        k = KYBER1024_K;
1871
        compVecSz = KYBER1024_POLY_VEC_COMPRESSED_SZ;
1872
        break;
1873
#endif
1874
#endif
1875
0
    default:
1876
        /* No other values supported. */
1877
0
        ret = NOT_COMPILED_IN;
1878
0
        break;
1879
0
    }
1880
1881
0
#if defined(WOLFSSL_SMALL_STACK) || \
1882
0
    (!defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC))
1883
0
    if (ret == 0) {
1884
        /* Allocate dynamic memory for a vector and a polynomial. */
1885
0
        uAllocSz = (k + 1) * MLKEM_N * sizeof(sword16);
1886
0
        u = (sword16*)XMALLOC(uAllocSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1887
0
        if (u == NULL) {
1888
0
            ret = MEMORY_E;
1889
0
        }
1890
0
    }
1891
0
#endif
1892
0
    if (ret == 0) {
1893
        /* Step 1: c1 <- c[0 : 32.d_u.k] */
1894
0
        const byte* c1 = c;
1895
        /* Step 2: c2 <- c[32.d_u.k : 32(d_u.k + d_v)] */
1896
0
        const byte* c2 = c + compVecSz;
1897
1898
        /* Assign allocated dynamic memory to pointers.
1899
         * u (v) | v (p) */
1900
0
        v = u + k * MLKEM_N;
1901
0
        w = u;
1902
1903
0
    #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512)
1904
0
        if (k == WC_ML_KEM_512_K) {
1905
            /* Step 3: u' <- Decompress_d_u(ByteDecode_d_u(c1)) */
1906
0
            mlkem_vec_decompress_10(u, c1, k);
1907
            /* Step 4: v' <- Decompress_d_v(ByteDecode_d_v(c2)) */
1908
0
            mlkem_decompress_4(v, c2);
1909
0
        }
1910
0
    #endif
1911
0
    #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768)
1912
0
        if (k == WC_ML_KEM_768_K) {
1913
            /* Step 3: u' <- Decompress_d_u(ByteDecode_d_u(c1)) */
1914
0
            mlkem_vec_decompress_10(u, c1, k);
1915
            /* Step 4: v' <- Decompress_d_v(ByteDecode_d_v(c2)) */
1916
0
            mlkem_decompress_4(v, c2);
1917
0
        }
1918
0
    #endif
1919
0
    #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024)
1920
0
        if (k == WC_ML_KEM_1024_K) {
1921
            /* Step 3: u' <- Decompress_d_u(ByteDecode_d_u(c1)) */
1922
0
            mlkem_vec_decompress_11(u, c1);
1923
            /* Step 4: v' <- Decompress_d_v(ByteDecode_d_v(c2)) */
1924
0
            mlkem_decompress_5(v, c2);
1925
0
        }
1926
0
    #endif
1927
1928
        /* Decapsulate the cipher text into polynomial.
1929
         * Step 6: w <- v' - InvNTT(s_hat_trans o NTT(u')) */
1930
0
        mlkem_decapsulate(key->priv, w, u, v, (int)k);
1931
1932
        /* Convert the polynomial into a array of bytes (message).
1933
         * Step 7: m <- ByteEncode_1(Compress_1(w)) */
1934
0
        mlkem_to_msg(m, w);
1935
        /* Step 8: return m */
1936
0
    }
1937
1938
0
#if defined(WOLFSSL_SMALL_STACK) || \
1939
0
    (!defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC))
1940
    /* Dispose of dynamic memory allocated in function. u (aliased as w) holds
1941
     * the secret decrypted polynomial w = v' - InvNTT(s_hat^T o NTT(u')) from
1942
     * K-PKE.Decrypt; zeroize the whole buffer before release - FIPS 203
1943
     * section 3.3. */
1944
0
    if (u != NULL) {
1945
0
        ForceZero(u, uAllocSz);
1946
0
        XFREE(u, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
1947
0
    }
1948
#else
1949
    /* u is a stack buffer holding the secret decrypted polynomial; zeroize. */
1950
#ifdef WOLFSSL_CHECK_MEM_ZERO
1951
    wc_MemZero_Add("mlkem decrypt u", u, sizeof(u));
1952
#endif
1953
    ForceZero(u, sizeof(u));
1954
#ifdef WOLFSSL_CHECK_MEM_ZERO
1955
    wc_MemZero_Check(u, sizeof(u));
1956
#endif
1957
#endif
1958
1959
0
    return ret;
1960
0
}
1961
1962
/**
1963
 * Decapsulate the cipher text to calculate the shared secret.
1964
 *
1965
 * Validates the cipher text by encapsulating and comparing with data passed in.
1966
 *
1967
 * FIPS 203, Algorithm 21: ML-KEM.Decaps(dk, c)
1968
 * Uses the decapsulation key to produce a shared secret key from a ciphertext.
1969
 *   1: K' <- ML-KEM.Decaps_internal(dk,c)
1970
 *                                        > run internal decapsulation algorithm
1971
 *   2: return K'
1972
 *
1973
 * FIPS 203, Algorithm 18: ML-KEM.Decaps_internal(dk, c)
1974
 * Uses the decapsulation key to produce a shared secret key from a ciphertext.
1975
 *   ...
1976
 *   1: dk_PKE <- dk[0 : 384k]
1977
 *                        > extract (from KEM decaps key) the PKE decryption key
1978
 *   2: ek_PKE <- dk[384k : 768k + 32]
1979
 *                                                  > extract PKE encryption key
1980
 *   3: h <- dk[768k + 32 : 768k + 64]
1981
 *                                          > extract hash of PKE encryption key
1982
 *   4: z <- dk[768k + 64 : 768k + 96]
1983
 *                                            > extract implicit rejection value
1984
 *   5: m' <- K-PKE.Decrypt(dk_PKE, c)                      > decrypt ciphertext
1985
 *   6: (K', r') <- G(m'||h)
1986
 *   7: K_bar <- J(z||c)
1987
 *   8: c' <- K-PKE.Encrypt(ek_PKE, m', r')
1988
 *                                  > re-encrypt using the derived randomness r'
1989
 *   9: if c != c' then
1990
 *  10:      K' <- K_bar
1991
 *                            > if ciphertexts do not match, "implicitly reject"
1992
 *  11: end if
1993
 *  12: return K'
1994
 *
1995
 * @param  [in]   key  ML-KEM key object.
1996
 * @param  [out]  ss   Shared secret.
1997
 * @param  [in]   ct   Cipher text.
1998
 * @param  [in]   len  Length of cipher text.
1999
 * @return  0 on success.
2000
 * @return  BAD_FUNC_ARG when key, ss or ct are NULL.
2001
 * @return  BAD_STATE_E when private key is not set.
2002
 * @return  NOT_COMPILED_IN when key type is not supported.
2003
 * @return  BUFFER_E when len is not the length of cipher text for the key type.
2004
 * @return  MEMORY_E when dynamic memory allocation failed.
2005
 */
2006
int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss,
2007
    const unsigned char* ct, word32 len)
2008
0
{
2009
0
    byte msg[WC_ML_KEM_SYM_SZ];
2010
0
    byte kr[2 * WC_ML_KEM_SYM_SZ + 1];
2011
0
    int ret = 0;
2012
0
    unsigned int ctSz = 0;
2013
0
    unsigned int i = 0;
2014
0
    int fail = 0;
2015
0
#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC)
2016
0
    byte* cmp = NULL;
2017
#else
2018
    byte cmp[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE];
2019
#endif
2020
2021
    /* Validate parameters. */
2022
0
    if ((key == NULL) || (ss == NULL) || (ct == NULL)) {
2023
0
        ret = BAD_FUNC_ARG;
2024
0
    }
2025
0
    if ((ret == 0) && ((key->flags & MLKEM_FLAG_PRIV_SET) == 0)) {
2026
0
        ret = BAD_STATE_E;
2027
0
    }
2028
2029
0
    if (ret == 0) {
2030
        /* Establish cipher text size based on key type. */
2031
0
        switch (key->type) {
2032
0
#ifndef WOLFSSL_NO_ML_KEM
2033
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2034
0
        case WC_ML_KEM_512:
2035
0
            ctSz = WC_ML_KEM_512_CIPHER_TEXT_SIZE;
2036
0
            break;
2037
0
    #endif
2038
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2039
0
        case WC_ML_KEM_768:
2040
0
            ctSz = WC_ML_KEM_768_CIPHER_TEXT_SIZE;
2041
0
            break;
2042
0
    #endif
2043
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2044
0
        case WC_ML_KEM_1024:
2045
0
            ctSz = WC_ML_KEM_1024_CIPHER_TEXT_SIZE;
2046
0
            break;
2047
0
    #endif
2048
0
#endif
2049
#ifdef WOLFSSL_MLKEM_KYBER
2050
    #ifdef WOLFSSL_KYBER512
2051
        case KYBER512:
2052
            ctSz = KYBER512_CIPHER_TEXT_SIZE;
2053
            break;
2054
    #endif
2055
    #ifdef WOLFSSL_KYBER768
2056
        case KYBER768:
2057
            ctSz = KYBER768_CIPHER_TEXT_SIZE;
2058
            break;
2059
    #endif
2060
    #ifdef WOLFSSL_KYBER1024
2061
        case KYBER1024:
2062
            ctSz = KYBER1024_CIPHER_TEXT_SIZE;
2063
            break;
2064
    #endif
2065
#endif
2066
0
        default:
2067
            /* No other values supported. */
2068
0
            ret = NOT_COMPILED_IN;
2069
0
            break;
2070
0
        }
2071
0
    }
2072
2073
    /* Ensure the cipher text passed in is the correct size. */
2074
0
    if ((ret == 0) && (len != ctSz)) {
2075
0
        ret = BUFFER_E;
2076
0
    }
2077
2078
0
#ifdef WOLF_CRYPTO_CB
2079
0
#ifndef WOLF_CRYPTO_CB_FIND
2080
0
    if ((ret == 0) && (key->devId != INVALID_DEVID)) {
2081
#else
2082
    if (ret == 0) {
2083
#endif
2084
0
        ret = wc_CryptoCb_PqcDecapsulate(ct, ctSz, ss, WC_ML_KEM_SS_SZ,
2085
0
            WC_PQC_KEM_TYPE_MLKEM, key);
2086
0
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
2087
0
            ret = BAD_STATE_E; /* async unsupported for KEM decaps */
2088
0
        if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
2089
0
            return ret;
2090
        /* fall-through when unavailable */
2091
0
        ret = 0;
2092
0
    }
2093
0
#endif
2094
2095
0
#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC)
2096
0
    if (ret == 0) {
2097
        /* Allocate memory for cipher text that is generated. */
2098
0
        cmp = (byte*)XMALLOC(ctSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
2099
0
        if (cmp == NULL) {
2100
0
            ret = MEMORY_E;
2101
0
        }
2102
0
    }
2103
0
#endif
2104
2105
    /* msg and kr hold secret decapsulation material; baseline-zero and register
2106
     * them here (below the crypto-callback early return) so any later exit
2107
     * before the ForceZero is covered. */
2108
#ifdef WOLFSSL_CHECK_MEM_ZERO
2109
    XMEMSET(msg, 0, sizeof(msg));
2110
    XMEMSET(kr, 0, sizeof(kr));
2111
    wc_MemZero_Add("mlkem decapsulate msg", msg, sizeof(msg));
2112
    wc_MemZero_Add("mlkem decapsulate kr", kr, sizeof(kr));
2113
#endif
2114
0
    if (ret == 0) {
2115
        /* Decapsulate the cipher text. */
2116
0
        ret = mlkemkey_decapsulate(key, msg, ct);
2117
0
    }
2118
0
    if (ret == 0) {
2119
        /* Check we have H, hash of public, set. */
2120
0
        ret = wc_mlkemkey_check_h(key);
2121
0
    }
2122
0
    if (ret == 0) {
2123
        /* Hash message into seed buffer. */
2124
0
        ret = MLKEM_HASH_G(&key->hash, msg, WC_ML_KEM_SYM_SZ, key->h,
2125
0
            WC_ML_KEM_SYM_SZ, kr);
2126
0
    }
2127
0
    if (ret == 0) {
2128
        /* Encapsulate the message. */
2129
0
        ret = mlkemkey_encapsulate(key, msg, kr + WC_ML_KEM_SYM_SZ, cmp);
2130
0
    }
2131
0
    if (ret == 0) {
2132
        /* Compare generated cipher text with that passed in. */
2133
0
        fail = mlkem_cmp(ct, cmp, (int)ctSz);
2134
2135
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
2136
        if (key->type & MLKEM_KYBER)
2137
#endif
2138
#ifdef WOLFSSL_MLKEM_KYBER
2139
        {
2140
            /* Hash the cipher text after the seed. */
2141
            ret = MLKEM_HASH_H(&key->hash, ct, ctSz, kr + WC_ML_KEM_SYM_SZ);
2142
            if (ret == 0) {
2143
                /* Change seed to z on comparison failure. */
2144
                for (i = 0; i < WC_ML_KEM_SYM_SZ; i++) {
2145
                    kr[i] ^= (kr[i] ^ key->z[i]) & fail;
2146
                }
2147
2148
                /* Derive the secret from the seed and hash of cipher text. */
2149
                ret = MLKEM_KDF(kr, 2 * WC_ML_KEM_SYM_SZ, ss, WC_ML_KEM_SS_SZ);
2150
            }
2151
        }
2152
#endif
2153
#if defined(WOLFSSL_MLKEM_KYBER) && !defined(WOLFSSL_NO_ML_KEM)
2154
        else
2155
#endif
2156
0
#ifndef WOLFSSL_NO_ML_KEM
2157
0
        {
2158
0
            ret = mlkem_derive_secret(&key->prf, key->z, ct, ctSz, msg);
2159
0
            if (ret == 0) {
2160
               /* Set secret to kr or fake secret on comparison failure. */
2161
0
               for (i = 0; i < WC_ML_KEM_SYM_SZ; i++) {
2162
0
                   ss[i] = (byte)(kr[i] ^ ((kr[i] ^ msg[i]) & fail));
2163
0
               }
2164
0
            }
2165
0
        }
2166
0
#endif
2167
0
    }
2168
2169
0
#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC)
2170
    /* Dispose of dynamic memory allocated in function. cmp holds the
2171
     * re-encrypted ciphertext computed from the secret decrypted message;
2172
     * zeroize before release - FIPS 203 section 3.3 (consistent with the PCT
2173
     * ciphertext handling in wc_MlKemKey_MakeKey). */
2174
0
    if (cmp != NULL) {
2175
0
        ForceZero(cmp, ctSz);
2176
0
        XFREE(cmp, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
2177
0
    }
2178
#else
2179
    /* cmp is a stack buffer holding the re-encrypted ciphertext; zeroize it. */
2180
    ForceZero(cmp, sizeof(cmp));
2181
#endif
2182
2183
0
    ForceZero(msg, sizeof(msg));
2184
0
    ForceZero(kr, sizeof(kr));
2185
#ifdef WOLFSSL_CHECK_MEM_ZERO
2186
    wc_MemZero_Check(msg, sizeof(msg));
2187
    wc_MemZero_Check(kr, sizeof(kr));
2188
#endif
2189
2190
0
    return ret;
2191
0
}
2192
#endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */
2193
2194
/******************************************************************************/
2195
2196
/**
2197
 * Get the public key and public seed from bytes.
2198
 *
2199
 * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE, m, r)
2200
 *   ...
2201
 *   2: t <- ByteDecode_12(ek_PKE[0 : 384k])
2202
 *   3: rho <- ek_PKE[384k :  384k + 32]
2203
 *   ...
2204
 *
2205
 * @param [out] pub      Public key - vector.
2206
 * @param [out] pubSeed  Public seed.
2207
 * @param [in]  p        Public key data.
2208
 * @param [in]  k        Number of polynomials in vector.
2209
 */
2210
static void mlkemkey_decode_public(sword16* pub, byte* pubSeed, const byte* p,
2211
    unsigned int k)
2212
0
{
2213
0
    unsigned int i;
2214
2215
    /* Decode public key that is vector of polynomials.
2216
     * Step 2: t <- ByteDecode_12(ek_PKE[0 : 384k]) */
2217
0
    mlkem_from_bytes(pub, p, (int)k);
2218
0
    p += k * WC_ML_KEM_POLY_SIZE;
2219
2220
    /* Read public key seed.
2221
     * Step 3: rho <- ek_PKE[384k :  384k + 32] */
2222
0
    for (i = 0; i < WC_ML_KEM_SYM_SZ; i++) {
2223
0
        pubSeed[i] = p[i];
2224
0
    }
2225
0
}
2226
2227
/**
2228
 * Decode the private key.
2229
 *
2230
 * Private Vector | Public Key | Public Hash | Randomizer
2231
 *
2232
 * FIPS 203, Algorithm 18: ML-KEM.Decaps_internal(dk, c)
2233
 *   1: dk_PKE <- dk[0 : 384k]
2234
 *                        > extract (from KEM decaps key) the PKE decryption key
2235
 *   2: ek_PKE <- dk[384k : 768k + 32]
2236
 *                                                  > extract PKE encryption key
2237
 *   3: h <- dk[768k + 32 : 768k + 64]
2238
 *                                          > extract hash of PKE encryption key
2239
 *   4: z <- dk[768k + 64 : 768k + 96]
2240
 *                                            > extract implicit rejection value
2241
 *
2242
 * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE, c)
2243
 *   ...
2244
 *   5: s_hat <- ByteDecode_12(dk_PKE)
2245
 *   ...
2246
 *
2247
 * @param  [in, out]  key  ML-KEM key object.
2248
 * @param  [in]       in   Buffer holding encoded key.
2249
 * @param  [in]       len  Length of data in buffer.
2250
 * @return  0 on success.
2251
 * @return  BAD_FUNC_ARG when key or in is NULL.
2252
 * @return  NOT_COMPILED_IN when key type is not supported.
2253
 * @return  BUFFER_E when len is not the correct size.
2254
 * @return  PUBLIC_KEY_E when the private or public vector has a coefficient
2255
 *          that is not reduced modulo q, or public key data doesn't match
2256
 *          parameters.
2257
 * @return  MLKEM_PUB_HASH_E when public key hash doesn't match stored hash.
2258
 * @return  MEMORY_E when dynamic memory allocation failed.
2259
 */
2260
int wc_MlKemKey_DecodePrivateKey(MlKemKey* key, const unsigned char* in,
2261
    word32 len)
2262
0
{
2263
0
    int ret = 0;
2264
0
    word32 privLen = 0;
2265
0
    word32 pubLen = 0;
2266
0
    unsigned int k = 0;
2267
0
    const unsigned char* p = in;
2268
2269
    /* Validate parameters. */
2270
0
    if ((key == NULL) || (in == NULL)) {
2271
0
        ret = BAD_FUNC_ARG;
2272
0
    }
2273
2274
0
    if (ret == 0) {
2275
        /* Establish parameters based on key type. */
2276
0
        switch (key->type) {
2277
0
#ifndef WOLFSSL_NO_ML_KEM
2278
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2279
0
        case WC_ML_KEM_512:
2280
0
            k = WC_ML_KEM_512_K;
2281
0
            privLen = WC_ML_KEM_512_PRIVATE_KEY_SIZE;
2282
0
            pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE;
2283
0
            break;
2284
0
    #endif
2285
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2286
0
        case WC_ML_KEM_768:
2287
0
            k = WC_ML_KEM_768_K;
2288
0
            privLen = WC_ML_KEM_768_PRIVATE_KEY_SIZE;
2289
0
            pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE;
2290
0
            break;
2291
0
    #endif
2292
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2293
0
        case WC_ML_KEM_1024:
2294
0
            k = WC_ML_KEM_1024_K;
2295
0
            privLen = WC_ML_KEM_1024_PRIVATE_KEY_SIZE;
2296
0
            pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE;
2297
0
            break;
2298
0
    #endif
2299
0
#endif
2300
#ifdef WOLFSSL_MLKEM_KYBER
2301
    #ifdef WOLFSSL_KYBER512
2302
        case KYBER512:
2303
            k = KYBER512_K;
2304
            privLen = KYBER512_PRIVATE_KEY_SIZE;
2305
            pubLen = KYBER512_PUBLIC_KEY_SIZE;
2306
            break;
2307
    #endif
2308
    #ifdef WOLFSSL_KYBER768
2309
        case KYBER768:
2310
            k = KYBER768_K;
2311
            privLen = KYBER768_PRIVATE_KEY_SIZE;
2312
            pubLen = KYBER768_PUBLIC_KEY_SIZE;
2313
            break;
2314
    #endif
2315
    #ifdef WOLFSSL_KYBER1024
2316
        case KYBER1024:
2317
            k = KYBER1024_K;
2318
            privLen = KYBER1024_PRIVATE_KEY_SIZE;
2319
            pubLen = KYBER1024_PUBLIC_KEY_SIZE;
2320
            break;
2321
    #endif
2322
#endif
2323
0
        default:
2324
            /* No other values supported. */
2325
0
            ret = NOT_COMPILED_IN;
2326
0
            break;
2327
0
        }
2328
0
    }
2329
    /* Ensure the data is the correct length for the key type. */
2330
0
    if ((ret == 0) && (len != privLen)) {
2331
0
        ret = BUFFER_E;
2332
0
    }
2333
2334
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
2335
    if (ret == 0) {
2336
        ret = mlkemkey_alloc_priv(key, k);
2337
    }
2338
    if (ret == 0) {
2339
        ret = mlkemkey_alloc_pub(key, k);
2340
    }
2341
#endif
2342
0
    if (ret == 0) {
2343
        /* Clear the key-set flags first so any failure below (size, reduction
2344
         * check, or hash) leaves a reused key object consistently unusable
2345
         * rather than flagged-set with zeroed material. */
2346
0
        key->flags &= ~(MLKEM_FLAG_BOTH_SET | MLKEM_FLAG_H_SET);
2347
2348
        /* Decode private key that is vector of polynomials.
2349
         * Alg 18 Step 1: dk_PKE <- dk[0 : 384k]
2350
         * Alg 15 Step 5: s_hat <- ByteDecode_12(dk_PKE) */
2351
0
        mlkem_from_bytes(key->priv, p, (int)k);
2352
0
        p += k * WC_ML_KEM_POLY_SIZE;
2353
2354
        /* Both vectors must decode to coefficients reduced modulo q. */
2355
0
        ret = mlkem_check_reduced(key->priv, (int)k);
2356
0
        if (ret == 0) {
2357
            /* Decode the public key that is after the private key. */
2358
0
            mlkemkey_decode_public(key->pub, key->pubSeed, p, k);
2359
0
            ret = mlkem_check_reduced(key->pub, (int)k);
2360
0
        }
2361
0
        if (ret != 0) {
2362
0
            ForceZero(key->priv, k * MLKEM_N * sizeof(sword16));
2363
0
        }
2364
0
    }
2365
0
    if (ret == 0) {
2366
        /* Compute the hash of the public key. */
2367
0
        ret = MLKEM_HASH_H(&key->hash, p, pubLen, key->h);
2368
0
        if (ret != 0) {
2369
0
            ForceZero(key->priv, k * MLKEM_N * sizeof(sword16));
2370
0
        }
2371
0
    }
2372
2373
0
    if (ret == 0) {
2374
0
        p += pubLen;
2375
        /* Compare computed public key hash with stored hash */
2376
0
        if (XMEMCMP(key->h, p, WC_ML_KEM_SYM_SZ) != 0) {
2377
0
            ForceZero(key->priv, k * MLKEM_N * sizeof(sword16));
2378
0
            ret = MLKEM_PUB_HASH_E;
2379
0
        }
2380
0
    }
2381
2382
0
    if (ret == 0) {
2383
        /* Copy the hash of the encoded public key that is after public key. */
2384
0
        XMEMCPY(key->h, p, sizeof(key->h));
2385
0
        p += WC_ML_KEM_SYM_SZ;
2386
        /* Copy the z (randomizer) that is after hash. */
2387
0
        XMEMCPY(key->z, p, sizeof(key->z));
2388
2389
        /* Set flags */
2390
0
        key->flags |= MLKEM_FLAG_H_SET | MLKEM_FLAG_BOTH_SET;
2391
0
    }
2392
2393
0
    return ret;
2394
0
}
2395
2396
/**
2397
 * Decode public key.
2398
 *
2399
 * Public vector | Public Seed
2400
 *
2401
 * @param  [in, out]  key  ML-KEM key object.
2402
 * @param  [in]       in   Buffer holding encoded key.
2403
 * @param  [in]       len  Length of data in buffer.
2404
 * @return  0 on success.
2405
 * @return  BAD_FUNC_ARG when key or in is NULL.
2406
 * @return  NOT_COMPILED_IN when key type is not supported.
2407
 * @return  BUFFER_E when len is not the correct size.
2408
 * @return  PUBLIC_KEY_E when public key data doesn't match parameters.
2409
 * @return  MEMORY_E when dynamic memory allocation failed.
2410
 */
2411
int wc_MlKemKey_DecodePublicKey(MlKemKey* key, const unsigned char* in,
2412
    word32 len)
2413
0
{
2414
0
    int ret = 0;
2415
0
    word32 pubLen = 0;
2416
0
    unsigned int k = 0;
2417
0
    const unsigned char* p = in;
2418
2419
0
    if ((key == NULL) || (in == NULL)) {
2420
0
        ret = BAD_FUNC_ARG;
2421
0
    }
2422
2423
0
    if (ret == 0) {
2424
        /* Establish parameters based on key type. */
2425
0
        switch (key->type) {
2426
0
#ifndef WOLFSSL_NO_ML_KEM
2427
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2428
0
        case WC_ML_KEM_512:
2429
0
            k = WC_ML_KEM_512_K;
2430
0
            pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE;
2431
0
            break;
2432
0
    #endif
2433
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2434
0
        case WC_ML_KEM_768:
2435
0
            k = WC_ML_KEM_768_K;
2436
0
            pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE;
2437
0
            break;
2438
0
    #endif
2439
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2440
0
        case WC_ML_KEM_1024:
2441
0
            k = WC_ML_KEM_1024_K;
2442
0
            pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE;
2443
0
            break;
2444
0
    #endif
2445
0
#endif
2446
#ifdef WOLFSSL_MLKEM_KYBER
2447
    #ifdef WOLFSSL_KYBER512
2448
        case KYBER512:
2449
            k = KYBER512_K;
2450
            pubLen = KYBER512_PUBLIC_KEY_SIZE;
2451
            break;
2452
    #endif
2453
    #ifdef WOLFSSL_KYBER768
2454
        case KYBER768:
2455
            k = KYBER768_K;
2456
            pubLen = KYBER768_PUBLIC_KEY_SIZE;
2457
            break;
2458
    #endif
2459
    #ifdef WOLFSSL_KYBER1024
2460
        case KYBER1024:
2461
            k = KYBER1024_K;
2462
            pubLen = KYBER1024_PUBLIC_KEY_SIZE;
2463
            break;
2464
    #endif
2465
#endif
2466
0
        default:
2467
            /* No other values supported. */
2468
0
            ret = NOT_COMPILED_IN;
2469
0
            break;
2470
0
        }
2471
0
    }
2472
    /* Ensure the data is the correct length for the key type. */
2473
0
    if ((ret == 0) && (len != pubLen)) {
2474
0
        ret = BUFFER_E;
2475
0
    }
2476
2477
#ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS
2478
    if (ret == 0) {
2479
        ret = mlkemkey_alloc_pub(key, k);
2480
    }
2481
#endif
2482
0
    if (ret == 0) {
2483
        /* Decode public key and check public key matches parameters. */
2484
0
        mlkemkey_decode_public(key->pub, key->pubSeed, p, k);
2485
0
        ret = mlkem_check_reduced(key->pub, (int)k);
2486
0
    }
2487
0
    if (ret == 0) {
2488
        /* Calculate public hash. */
2489
0
        ret = MLKEM_HASH_H(&key->hash, in, len, key->h);
2490
0
    }
2491
0
    if (ret == 0) {
2492
        /* Record public key and public hash set. */
2493
0
        key->flags |= MLKEM_FLAG_PUB_SET | MLKEM_FLAG_H_SET;
2494
0
    }
2495
2496
0
    return ret;
2497
0
}
2498
2499
/**
2500
 * Get the size in bytes of encoded private key for the key.
2501
 *
2502
 * @param  [in]   key  ML-KEM key object.
2503
 * @param  [out]  len  Length of encoded private key in bytes.
2504
 * @return  0 on success.
2505
 * @return  BAD_FUNC_ARG when key or len is NULL.
2506
 * @return  NOT_COMPILED_IN when key type is not supported.
2507
 */
2508
int wc_MlKemKey_PrivateKeySize(MlKemKey* key, word32* len)
2509
4.50k
{
2510
4.50k
    int ret = 0;
2511
2512
    /* Validate parameters. */
2513
4.50k
    if ((key == NULL) || (len == NULL)) {
2514
0
        ret = BAD_FUNC_ARG;
2515
0
    }
2516
2517
4.50k
    if (ret == 0) {
2518
        /* Return in 'len' size of the encoded private key for the type of this
2519
         * key. */
2520
4.50k
        switch (key->type) {
2521
0
#ifndef WOLFSSL_NO_ML_KEM
2522
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2523
0
        case WC_ML_KEM_512:
2524
0
            *len = WC_ML_KEM_512_PRIVATE_KEY_SIZE;
2525
0
            break;
2526
0
    #endif
2527
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2528
4.36k
        case WC_ML_KEM_768:
2529
4.36k
            *len = WC_ML_KEM_768_PRIVATE_KEY_SIZE;
2530
4.36k
            break;
2531
0
    #endif
2532
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2533
138
        case WC_ML_KEM_1024:
2534
138
            *len = WC_ML_KEM_1024_PRIVATE_KEY_SIZE;
2535
138
            break;
2536
0
    #endif
2537
0
#endif
2538
#ifdef WOLFSSL_MLKEM_KYBER
2539
    #ifdef WOLFSSL_KYBER512
2540
        case KYBER512:
2541
            *len = KYBER512_PRIVATE_KEY_SIZE;
2542
            break;
2543
    #endif
2544
    #ifdef WOLFSSL_KYBER768
2545
        case KYBER768:
2546
            *len = KYBER768_PRIVATE_KEY_SIZE;
2547
            break;
2548
    #endif
2549
    #ifdef WOLFSSL_KYBER1024
2550
        case KYBER1024:
2551
            *len = KYBER1024_PRIVATE_KEY_SIZE;
2552
            break;
2553
    #endif
2554
#endif
2555
0
        default:
2556
            /* No other values supported. */
2557
0
            ret = NOT_COMPILED_IN;
2558
0
            break;
2559
4.50k
        }
2560
4.50k
    }
2561
2562
4.50k
    return ret;
2563
4.50k
}
2564
2565
/**
2566
 * Get the size in bytes of encoded public key for the key.
2567
 *
2568
 * @param  [in]   key  ML-KEM key object.
2569
 * @param  [out]  len  Length of encoded public key in bytes.
2570
 * @return  0 on success.
2571
 * @return  BAD_FUNC_ARG when key or len is NULL.
2572
 * @return  NOT_COMPILED_IN when key type is not supported.
2573
 */
2574
int wc_MlKemKey_PublicKeySize(MlKemKey* key, word32* len)
2575
4.50k
{
2576
4.50k
    int ret = 0;
2577
2578
    /* Validate parameters. */
2579
4.50k
    if ((key == NULL) || (len == NULL)) {
2580
0
        ret = BAD_FUNC_ARG;
2581
0
    }
2582
2583
4.50k
    if (ret == 0) {
2584
        /* Return in 'len' size of the encoded public key for the type of this
2585
         * key. */
2586
4.50k
        switch (key->type) {
2587
0
#ifndef WOLFSSL_NO_ML_KEM
2588
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2589
0
        case WC_ML_KEM_512:
2590
0
            *len = WC_ML_KEM_512_PUBLIC_KEY_SIZE;
2591
0
            break;
2592
0
    #endif
2593
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2594
4.36k
        case WC_ML_KEM_768:
2595
4.36k
            *len = WC_ML_KEM_768_PUBLIC_KEY_SIZE;
2596
4.36k
            break;
2597
0
    #endif
2598
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2599
138
        case WC_ML_KEM_1024:
2600
138
            *len = WC_ML_KEM_1024_PUBLIC_KEY_SIZE;
2601
138
            break;
2602
0
    #endif
2603
0
#endif
2604
#ifdef WOLFSSL_MLKEM_KYBER
2605
    #ifdef WOLFSSL_KYBER512
2606
        case KYBER512:
2607
            *len = KYBER512_PUBLIC_KEY_SIZE;
2608
            break;
2609
    #endif
2610
    #ifdef WOLFSSL_KYBER768
2611
        case KYBER768:
2612
            *len = KYBER768_PUBLIC_KEY_SIZE;
2613
            break;
2614
    #endif
2615
    #ifdef WOLFSSL_KYBER1024
2616
        case KYBER1024:
2617
            *len = KYBER1024_PUBLIC_KEY_SIZE;
2618
            break;
2619
    #endif
2620
#endif
2621
0
        default:
2622
            /* No other values supported. */
2623
0
            ret = NOT_COMPILED_IN;
2624
0
            break;
2625
4.50k
        }
2626
4.50k
    }
2627
2628
4.50k
    return ret;
2629
4.50k
}
2630
2631
/**
2632
 * Encode the private key.
2633
 *
2634
 * Private Vector | Public Key | Public Hash | Randomizer
2635
 *
2636
 * FIPS 203, Algorithm 16: ML-KEM.KeyGen_internal(d,z)
2637
 *   ...
2638
 *   3: dk <- (dk_PKE||ek||H(ek)||z)
2639
 *   ...
2640
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
2641
 *   ...
2642
 *   20: dk_PKE  <- ByteEncode_12(s_hat)
2643
 *   ...
2644
 *
2645
 * @param  [in]   key  ML-KEM key object.
2646
 * @param  [out]  out  Buffer to hold data.
2647
 * @param  [in]   len  Size of buffer in bytes.
2648
 * @return  0 on success.
2649
 * @return  BAD_FUNC_ARG when key or out is NULL.
2650
 * @return  BAD_STATE_E when private/public key not available.
2651
 * @return  NOT_COMPILED_IN when key type is not supported.
2652
 */
2653
int wc_MlKemKey_EncodePrivateKey(MlKemKey* key, unsigned char* out, word32 len)
2654
4.48k
{
2655
4.48k
    int ret = 0;
2656
4.48k
    unsigned int k = 0;
2657
4.48k
    unsigned int pubLen = 0;
2658
4.48k
    unsigned int privLen = 0;
2659
4.48k
    unsigned char* p = out;
2660
2661
4.48k
    if ((key == NULL) || (out == NULL)) {
2662
0
        ret = BAD_FUNC_ARG;
2663
0
    }
2664
4.48k
    if ((ret == 0) &&
2665
4.48k
            ((key->flags & MLKEM_FLAG_BOTH_SET) != MLKEM_FLAG_BOTH_SET)) {
2666
0
        ret = BAD_STATE_E;
2667
0
    }
2668
2669
4.48k
    if (ret == 0) {
2670
4.48k
        switch (key->type) {
2671
0
#ifndef WOLFSSL_NO_ML_KEM
2672
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2673
0
        case WC_ML_KEM_512:
2674
0
            k = WC_ML_KEM_512_K;
2675
0
            pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE;
2676
0
            privLen = WC_ML_KEM_512_PRIVATE_KEY_SIZE;
2677
0
            break;
2678
0
    #endif
2679
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2680
4.35k
        case WC_ML_KEM_768:
2681
4.35k
            k = WC_ML_KEM_768_K;
2682
4.35k
            pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE;
2683
4.35k
            privLen = WC_ML_KEM_768_PRIVATE_KEY_SIZE;
2684
4.35k
            break;
2685
0
    #endif
2686
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2687
137
        case WC_ML_KEM_1024:
2688
137
            k = WC_ML_KEM_1024_K;
2689
137
            pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE;
2690
137
            privLen = WC_ML_KEM_1024_PRIVATE_KEY_SIZE;
2691
137
            break;
2692
0
    #endif
2693
0
#endif
2694
#ifdef WOLFSSL_MLKEM_KYBER
2695
    #ifdef WOLFSSL_KYBER512
2696
        case KYBER512:
2697
            k = KYBER512_K;
2698
            pubLen = KYBER512_PUBLIC_KEY_SIZE;
2699
            privLen = KYBER512_PRIVATE_KEY_SIZE;
2700
            break;
2701
    #endif
2702
    #ifdef WOLFSSL_KYBER768
2703
        case KYBER768:
2704
            k = KYBER768_K;
2705
            pubLen = KYBER768_PUBLIC_KEY_SIZE;
2706
            privLen = KYBER768_PRIVATE_KEY_SIZE;
2707
            break;
2708
    #endif
2709
    #ifdef WOLFSSL_KYBER1024
2710
        case KYBER1024:
2711
            k = KYBER1024_K;
2712
            pubLen = KYBER1024_PUBLIC_KEY_SIZE;
2713
            privLen = KYBER1024_PRIVATE_KEY_SIZE;
2714
            break;
2715
    #endif
2716
#endif
2717
0
        default:
2718
            /* No other values supported. */
2719
0
            ret = NOT_COMPILED_IN;
2720
0
            break;
2721
4.48k
        }
2722
4.48k
    }
2723
    /* Check buffer is big enough for encoding. */
2724
4.48k
    if ((ret == 0) && (len != privLen)) {
2725
0
        ret = BUFFER_E;
2726
0
    }
2727
2728
4.48k
    if (ret == 0) {
2729
        /* Encode private key that is vector of polynomials. */
2730
4.48k
        mlkem_to_bytes(p, key->priv, (int)k);
2731
4.48k
        p += WC_ML_KEM_POLY_SIZE * k;
2732
2733
        /* Encode public key - calculates hash of public key. */
2734
4.48k
        ret = wc_MlKemKey_EncodePublicKey(key, p, pubLen);
2735
4.48k
        p += pubLen;
2736
4.48k
    }
2737
4.48k
    if (ret == 0) {
2738
        /* Append public hash. */
2739
4.48k
        XMEMCPY(p, key->h, sizeof(key->h));
2740
4.48k
        p += WC_ML_KEM_SYM_SZ;
2741
        /* Append z (randomizer). */
2742
4.48k
        XMEMCPY(p, key->z, sizeof(key->z));
2743
4.48k
    }
2744
2745
4.48k
    return ret;
2746
4.48k
}
2747
2748
/**
2749
 * Encode the public key.
2750
 *
2751
 * Public vector | Public Seed
2752
 *
2753
 * FIPS 203, Algorithm 16: ML-KEM.KeyGen_internal(d,z)
2754
 *   ...
2755
 *   2: ek <- ek_PKE
2756
 *   ...
2757
 * FIPS 203, Algorithm 13: K-PKE.KeyGen(d)
2758
 *   ...
2759
 *   19: ek_PKE  <- ByteEncode_12(t_hat)||rho
2760
 *   ...
2761
 *
2762
 * @param  [in]   key  ML-KEM key object.
2763
 * @param  [out]  out  Buffer to hold data.
2764
 * @param  [in]   len  Size of buffer in bytes.
2765
 * @return  0 on success.
2766
 * @return  BAD_FUNC_ARG when key or out is NULL.
2767
 * @return  BAD_STATE_E when public key not available.
2768
 * @return  NOT_COMPILED_IN when key type is not supported.
2769
 */
2770
int wc_MlKemKey_EncodePublicKey(MlKemKey* key, unsigned char* out, word32 len)
2771
8.97k
{
2772
8.97k
    int ret = 0;
2773
8.97k
    unsigned int k = 0;
2774
8.97k
    unsigned int pubLen = 0;
2775
8.97k
    unsigned char* p = out;
2776
2777
8.97k
    if ((key == NULL) || (out == NULL)) {
2778
0
        ret = BAD_FUNC_ARG;
2779
0
    }
2780
8.97k
    if ((ret == 0) &&
2781
8.97k
            ((key->flags & MLKEM_FLAG_PUB_SET) != MLKEM_FLAG_PUB_SET)) {
2782
0
        ret = BAD_STATE_E;
2783
0
    }
2784
2785
8.97k
    if (ret == 0) {
2786
8.97k
        switch (key->type) {
2787
0
#ifndef WOLFSSL_NO_ML_KEM
2788
0
    #ifdef WOLFSSL_WC_ML_KEM_512
2789
0
        case WC_ML_KEM_512:
2790
0
            k = WC_ML_KEM_512_K;
2791
0
            pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE;
2792
0
            break;
2793
0
    #endif
2794
0
    #ifdef WOLFSSL_WC_ML_KEM_768
2795
8.70k
        case WC_ML_KEM_768:
2796
8.70k
            k = WC_ML_KEM_768_K;
2797
8.70k
            pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE;
2798
8.70k
            break;
2799
0
    #endif
2800
0
    #ifdef WOLFSSL_WC_ML_KEM_1024
2801
274
        case WC_ML_KEM_1024:
2802
274
            k = WC_ML_KEM_1024_K;
2803
274
            pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE;
2804
274
            break;
2805
0
    #endif
2806
0
#endif
2807
#ifdef WOLFSSL_MLKEM_KYBER
2808
    #ifdef WOLFSSL_KYBER512
2809
        case KYBER512:
2810
            k = KYBER512_K;
2811
            pubLen = KYBER512_PUBLIC_KEY_SIZE;
2812
            break;
2813
    #endif
2814
    #ifdef WOLFSSL_KYBER768
2815
        case KYBER768:
2816
            k = KYBER768_K;
2817
            pubLen = KYBER768_PUBLIC_KEY_SIZE;
2818
            break;
2819
    #endif
2820
    #ifdef WOLFSSL_KYBER1024
2821
        case KYBER1024:
2822
            k = KYBER1024_K;
2823
            pubLen = KYBER1024_PUBLIC_KEY_SIZE;
2824
            break;
2825
    #endif
2826
#endif
2827
0
        default:
2828
            /* No other values supported. */
2829
0
            ret = NOT_COMPILED_IN;
2830
0
            break;
2831
8.97k
        }
2832
8.97k
    }
2833
    /* Check buffer is big enough for encoding. */
2834
8.97k
    if ((ret == 0) && (len != pubLen)) {
2835
0
        ret = BUFFER_E;
2836
0
    }
2837
2838
8.97k
    if (ret == 0) {
2839
8.97k
        int i;
2840
2841
        /* Encode public key polynomial by polynomial. */
2842
8.97k
        mlkem_to_bytes(p, key->pub, (int)k);
2843
8.97k
        p += k * WC_ML_KEM_POLY_SIZE;
2844
2845
        /* Append public seed. */
2846
296k
        for (i = 0; i < WC_ML_KEM_SYM_SZ; i++) {
2847
287k
            p[i] = key->pubSeed[i];
2848
287k
        }
2849
2850
        /* Make sure public hash is set. */
2851
8.97k
        if ((key->flags & MLKEM_FLAG_H_SET) == 0) {
2852
4.48k
            ret = MLKEM_HASH_H(&key->hash, out, len, key->h);
2853
4.48k
        }
2854
8.97k
    }
2855
8.97k
    if (ret == 0) {
2856
        /* Public hash is set. wc_mlkemkey_check_h() relies on this happening on
2857
         * every successful path: it calls this function to establish the flag
2858
         * and does not test it again afterwards. */
2859
8.97k
        key->flags |= MLKEM_FLAG_H_SET;
2860
8.97k
    }
2861
2862
8.97k
    return ret;
2863
8.97k
}
2864
2865
#endif /* WOLFSSL_HAVE_MLKEM */