Coverage Report

Created: 2026-09-12 06:55

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
37
typedef struct {
38
    ML_KEM_KEY *key;
39
    uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];
40
    uint8_t *entropy;
41
    int op;
42
} PROV_ML_KEM_CTX;
43
44
static void *ml_kem_newctx(void *provctx)
45
342
{
46
342
    PROV_ML_KEM_CTX *ctx;
47
48
342
    if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
49
0
        return NULL;
50
51
342
    ctx->key = NULL;
52
342
    ctx->entropy = NULL;
53
342
    ctx->op = 0;
54
342
    return ctx;
55
342
}
56
57
static void ml_kem_freectx(void *vctx)
58
342
{
59
342
    PROV_ML_KEM_CTX *ctx = vctx;
60
61
342
    if (ctx->entropy != NULL)
62
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
63
342
    OPENSSL_free(ctx);
64
342
}
65
66
static int ml_kem_init(void *vctx, int op, void *key,
67
    const OSSL_PARAM params[])
68
134
{
69
134
    PROV_ML_KEM_CTX *ctx = vctx;
70
71
134
    if (!ossl_prov_is_running())
72
0
        return 0;
73
134
    ctx->key = key;
74
134
    ctx->op = op;
75
134
    return ml_kem_set_ctx_params(vctx, params);
76
134
}
77
78
static int ml_kem_encapsulate_init(void *vctx, void *vkey,
79
    const OSSL_PARAM params[])
80
183
{
81
183
    ML_KEM_KEY *key = vkey;
82
83
183
    if (!ossl_ml_kem_have_pubkey(key)) {
84
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
85
0
        return 0;
86
0
    }
87
183
    return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
88
183
}
89
90
static int ml_kem_decapsulate_init(void *vctx, void *vkey,
91
    const OSSL_PARAM params[])
92
159
{
93
159
    ML_KEM_KEY *key = vkey;
94
95
159
    if (!ossl_ml_kem_have_prvkey(key)) {
96
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
97
0
        return 0;
98
0
    }
99
159
    return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
100
159
}
101
102
/* clang-format off */
103
/* Machine generated by util/perl/OpenSSL/paramnames.pm */
104
#ifndef ml_kem_set_ctx_params_list
105
static const OSSL_PARAM ml_kem_set_ctx_params_list[] = {
106
    OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
107
    OSSL_PARAM_END
108
};
109
#endif
110
111
#ifndef ml_kem_set_ctx_params_st
112
struct ml_kem_set_ctx_params_st {
113
    OSSL_PARAM *ikme;
114
};
115
#endif
116
117
#ifndef ml_kem_set_ctx_params_decoder
118
static int ml_kem_set_ctx_params_decoder
119
    (const OSSL_PARAM *p, struct ml_kem_set_ctx_params_st *r)
120
282
{
121
282
    const char *s;
122
123
282
    memset(r, 0, sizeof(*r));
124
282
    if (p != NULL)
125
0
        for (; (s = p->key) != NULL; p++)
126
0
            if (ossl_likely(strcmp("ikme", s + 0) == 0)) {
127
                /* OSSL_KEM_PARAM_IKME */
128
0
                if (ossl_unlikely(r->ikme != NULL)) {
129
0
                    ERR_raise_data(ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER,
130
0
                                   "param %s is repeated", s);
131
0
                    return 0;
132
0
                }
133
0
                r->ikme = (OSSL_PARAM *)p;
134
0
            }
135
282
    return 1;
136
282
}
137
#endif
138
/* End of machine generated */
139
/* clang-format on */
140
141
static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
142
74
{
143
74
    PROV_ML_KEM_CTX *ctx = vctx;
144
74
    struct ml_kem_set_ctx_params_st p;
145
146
74
    if (ctx == NULL || !ml_kem_set_ctx_params_decoder(params, &p))
147
0
        return 0;
148
149
74
    if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) {
150
        /* Decapsulation is deterministic */
151
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
152
0
        ctx->entropy = NULL;
153
0
    }
154
155
    /* Encapsulation ephemeral input key material "ikmE" */
156
74
    if (ctx->op == EVP_PKEY_OP_ENCAPSULATE && p.ikme != NULL) {
157
0
        size_t len = ML_KEM_RANDOM_BYTES;
158
159
0
        ctx->entropy = ctx->entropy_buf;
160
0
        if (OSSL_PARAM_get_octet_string(p.ikme, (void **)&ctx->entropy,
161
0
                len, &len)
162
0
            && len == ML_KEM_RANDOM_BYTES)
163
0
            return 1;
164
165
        /* Possibly, but much less likely wrong type */
166
0
        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
167
0
        OPENSSL_cleanse((void *)ctx->entropy_buf, sizeof(ctx->entropy_buf));
168
0
        ctx->entropy = NULL;
169
0
        return 0;
170
0
    }
