Coverage Report

Created: 2026-10-28 06:28

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