Coverage Report

Created: 2026-10-16 06:40

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