Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/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
10
#include <string.h>
11
#include <openssl/crypto.h>
12
#include <openssl/evp.h>
13
#include <openssl/core_dispatch.h>
14
#include <openssl/core_names.h>
15
#include <openssl/params.h>
16
#include <openssl/err.h>
17
#include <openssl/proverr.h>
18
#include "crypto/ml_kem.h"
19
#include "internal/cryptlib.h"
20
#include "internal/fips.h"
21
#include "prov/provider_ctx.h"
22
#include "prov/implementations.h"
23
#include "prov/securitycheck.h"
24
#include "prov/providercommon.h"
25
#include "providers/implementations/kem/ml_kem_kem.inc"
26
27
static OSSL_FUNC_kem_newctx_fn ml_kem_newctx;
28
static OSSL_FUNC_kem_freectx_fn ml_kem_freectx;
29
static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init;
30
static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate;
31
static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init;
32
static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate;
33
static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params;
34
static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params;
35
#ifdef FIPS_MODULE
36
static OSSL_FUNC_kem_get_ctx_params_fn ml_kem_get_ctx_params;
37
static OSSL_FUNC_kem_gettable_ctx_params_fn ml_kem_gettable_ctx_params;
38
#endif
39
40
typedef struct {
41
    ML_KEM_KEY *key;
42
    uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];
43
    uint8_t *entropy;
44
    int op;
45
    int test_entropy_used;
46
} PROV_ML_KEM_CTX;
47
48
static void *ml_kem_newctx(void *provctx)
49
0
{
50
0
    PROV_ML_KEM_CTX *ctx;
51
52
0
    if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
53
0
        return NULL;
54
55
#ifdef FIPS_MODULE
56
    if (!ossl_deferred_self_test(PROV_LIBCTX_OF(provctx),
57
            ST_ID_KEM_ML_KEM))
58
        return NULL;
59
#endif
60
61
0
    ctx->key = NULL;
62
0
    ctx->entropy = NULL;
63
0
    ctx->op = 0;
64
0
    ctx->test_entropy_used = 0;
65
0
    return ctx;
66
0
}
67
68
static void ml_kem_freectx(void *vctx)
69
0
{
70
0
    PROV_ML_KEM_CTX *ctx = vctx;
71
72
0
    if (ctx->entropy != NULL)
73
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
74
0
    OPENSSL_free(ctx);
75
0
}
76
77
#ifdef FIPS_MODULE
78
static const OSSL_PARAM *ml_kem_gettable_ctx_params(ossl_unused void *vctx,
79
    ossl_unused void *provctx)
80
{
81
    return ml_kem_get_ctx_params_list;
82
}
83
84
static int ml_kem_get_ctx_params(void *vctx, OSSL_PARAM params[])
85
{
86
    PROV_ML_KEM_CTX *ctx = vctx;
87
    struct ml_kem_get_ctx_params_st p;
88
89
    if (ctx == NULL || !ml_kem_get_ctx_params_decoder(params, &p))
90
        return 0;
91
    if (!OSSL_FIPS_IND_GET_PARAM_CONDITIONAL(p.ind,
92
            !ctx->test_entropy_used))
93
        return 0;
94
    return 1;
95
}
96
#endif
97
98
static int ml_kem_init(void *vctx, int op, void *key,
99
    const OSSL_PARAM params[])
100
0
{
101
0
    PROV_ML_KEM_CTX *ctx = vctx;
102
103
0
    if (!ossl_prov_is_running())
104
0
        return 0;
105
0
    ctx->key = key;
106
0
    ctx->op = op;
107
0
    ctx->test_entropy_used = 0;
108
0
    if (ctx->entropy != NULL) {
109
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
110
0
        ctx->entropy = NULL;
111
0
    }
112
0
    return ml_kem_set_ctx_params(vctx, params);
113
0
}
114
115
static int ml_kem_encapsulate_init(void *vctx, void *vkey,
116
    const OSSL_PARAM params[])
117
0
{
118
0
    ML_KEM_KEY *key = vkey;
119
120
0
    if (!ossl_ml_kem_have_pubkey(key)) {
121
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
122
0
        return 0;
123
0
    }
124
0
    return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
125
0
}
126
127
static int ml_kem_decapsulate_init(void *vctx, void *vkey,
128
    const OSSL_PARAM params[])
129
0
{
130
0
    ML_KEM_KEY *key = vkey;
131
132
0
    if (!ossl_ml_kem_have_prvkey(key)) {
133
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
134
0
        return 0;
135
0
    }
136
0
    return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
137
0
}
138
139
static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
140
0
{
141
0
    PROV_ML_KEM_CTX *ctx = vctx;
142
0
    struct ml_kem_set_ctx_params_st p;
143
144
0
    if (ctx == NULL || !ml_kem_set_ctx_params_decoder(params, &p))
145
0
        return 0;
146
147
    /* Encapsulation ephemeral input key material "ikmE" */
148
0
    if (ctx->op == EVP_PKEY_OP_ENCAPSULATE && p.ikme != NULL) {
149
0
        size_t len = ML_KEM_RANDOM_BYTES;
150
151
0
        ctx->entropy = ctx->entropy_buf;
152
0
        if (OSSL_PARAM_get_octet_string(p.ikme, (void **)&ctx->entropy,
153
0
                len, &len)
154
0
            && len == ML_KEM_RANDOM_BYTES) {
155
0
            ctx->test_entropy_used = 1;
156
0
            return 1;
157
0
        }
158
159
        /* Possibly, but much less likely wrong type */
160
0
        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
161
0
        OPENSSL_cleanse((void *)ctx->entropy_buf, sizeof(ctx->entropy_buf));
162
0
        ctx->entropy = NULL;
163
0
        return 0;
164
0
    }
165
166
0
    return 1;
167
0
}
168
169
static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,
170
    ossl_unused void *provctx)
