Coverage Report

Created: 2026-10-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl36/providers/implementations/kem/ml_kem_kem.c
Line
Count
Source
1
/*
2
 * Copyright 2024-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
/* clang-format off */
10
11
/* clang-format on */
12
13
#include <string.h>
14
#include <openssl/crypto.h>
15
#include <openssl/evp.h>
16
#include <openssl/core_dispatch.h>
17
#include <openssl/core_names.h>
18
#include <openssl/params.h>
19
#include <openssl/err.h>
20
#include <openssl/proverr.h>
21
#include "crypto/ml_kem.h"
22
#include "internal/cryptlib.h"
23
#include "prov/provider_ctx.h"
24
#include "prov/implementations.h"
25
#include "prov/securitycheck.h"
26
#include "prov/providercommon.h"
27
28
static OSSL_FUNC_kem_newctx_fn ml_kem_newctx;
29
static OSSL_FUNC_kem_freectx_fn ml_kem_freectx;
30
static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init;
31
static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate;
32
static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init;
33
static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate;
34
static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params;
35
static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params;
36
#ifdef FIPS_MODULE
37
static OSSL_FUNC_kem_get_ctx_params_fn ml_kem_get_ctx_params;
38
static OSSL_FUNC_kem_gettable_ctx_params_fn ml_kem_gettable_ctx_params;
39
#endif
40
41
typedef struct {
42
    ML_KEM_KEY *key;
43
    uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];
44
    uint8_t *entropy;
45
    int op;
46
    int test_entropy_used;
47
} PROV_ML_KEM_CTX;
48
49
static void *ml_kem_newctx(void *provctx)
50
381
{
51
381
    PROV_ML_KEM_CTX *ctx;
52
53
381
    if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
54
0
        return NULL;
55
56
381
    ctx->key = NULL;
57
381
    ctx->entropy = NULL;
58
381
    ctx->op = 0;
59
381
    ctx->test_entropy_used = 0;
60
381
    return ctx;
61
381
}
62
63
static void ml_kem_freectx(void *vctx)
64
381
{
65
381
    PROV_ML_KEM_CTX *ctx = vctx;
66
67
381
    if (ctx->entropy != NULL)
68
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
69
381
    OPENSSL_free(ctx);
70
381
}
71
72
static int ml_kem_init(void *vctx, int op, void *key,
73
    const OSSL_PARAM params[])
74
317
{
75
317
    PROV_ML_KEM_CTX *ctx = vctx;
76
77
317
    if (!ossl_prov_is_running())
78
0
        return 0;
79
317
    ctx->key = key;
80
317
    ctx->op = op;
81
317
    ctx->test_entropy_used = 0;
82
317
    if (ctx->entropy != NULL) {
83
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
84
0
        ctx->entropy = NULL;
85
0
    }
86
317
    return ml_kem_set_ctx_params(vctx, params);
87
317
}
88
89
static int ml_kem_encapsulate_init(void *vctx, void *vkey,
90
    const OSSL_PARAM params[])
91
184
{
92
184
    ML_KEM_KEY *key = vkey;
93
94
184
    if (!ossl_ml_kem_have_pubkey(key)) {
95
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
96
0
        return 0;
97
0
    }
98
184
    return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
99
184
}
100
101
static int ml_kem_decapsulate_init(void *vctx, void *vkey,
102
    const OSSL_PARAM params[])
103
197
{
104
197
    ML_KEM_KEY *key = vkey;
105
106
197
    if (!ossl_ml_kem_have_prvkey(key)) {
107
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
108
0
        return 0;
109
0
    }
110
197
    return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
111
197
}
112
113
/* clang-format off */
114
/* Machine generated by util/perl/OpenSSL/paramnames.pm */
115
#ifndef ml_kem_set_ctx_params_list
116
static ossl_unused const OSSL_PARAM ml_kem_set_ctx_params_list[] = {
117
    OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
118
    OSSL_PARAM_END
119
};
120
#endif
121
122
#ifndef ml_kem_set_ctx_params_st
123
struct ml_kem_set_ctx_params_st {
124
    OSSL_PARAM *ikme;
125
};
126
#endif
127
128
#ifndef ml_kem_set_ctx_params_decoder
129
static ossl_unused int ml_kem_set_ctx_params_decoder
130
    (const OSSL_PARAM *p, struct ml_kem_set_ctx_params_st *r)
