Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/providers/implementations/kem/mlx_kem.c
109304 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 <openssl/core_dispatch.h>
11
#include <openssl/core_names.h>
12
#include <openssl/crypto.h>
13
#include <openssl/err.h>
14
#include <openssl/evp.h>
15
#include <openssl/params.h>
16
#include <openssl/proverr.h>
17
#include <openssl/rand.h>
18
#include "prov/implementations.h"
19
#include "prov/mlx_kem.h"
20
#include "prov/provider_ctx.h"
21
#include "prov/providercommon.h"
22
23
static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;
24
static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;
25
static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init;
26
static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate;
27
static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init;
28
static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate;
29
static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params;
30
static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params;
31
32
typedef struct {
33
OSSL_LIB_CTX *libctx;
34
MLX_KEY *key;
35
int op;
36
} PROV_MLX_KEM_CTX;
37
38
static void *mlx_kem_newctx(void *provctx)
39
{
40
PROV_MLX_KEM_CTX *ctx;
41
42
if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
43
return NULL;
44
45
ctx->libctx = PROV_LIBCTX_OF(provctx);
46
ctx->key = NULL;
47
ctx->op = 0;
48
return ctx;
49
}
50
51
static void mlx_kem_freectx(void *vctx)
52
{
53
OPENSSL_free(vctx);
54
}
55
56
static int mlx_kem_init(void *vctx, int op, void *key,
57
ossl_unused const OSSL_PARAM params[])
58
{
59
PROV_MLX_KEM_CTX *ctx = vctx;
60
61
if (!ossl_prov_is_running())
62
return 0;
63
ctx->key = key;
64
ctx->op = op;
65
return 1;
66
}
67
68
static int
69
mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
70
{
71
MLX_KEY *key = vkey;
72
73
if (!mlx_kem_have_pubkey(key)) {
74
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
75
return 0;
76
}
77
return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
78
}
79
80
static int
81
mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
82
{
83
MLX_KEY *key = vkey;
84
85
if (!mlx_kem_have_prvkey(key)) {
86
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
87
return 0;
88
}
89
return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
90
}
91
92
static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx,
93
ossl_unused void *provctx)
94
{
95
static const OSSL_PARAM params[] = { OSSL_PARAM_END };
96
97
return params;
98
}
99
100
static int
101
mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
102
{
103
return 1;
104
}
105
106
static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
107
unsigned char *shsec, size_t *slen)
108
{
109
MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;
110
EVP_PKEY_CTX *ctx = NULL;
111
EVP_PKEY *xkey = NULL;
112
size_t encap_clen;
113
size_t encap_slen;
114
uint8_t *cbuf;
115
uint8_t *sbuf;
116
int ml_kem_slot = key->xinfo->ml_kem_slot;
117
int ret = 0;
118
119
if (!mlx_kem_have_pubkey(key)) {
120
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
121
goto end;
122
}
123
encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
124
encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
125
126
if (ctext == NULL) {
127
if (clen == NULL && slen == NULL)
128
return 0;
129
if (clen != NULL)
130
*clen = encap_clen;
131
if (slen != NULL)
132
*slen = encap_slen;
133
return 1;
134
}
135
if (shsec == NULL) {
136
ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
137
"null shared-secret output buffer");
138
return 0;
139
}
140
141
if (clen == NULL) {
142
ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
143
"null ciphertext input/output length pointer");
144
return 0;
145
} else if (*clen < encap_clen) {
146
ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
147
"ciphertext buffer too small");
148
return 0;
149
} else {
150
*clen = encap_clen;
151
}
152
153
if (slen == NULL) {
154
ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
155
"null shared secret input/output length pointer");
156
return 0;
157
} else if (*slen < encap_slen) {
158
ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
159
"shared-secret buffer too small");
160
return 0;
161
} else {
162
*slen = encap_slen;
163
}
164
165
/* ML-KEM encapsulation */
166
encap_clen = key->minfo->ctext_bytes;
167
encap_slen = ML_KEM_SHARED_SECRET_BYTES;
168
cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
169
sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
170
ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
171
if (ctx == NULL
172
|| EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
173
|| EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)
174
goto end;
175
if (encap_clen != key->minfo->ctext_bytes) {
176
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
177
"unexpected %s ciphertext output size: %lu",
178
key->minfo->algorithm_name, (unsigned long)encap_clen);
179
goto end;
180
}
181
if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) {
182
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
183
"unexpected %s shared secret output size: %lu",
184
key->minfo->algorithm_name, (unsigned long)encap_slen);
185
goto end;
186
}
187
EVP_PKEY_CTX_free(ctx);
188
189
/*-
190
* ECDHE encapsulation
191
*
192
* Generate own ephemeral private key and add its public key to ctext.
193
*
194
* Note, we could support a settable parameter that sets an extant ECDH
195
* keypair as the keys to use in encap, making it possible to reuse the
196
* same (TLS client) ECDHE keypair for both the classical EC keyshare and a
197
* corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need
198
* know that this is a hybrid, and that it can partly reuse the same keys
199
* as another group for which a keyshare will be sent. Deferred until we
200
* support generating multiple keyshares, there's a workable keyshare
201
* prediction specification, and the optimisation is justified.
202
*/
203
cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
204
encap_clen = key->xinfo->pubkey_bytes;
205
ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
206
if (ctx == NULL
207
|| EVP_PKEY_keygen_init(ctx) <= 0
208
|| EVP_PKEY_keygen(ctx, &xkey) <= 0
209
|| EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
210
cbuf, encap_clen, &encap_clen)
211
<= 0)
212
goto end;
213
if (encap_clen != key->xinfo->pubkey_bytes) {
214
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
215
"unexpected %s public key output size: %lu",
216
key->xinfo->algorithm_name, (unsigned long)encap_clen);
217
goto end;
218
}
219
EVP_PKEY_CTX_free(ctx);
220
221
/* Derive the ECDH shared secret */
222
encap_slen = key->xinfo->shsec_bytes;
223
sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
224
ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);
225
if (ctx == NULL
226
|| EVP_PKEY_derive_init(ctx) <= 0
227
|| EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0
228
|| EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)
229
goto end;
230
if (encap_slen != key->xinfo->shsec_bytes) {
231
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
232
"unexpected %s shared secret output size: %lu",
233
key->xinfo->algorithm_name, (unsigned long)encap_slen);
234
goto end;
235
}
236
237
ret = 1;
238
end:
239
EVP_PKEY_free(xkey);
240
EVP_PKEY_CTX_free(ctx);
241
return ret;
242
}
243
244
static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
245
const uint8_t *ctext, size_t clen)
246
{
247
MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;
248
EVP_PKEY_CTX *ctx = NULL;
249
EVP_PKEY *xkey = NULL;
250
const uint8_t *cbuf;
251
uint8_t *sbuf;
252
size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
253
size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
254
int ml_kem_slot = key->xinfo->ml_kem_slot;
255
int ret = 0;
256
257
if (!mlx_kem_have_prvkey(key)) {
258
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
259
return 0;
260
}
261
262
if (shsec == NULL) {
263
if (slen == NULL)
264
return 0;
265
*slen = decap_slen;
266
return 1;
267
}
268
269
/* For now tolerate newly-deprecated NULL length pointers. */
270
if (slen == NULL) {
271
slen = &decap_slen;
272
} else if (*slen < decap_slen) {
273
ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
274
"shared-secret buffer too small");
275
return 0;
276
} else {
277
*slen = decap_slen;
278
}
279
if (clen != decap_clen) {
280
ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,
281
"wrong decapsulation input ciphertext size: %lu",
282
(unsigned long)clen);
283
return 0;
284
}
285
286
/* ML-KEM decapsulation */
287
decap_clen = key->minfo->ctext_bytes;
288
decap_slen = ML_KEM_SHARED_SECRET_BYTES;
289
cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
290
sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
291
ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
292
if (ctx == NULL
293
|| EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
294
|| EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)
295
goto end;
296
if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {
297
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
298
"unexpected %s shared secret output size: %lu",
299
key->minfo->algorithm_name, (unsigned long)decap_slen);
300
goto end;
301
}
302
EVP_PKEY_CTX_free(ctx);
303
304
/* ECDH decapsulation */
305
decap_clen = key->xinfo->pubkey_bytes;
306
decap_slen = key->xinfo->shsec_bytes;
307
cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
308
sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
309
ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
310
if (ctx == NULL
311
|| (xkey = EVP_PKEY_new()) == NULL
312
|| EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0
313
|| EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0
314
|| EVP_PKEY_derive_init(ctx) <= 0
315
|| EVP_PKEY_derive_set_peer(ctx, xkey) <= 0
316
|| EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)
317
goto end;
318
if (decap_slen != key->xinfo->shsec_bytes) {
319
ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
320
"unexpected %s shared secret output size: %lu",
321
key->xinfo->algorithm_name, (unsigned long)decap_slen);
322
goto end;
323
}
324
325
ret = 1;
326
end:
327
EVP_PKEY_CTX_free(ctx);
328
EVP_PKEY_free(xkey);
329
return ret;
330
}
331
332
const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {
333
{ OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)mlx_kem_newctx },
334
{ OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)mlx_kem_encapsulate_init },
335
{ OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)mlx_kem_encapsulate },
336
{ OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)mlx_kem_decapsulate_init },
337
{ OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)mlx_kem_decapsulate },
338
{ OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)mlx_kem_freectx },
339
{ OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)mlx_kem_set_ctx_params },
340
{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)mlx_kem_settable_ctx_params },
341
OSSL_DISPATCH_END
342
};
343
344