171
0
{
172
0
    return ml_kem_set_ctx_params_list;
173
0
}
174
175
static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
176
    unsigned char *shsec, size_t *slen)
177
0
{
178
0
    PROV_ML_KEM_CTX *ctx = vctx;
179
0
    ML_KEM_KEY *key = ctx->key;
180
0
    const ML_KEM_VINFO *v;
181
0
    size_t encap_clen;
182
0
    size_t encap_slen;
183
0
    int ret = 0;
184
185
0
    if (!ossl_ml_kem_have_pubkey(key)) {
186
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
187
0
        goto end;
188
0
    }
189
0
    v = ossl_ml_kem_key_vinfo(key);
190
0
    encap_clen = v->ctext_bytes;
191
0
    encap_slen = ML_KEM_SHARED_SECRET_BYTES;
192
193
0
    if (ctext == NULL) {
194
0
        if (clen == NULL && slen == NULL)
195
0
            return 0;
196
0
        if (clen != NULL)
197
0
            *clen = encap_clen;
198
0
        if (slen != NULL)
199
0
            *slen = encap_slen;
200
0
        return 1;
201
0
    }
202
0
    if (shsec == NULL) {
203
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
204
0
            "NULL shared-secret buffer");
205
0
        goto end;
206
0
    }
207
208
0
    if (clen == NULL) {
209
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
210
0
            "null ciphertext input/output length pointer");
211
0
        goto end;
212
0
    } else if (*clen < encap_clen) {
213
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
214
0
            "ciphertext buffer too small");
215
0
        goto end;
216
0
    } else {
217
0
        *clen = encap_clen;
218
0
    }
219
220
0
    if (slen == NULL) {
221
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
222
0
            "null shared secret input/output length pointer");
223
0
        goto end;
224
0
    } else if (*slen < encap_slen) {
225
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
226
0
            "shared-secret buffer too small");
227
0
        goto end;
228
0
    } else {
229
0
        *slen = encap_slen;
230
0
    }
231
232
0
    if (ctx->entropy != NULL)
233
0
        ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,
234
0
            ctx->entropy, ML_KEM_RANDOM_BYTES, key);
235
0
    else
236
0
        ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);
237
238
0
end:
239
    /*
240
     * One shot entropy, each encapsulate call must either provide a new
241
     * "ikmE", or else will use a random value.  If a caller sets an explicit
242
     * ikmE once for testing, and later performs multiple encapsulations
243
     * without again calling encapsulate_init(), these should not share the
244
     * original entropy.
245
     */
246
0
    if (ctx->entropy != NULL) {
247
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
248
0
        ctx->entropy = NULL;
249
0
    }
250
0
    return ret;
251
0
}
252
253
static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
254
    const uint8_t *ctext, size_t clen)
255
0
{
256
0
    PROV_ML_KEM_CTX *ctx = vctx;
257
0
    ML_KEM_KEY *key = ctx->key;
258
0
    size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;
259
260
0
    if (!ossl_ml_kem_have_prvkey(key)) {
261
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
262
0
        return 0;
263
0
    }
264
265
0
    if (shsec == NULL) {
266
0
        if (slen == NULL)
267
0
            return 0;
268
0
        *slen = ML_KEM_SHARED_SECRET_BYTES;
269
0
        return 1;
270
0
    }
271
272
    /* For now tolerate newly-deprecated NULL length pointers. */
273
0
    if (slen == NULL) {
274
0
        slen = &decap_slen;
275
0
    } else if (*slen < decap_slen) {
276
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
277
0
            "shared-secret buffer too small");
278
0
        return 0;
279
0
    } else {
280
0
        *slen = decap_slen;
281
0
    }
282
283
    /* ML-KEM decap handles incorrect ciphertext lengths internally */
284
0
    return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);
285
0
}
286
287
const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {
288
    { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)ml_kem_newctx },
289
    { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)ml_kem_encapsulate_init },
290
    { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)ml_kem_encapsulate },
291
    { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)ml_kem_decapsulate_init },
292
    { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)ml_kem_decapsulate },
293
    { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)ml_kem_freectx },
294
    { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)ml_kem_set_ctx_params },
295
    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)ml_kem_settable_ctx_params },
296
#ifdef FIPS_MODULE
297
    { OSSL_FUNC_KEM_GET_CTX_PARAMS, (OSSL_FUNC)ml_kem_get_ctx_params },
298
    { OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS, (OSSL_FUNC)ml_kem_gettable_ctx_params },
299
#endif
300
    OSSL_DISPATCH_END
301
};