Coverage Report

Created: 2026-09-12 06:55

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