131
317
{
132
317
    const char *s;
133
134
317
    memset(r, 0, sizeof(*r));
135
317
    if (p != NULL)
136
0
        for (; (s = p->key) != NULL; p++)
137
0
            if (ossl_likely(strcmp("ikme", s + 0) == 0)) {
138
                /* OSSL_KEM_PARAM_IKME */
139
0
                if (ossl_unlikely(r->ikme != NULL)) {
140
0
                    ERR_raise_data(ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER,
141
0
                                   "param %s is repeated", s);
142
0
                    return 0;
143
0
                }
144
0
                r->ikme = (OSSL_PARAM *)p;
145
0
            }
146
317
    return 1;
147
317
}
148
#endif
149
/* End of machine generated */
150
151
/* Machine generated by util/perl/OpenSSL/paramnames.pm */
152
#ifndef ml_kem_get_ctx_params_list
153
static ossl_unused const OSSL_PARAM ml_kem_get_ctx_params_list[] = {
154
# if defined(FIPS_MODULE)
155
    OSSL_PARAM_int(OSSL_KEM_PARAM_FIPS_APPROVED_INDICATOR, NULL),
156
# endif
157
    OSSL_PARAM_END
158
};
159
#endif
160
161
#ifndef ml_kem_get_ctx_params_st
162
struct ml_kem_get_ctx_params_st {
163
# if defined(FIPS_MODULE)
164
    OSSL_PARAM *ind;
165
# else
166
    int dummy; /* unused */
167
# endif
168
};
169
#endif
170
171
#ifndef ml_kem_get_ctx_params_decoder
172
static ossl_unused int ml_kem_get_ctx_params_decoder
173
    (const OSSL_PARAM *p, struct ml_kem_get_ctx_params_st *r)
174
0
{
175
0
    const char *s;
176
0
177
0
    memset(r, 0, sizeof(*r));
178
0
    if (p != NULL)
179
0
        for (; (s = p->key) != NULL; p++)
180
0
# if defined(FIPS_MODULE)
181
0
            if (ossl_likely(strcmp("fips-indicator", s + 0) == 0)) {
182
0
                /* OSSL_KEM_PARAM_FIPS_APPROVED_INDICATOR */
183
0
                if (ossl_unlikely(r->ind != NULL)) {
184
0
                    ERR_raise_data(ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER,
185
0
                                   "param %s is repeated", s);
186
0
                    return 0;
187
0
                }
188
0
                r->ind = (OSSL_PARAM *)p;
189
0
            }
190
0
# else
191
0
            ;
192
0
# endif
193
0
    return 1;
194
0
}
195
#endif
196
/* End of machine generated */
197
/* clang-format on */
198
199
#ifdef FIPS_MODULE
200
static const OSSL_PARAM *ml_kem_gettable_ctx_params(ossl_unused void *vctx,
201
    ossl_unused void *provctx)
202
{
203
    return ml_kem_get_ctx_params_list;
204
}
205
206
static int ml_kem_get_ctx_params(void *vctx, OSSL_PARAM params[])
207
{
208
    PROV_ML_KEM_CTX *ctx = vctx;
209
    struct ml_kem_get_ctx_params_st p;
210
211
    if (ctx == NULL || !ml_kem_get_ctx_params_decoder(params, &p))
212
        return 0;
213
    if (!OSSL_FIPS_IND_GET_PARAM_CONDITIONAL(p.ind,
214
            !ctx->test_entropy_used))
215
        return 0;
216
    return 1;
217
}
218
#endif
219
220
static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
221
60
{
222
60
    PROV_ML_KEM_CTX *ctx = vctx;
223
60
    struct ml_kem_set_ctx_params_st p;
224
225
60
    if (ctx == NULL || !ml_kem_set_ctx_params_decoder(params, &p))
226
0
        return 0;
227
228
60
    if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) {
229
        /* Decapsulation is deterministic */
230
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
231
0
        ctx->entropy = NULL;
232
0
    }
233
234
    /* Encapsulation ephemeral input key material "ikmE" */
235
60
    if (ctx->op == EVP_PKEY_OP_ENCAPSULATE && p.ikme != NULL) {
236
0
        size_t len = ML_KEM_RANDOM_BYTES;
237
238
0
        ctx->entropy = ctx->entropy_buf;
239
0
        if (OSSL_PARAM_get_octet_string(p.ikme, (void **)&ctx->entropy,
240
0
                len, &len)
241
0
            && len == ML_KEM_RANDOM_BYTES) {
242
0
            ctx->test_entropy_used = 1;
243
0
            return 1;
244
0
        }
245
246
        /* Possibly, but much less likely wrong type */
247
0
        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
248
0
        OPENSSL_cleanse((void *)ctx->entropy_buf, sizeof(ctx->entropy_buf));
249
0
        ctx->entropy = NULL;
250
0
        return 0;
251
0
    }
