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