171
172
74
    return 1;
173
74
}
174
175
static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,
176
    ossl_unused void *provctx)
177
12
{
178
12
    return ml_kem_set_ctx_params_list;
179
12
}
180
181
static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
182
    unsigned char *shsec, size_t *slen)
183
183
{
184
183
    PROV_ML_KEM_CTX *ctx = vctx;
185
183
    ML_KEM_KEY *key = ctx->key;
186
183
    const ML_KEM_VINFO *v;
187
183
    size_t encap_clen;
188
183
    size_t encap_slen;
189
183
    int ret = 0;
190
191
183
    if (!ossl_ml_kem_have_pubkey(key)) {
192
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
193
0
        goto end;
194
0
    }
195
183
    v = ossl_ml_kem_key_vinfo(key);
196
183
    encap_clen = v->ctext_bytes;
197
183
    encap_slen = ML_KEM_SHARED_SECRET_BYTES;
198
199
183
    if (ctext == NULL) {
200
0
        if (clen == NULL && slen == NULL)
201
0
            return 0;
202
0
        if (clen != NULL)
203
0
            *clen = encap_clen;
204
0
        if (slen != NULL)
205
0
            *slen = encap_slen;
206
0
        return 1;
207
0
    }
208
183
    if (shsec == NULL) {
209
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
210
0
            "NULL shared-secret buffer");
211
0
        goto end;
212
0
    }
213
214
183
    if (clen == NULL) {
215
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
216
0
            "null ciphertext input/output length pointer");
217
0
        goto end;
218
183
    } else if (*clen < encap_clen) {
219
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
220
0
            "ciphertext buffer too small");
221
0
        goto end;
222
183
    } else {
223
183
        *clen = encap_clen;
224
183
    }
225
226
183
    if (slen == NULL) {
227
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
228
0
            "null shared secret input/output length pointer");
229
0
        goto end;
230
183
    } else if (*slen < encap_slen) {
231
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
232
0
            "shared-secret buffer too small");
233
0
        goto end;
234
183
    } else {
235
183
        *slen = encap_slen;
236
183
    }
237
238
183
    if (ctx->entropy != NULL)
239
0
        ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,
240
0
            ctx->entropy, ML_KEM_RANDOM_BYTES, key);
241
183
    else
242
183
        ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);
243
244
183
end:
245
    /*
246
     * One shot entropy, each encapsulate call must either provide a new
247
     * "ikmE", or else will use a random value.  If a caller sets an explicit
248
     * ikmE once for testing, and later performs multiple encapsulations
249
     * without again calling encapsulate_init(), these should not share the
250
     * original entropy.
251
     */
252
183
    if (ctx->entropy != NULL) {
253
0
        OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
254
0
        ctx->entropy = NULL;
255
0
    }
256
183
    return ret;
257
183
}
258
259
static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
260
    const uint8_t *ctext, size_t clen)
261
159
{
262
159
    PROV_ML_KEM_CTX *ctx = vctx;
263
159
    ML_KEM_KEY *key = ctx->key;
264
159
    size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;
265
266
159
    if (!ossl_ml_kem_have_prvkey(key)) {
267
0
        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
268
0
        return 0;
269
0
    }
270
271
159
    if (shsec == NULL) {
272
0
        if (slen == NULL)
273
0
            return 0;
274
0
        *slen = ML_KEM_SHARED_SECRET_BYTES;
275
0
        return 1;
276
0
    }
277
278
    /* For now tolerate newly-deprecated NULL length pointers. */
279
159
    if (slen == NULL) {
280
0
        slen = &decap_slen;
281
159
    } else if (*slen < decap_slen) {
282
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
283
0
            "shared-secret buffer too small");
284
0
        return 0;
285
159
    } else {
286
159
        *slen = decap_slen;
287
159
    }
288
289
    /* ML-KEM decap handles incorrect ciphertext lengths internally */
290
159
    return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);
291
159
}
292
293
const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {
294
    { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)ml_kem_newctx },
295
    { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)ml_kem_encapsulate_init },
296
    { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)ml_kem_encapsulate },
297
    { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)ml_kem_decapsulate_init },
298
    { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)ml_kem_decapsulate },
299
    { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)ml_kem_freectx },
300
    { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)ml_kem_set_ctx_params },
301
    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)ml_kem_settable_ctx_params },
302
    OSSL_DISPATCH_END
303
};