252
253
60
    return 1;
254
60
}
255
256
static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,
257
    ossl_unused void *provctx)
258
19
{
259
19
    return ml_kem_set_ctx_params_list;
260
19
}
261
262
static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
263
    unsigned char *shsec, size_t *slen)
264
184
{
265
184
    PROV_ML_KEM_CTX *ctx = vctx;
266
184
    ML_KEM_KEY *key = ctx->key;
267
184
    const ML_KEM_VINFO *v;
268
184
    size_t encap_clen;
269
184
    size_t encap_slen;
270
184
    int ret = 0;
271
272
184
    if (!ossl_ml_kem_have_pubkey(key)) {
273
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
274
0
        goto end;
275
0
    }
276
184
    v = ossl_ml_kem_key_vinfo(key);
277
184
    encap_clen = v->ctext_bytes;
278
184
    encap_slen = ML_KEM_SHARED_SECRET_BYTES;
279
280
184
    if (ctext == NULL) {
281
0
        if (clen == NULL && slen == NULL)
282
0
            return 0;
283
0
        if (clen != NULL)
284
0
            *clen = encap_clen;
285
0
        if (slen != NULL)
286
0
            *slen = encap_slen;
287
0
        return 1;
288
0
    }
289
184
    if (shsec == NULL) {
290
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
291
0
            "NULL shared-secret buffer");
292
0
        goto end;
293
0
    }
294
295
184
    if (clen == NULL) {
296
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
297
0
            "null ciphertext input/output length pointer");
298
0
        goto end;
299
184
    } else if (*clen < encap_clen) {
300
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
301
0
            "ciphertext buffer too small");
302
0
        goto end;
303
184
    } else {
304
184
        *clen = encap_clen;
305
184
    }
306
307
184
    if (slen == NULL) {
308
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
309
0
            "null shared secret input/output length pointer");
310
0
        goto end;
311
184
    } else if (*slen < encap_slen) {
312
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
313
0
            "shared-secret buffer too small");
314
0
        goto end;
315
184
    } else {
316
184
        *slen = encap_slen;
317
184
    }
318
319
184
    if (ctx->entropy != NULL)
320
0
        ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,
321
0
            ctx->entropy, ML_KEM_RANDOM_BYTES, key);
322
184
    else
323
184
        ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);
324
325
184
end:
326
    /*
327
     * One shot entropy, each encapsulate call must either provide a new
328
     * "ikmE", or else will use a random value.  If a caller sets an explicit
329
     * ikmE once for testing, and later performs multiple encapsulations
330
     * without again calling encapsulate_init(), these should not share the
331
     * original entropy.
332
     */
333
184
    if (ctx->entropy != NULL) {
334
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
335
0
        ctx->entropy = NULL;
336
0
    }
337
184
    return ret;
338
184
}
339
340
static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
341
    const uint8_t *ctext, size_t clen)
342
197
{
343
197
    PROV_ML_KEM_CTX *ctx = vctx;
344
197
    ML_KEM_KEY *key = ctx->key;
345
197
    size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;
346
347
197
    if (!ossl_ml_kem_have_prvkey(key)) {
348
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
349
0
        return 0;
350
0
    }
351
352
197
    if (shsec == NULL) {
353
0
        if (slen == NULL)
354
0
            return 0;
355
0
        *slen = ML_KEM_SHARED_SECRET_BYTES;
356
0
        return 1;
357
0
    }
358
359
    /* For now tolerate newly-deprecated NULL length pointers. */
360
197
    if (slen == NULL) {
361
0
        slen = &decap_slen;
362
197
    } else if (*slen < decap_slen) {
363
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
364
0
            "shared-secret buffer too small");
365
0
        return 0;
366
197
    } else {
367
197
        *slen = decap_slen;
368
197
    }
369
370
    /* ML-KEM decap handles incorrect ciphertext lengths internally */
371
197
    return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);
372
197
}
373
374
const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {
375
    { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)ml_kem_newctx },
376
    { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)ml_kem_encapsulate_init },
377
    { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)ml_kem_encapsulate },
378
    { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)ml_kem_decapsulate_init },
379
    { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)ml_kem_decapsulate },
380
    { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)ml_kem_freectx },
381
    { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)ml_kem_set_ctx_params },
382
    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)ml_kem_settable_ctx_params },
383
#ifdef FIPS_MODULE
384
    { OSSL_FUNC_KEM_GET_CTX_PARAMS, (OSSL_FUNC)ml_kem_get_ctx_params },
385
    { OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS, (OSSL_FUNC)ml_kem_gettable_ctx_params },
386
#endif
387
    OSSL_DISPATCH_END
388
};