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/err.h>
13 #include <openssl/param_build.h>
14 #include <openssl/params.h>
15 #include <openssl/proverr.h>
16 #include <openssl/rand.h>
17 #include <openssl/self_test.h>
18 #include "internal/nelem.h"
19 #include "internal/param_build_set.h"
20 #include "prov/implementations.h"
21 #include "prov/mlx_kem.h"
22 #include "prov/provider_ctx.h"
23 #include "prov/providercommon.h"
24 #include "prov/securitycheck.h"
25
26 static OSSL_FUNC_keymgmt_gen_fn mlx_kem_gen;
27 static OSSL_FUNC_keymgmt_gen_cleanup_fn mlx_kem_gen_cleanup;
28 static OSSL_FUNC_keymgmt_gen_set_params_fn mlx_kem_gen_set_params;
29 static OSSL_FUNC_keymgmt_gen_settable_params_fn mlx_kem_gen_settable_params;
30 static OSSL_FUNC_keymgmt_get_params_fn mlx_kem_get_params;
31 static OSSL_FUNC_keymgmt_gettable_params_fn mlx_kem_gettable_params;
32 static OSSL_FUNC_keymgmt_set_params_fn mlx_kem_set_params;
33 static OSSL_FUNC_keymgmt_settable_params_fn mlx_kem_settable_params;
34 static OSSL_FUNC_keymgmt_has_fn mlx_kem_has;
35 static OSSL_FUNC_keymgmt_match_fn mlx_kem_match;
36 static OSSL_FUNC_keymgmt_import_fn mlx_kem_import;
37 static OSSL_FUNC_keymgmt_export_fn mlx_kem_export;
38 static OSSL_FUNC_keymgmt_import_types_fn mlx_kem_imexport_types;
39 static OSSL_FUNC_keymgmt_export_types_fn mlx_kem_imexport_types;
40 static OSSL_FUNC_keymgmt_dup_fn mlx_kem_dup;
41
42 static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
43 | OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
44
45 /* Must match DECLARE_DISPATCH invocations at the end of the file */
46 static const ECDH_VINFO hybrid_vtable[] = {
47 { "EC", "P-256", 65, 32, 32, 1, EVP_PKEY_ML_KEM_768 },
48 { "EC", "P-384", 97, 48, 48, 1, EVP_PKEY_ML_KEM_1024 },
49 #if !defined(OPENSSL_NO_ECX)
50 { "X25519", NULL, 32, 32, 32, 0, EVP_PKEY_ML_KEM_768 },
51 { "X448", NULL, 56, 56, 56, 0, EVP_PKEY_ML_KEM_1024 },
52 #endif
53 };
54
55 typedef struct mlx_kem_gen_ctx_st {
56 OSSL_LIB_CTX *libctx;
57 char *propq;
58 int selection;
59 unsigned int evp_type;
60 } PROV_ML_KEM_GEN_CTX;
61
mlx_kem_key_free(void * vkey)62 static void mlx_kem_key_free(void *vkey)
63 {
64 MLX_KEY *key = vkey;
65
66 if (key == NULL)
67 return;
68 OPENSSL_free(key->propq);
69 EVP_PKEY_free(key->mkey);
70 EVP_PKEY_free(key->xkey);
71 OPENSSL_free(key);
72 }
73
74 /* Takes ownership of propq */
75 static void *
mlx_kem_key_new(unsigned int v,OSSL_LIB_CTX * libctx,char * propq)76 mlx_kem_key_new(unsigned int v, OSSL_LIB_CTX *libctx, char *propq)
77 {
78 MLX_KEY *key = NULL;
79 unsigned int ml_kem_variant;
80
81 if (!ossl_prov_is_running()
82 || v >= OSSL_NELEM(hybrid_vtable)
83 || (key = OPENSSL_malloc(sizeof(*key))) == NULL)
84 goto err;
85
86 ml_kem_variant = hybrid_vtable[v].ml_kem_variant;
87 key->libctx = libctx;
88 key->minfo = ossl_ml_kem_get_vinfo(ml_kem_variant);
89 key->xinfo = &hybrid_vtable[v];
90 key->xkey = key->mkey = NULL;
91 key->state = MLX_HAVE_NOKEYS;
92 key->propq = propq;
93 return key;
94
95 err:
96 OPENSSL_free(propq);
97 return NULL;
98 }
99
100
mlx_kem_has(const void * vkey,int selection)101 static int mlx_kem_has(const void *vkey, int selection)
102 {
103 const MLX_KEY *key = vkey;
104
105 /* A NULL key MUST fail to have anything */
106 if (!ossl_prov_is_running() || key == NULL)
107 return 0;
108
109 switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
110 case 0:
111 return 1;
112 case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:
113 return mlx_kem_have_pubkey(key);
114 default:
115 return mlx_kem_have_prvkey(key);
116 }
117 }
118
mlx_kem_match(const void * vkey1,const void * vkey2,int selection)119 static int mlx_kem_match(const void *vkey1, const void *vkey2, int selection)
120 {
121 const MLX_KEY *key1 = vkey1;
122 const MLX_KEY *key2 = vkey2;
123 int have_pub1 = mlx_kem_have_pubkey(key1);
124 int have_pub2 = mlx_kem_have_pubkey(key2);
125
126 if (!ossl_prov_is_running())
127 return 0;
128
129 /* Compare domain parameters */
130 if (key1->xinfo != key2->xinfo)
131 return 0;
132
133 if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR))
134 return 1;
135
136 if (have_pub1 ^ have_pub2)
137 return 0;
138
139 /* As in other providers, equal when both have no key material. */
140 if (!have_pub1)
141 return 1;
142
143 return EVP_PKEY_eq(key1->mkey, key2->mkey)
144 && EVP_PKEY_eq(key1->xkey, key2->xkey);
145 }
146
147 typedef struct export_cb_arg_st {
148 const char *algorithm_name;
149 uint8_t *pubenc;
150 uint8_t *prvenc;
151 int pubcount;
152 int prvcount;
153 size_t puboff;
154 size_t prvoff;
155 size_t publen;
156 size_t prvlen;
157 } EXPORT_CB_ARG;
158
159 /* Copy any exported key material into its storage slot */
export_sub_cb(const OSSL_PARAM * params,void * varg)160 static int export_sub_cb(const OSSL_PARAM *params, void *varg)
161 {
162 EXPORT_CB_ARG *sub_arg = varg;
163 const OSSL_PARAM *p = NULL;
164 size_t len;
165
166 /*
167 * The caller will decide whether anything essential is missing, but, if
168 * some key material was returned, it should have the right (parameter)
169 * data type and length.
170 */
171 if (ossl_param_is_empty(params))
172 return 1;
173 if (sub_arg->pubenc != NULL
174 && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) {
175 void *pub = sub_arg->pubenc + sub_arg->puboff;
176
177 if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1)
178 return 0;
179 if (len != sub_arg->publen) {
180 ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
181 "Unexpected %s public key length %lu != %lu",
182 sub_arg->algorithm_name, (unsigned long) len,
183 sub_arg->publen);
184 return 0;
185 }
186 ++sub_arg->pubcount;
187 }
188 if (sub_arg->prvenc != NULL
189 && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) {
190 void *prv = sub_arg->prvenc + sub_arg->prvoff;
191
192 if (OSSL_PARAM_get_octet_string(p, &prv, sub_arg->prvlen, &len) != 1)
193 return 0;
194 if (len != sub_arg->prvlen) {
195 ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
196 "Unexpected %s private key length %lu != %lu",
197 sub_arg->algorithm_name, (unsigned long) len,
198 (unsigned long) sub_arg->publen);
199 return 0;
200 }
201 ++sub_arg->prvcount;
202 }
203 return 1;
204 }
205
206 static int
export_sub(EXPORT_CB_ARG * sub_arg,int selection,MLX_KEY * key)207 export_sub(EXPORT_CB_ARG *sub_arg, int selection, MLX_KEY *key)
208 {
209 int slot;
210
211 /*
212 * The caller is responsible for initialising only the pubenc and prvenc
213 * pointer fields, the rest are set here or in the callback.
214 */
215 sub_arg->pubcount = 0;
216 sub_arg->prvcount = 0;
217
218 for (slot = 0; slot < 2; ++slot) {
219 int ml_kem_slot = key->xinfo->ml_kem_slot;
220 EVP_PKEY *pkey;
221
222 /* Export the parts of each component into its storage slot */
223 if (slot == ml_kem_slot) {
224 pkey = key->mkey;
225 sub_arg->algorithm_name = key->minfo->algorithm_name;
226 sub_arg->puboff = slot * key->xinfo->pubkey_bytes;
227 sub_arg->prvoff = slot * key->xinfo->prvkey_bytes;
228 sub_arg->publen = key->minfo->pubkey_bytes;
229 sub_arg->prvlen = key->minfo->prvkey_bytes;
230 } else {
231 pkey = key->xkey;
232 sub_arg->algorithm_name = key->xinfo->algorithm_name;
233 sub_arg->puboff = (1 - ml_kem_slot) * key->minfo->pubkey_bytes;
234 sub_arg->prvoff = (1 - ml_kem_slot) * key->minfo->prvkey_bytes;
235 sub_arg->publen = key->xinfo->pubkey_bytes;
236 sub_arg->prvlen = key->xinfo->prvkey_bytes;
237 }
238 if (!EVP_PKEY_export(pkey, selection, export_sub_cb, (void *)sub_arg))
239 return 0;
240 }
241 return 1;
242 }
243
mlx_kem_export(void * vkey,int selection,OSSL_CALLBACK * param_cb,void * cbarg)244 static int mlx_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
245 void *cbarg)
246 {
247 MLX_KEY *key = vkey;
248 OSSL_PARAM_BLD *tmpl = NULL;
249 OSSL_PARAM *params = NULL;
250 size_t publen;
251 size_t prvlen;
252 int ret = 0;
253 EXPORT_CB_ARG sub_arg;
254
255 if (!ossl_prov_is_running() || key == NULL)
256 return 0;
257
258 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
259 return 0;
260
261 /* Fail when no key material has yet been provided */
262 if (!mlx_kem_have_pubkey(key)) {
263 ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
264 return 0;
265 }
266 publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
267 prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
268 memset(&sub_arg, 0, sizeof(sub_arg));
269
270 if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
271 sub_arg.pubenc = OPENSSL_malloc(publen);
272 if (sub_arg.pubenc == NULL)
273 goto err;
274 }
275
276 if (mlx_kem_have_prvkey(key)
277 && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
278 /*
279 * Allocated on the secure heap if configured, this is detected in
280 * ossl_param_build_set_octet_string(), which will then also use the
281 * secure heap.
282 */
283 sub_arg.prvenc = OPENSSL_secure_zalloc(prvlen);
284 if (sub_arg.prvenc == NULL)
285 goto err;
286 }
287
288 tmpl = OSSL_PARAM_BLD_new();
289 if (tmpl == NULL)
290 goto err;
291
292 /* Extract sub-component key material */
293 if (!export_sub(&sub_arg, selection, key))
294 goto err;
295
296 if (sub_arg.pubenc != NULL && sub_arg.pubcount == 2
297 && !ossl_param_build_set_octet_string(
298 tmpl, NULL, OSSL_PKEY_PARAM_PUB_KEY, sub_arg.pubenc, publen))
299 goto err;
300
301 if (sub_arg.prvenc != NULL && sub_arg.prvcount == 2
302 && !ossl_param_build_set_octet_string(
303 tmpl, NULL, OSSL_PKEY_PARAM_PRIV_KEY, sub_arg.prvenc, prvlen))
304 goto err;
305
306 params = OSSL_PARAM_BLD_to_param(tmpl);
307 if (params == NULL)
308 goto err;
309
310 ret = param_cb(params, cbarg);
311 OSSL_PARAM_free(params);
312
313 err:
314 OSSL_PARAM_BLD_free(tmpl);
315 OPENSSL_secure_clear_free(sub_arg.prvenc, prvlen);
316 OPENSSL_free(sub_arg.pubenc);
317 return ret;
318 }
319
mlx_kem_imexport_types(int selection)320 static const OSSL_PARAM *mlx_kem_imexport_types(int selection)
321 {
322 static const OSSL_PARAM key_types[] = {
323 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
324 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
325 OSSL_PARAM_END
326 };
327
328 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
329 return key_types;
330 return NULL;
331 }
332
333 static int
load_slot(OSSL_LIB_CTX * libctx,const char * propq,const char * pname,int selection,MLX_KEY * key,int slot,const uint8_t * in,int mbytes,int xbytes)334 load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,
335 int selection, MLX_KEY *key, int slot, const uint8_t *in,
336 int mbytes, int xbytes)
337 {
338 EVP_PKEY_CTX *ctx;
339 EVP_PKEY **ppkey;
340 OSSL_PARAM parr[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
341 const char *alg;
342 char *group = NULL;
343 size_t off, len;
344 void *val;
345 int ml_kem_slot = key->xinfo->ml_kem_slot;
346 int ret = 0;
347
348 if (slot == ml_kem_slot) {
349 alg = key->minfo->algorithm_name;
350 ppkey = &key->mkey;
351 off = slot * xbytes;
352 len = mbytes;
353 } else {
354 alg = key->xinfo->algorithm_name;
355 group = (char *) key->xinfo->group_name;
356 ppkey = &key->xkey;
357 off = (1 - ml_kem_slot) * mbytes;
358 len = xbytes;
359 }
360 val = (void *)(in + off);
361
362 if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL
363 || EVP_PKEY_fromdata_init(ctx) <= 0)
364 goto err;
365 parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len);
366 if (group != NULL)
367 parr[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
368 group, 0);
369 if (EVP_PKEY_fromdata(ctx, ppkey, selection, parr) > 0)
370 ret = 1;
371
372 err:
373 EVP_PKEY_CTX_free(ctx);
374 return ret;
375 }
376
377 static int
load_keys(MLX_KEY * key,const uint8_t * pubenc,size_t publen,const uint8_t * prvenc,size_t prvlen)378 load_keys(MLX_KEY *key,
379 const uint8_t *pubenc, size_t publen,
380 const uint8_t *prvenc, size_t prvlen)
381 {
382 int slot;
383
384 for (slot = 0; slot < 2; ++slot) {
385 if (prvlen) {
386 /* Ignore public keys when private provided */
387 if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PRIV_KEY,
388 minimal_selection, key, slot, prvenc,
389 key->minfo->prvkey_bytes, key->xinfo->prvkey_bytes))
390 goto err;
391 } else if (publen) {
392 /* Absent private key data, import public keys */
393 if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PUB_KEY,
394 minimal_selection, key, slot, pubenc,
395 key->minfo->pubkey_bytes, key->xinfo->pubkey_bytes))
396 goto err;
397 }
398 }
399 key->state = prvlen ? MLX_HAVE_PRVKEY : MLX_HAVE_PUBKEY;
400 return 1;
401
402 err:
403 EVP_PKEY_free(key->mkey);
404 EVP_PKEY_free(key->xkey);
405 key->xkey = key->mkey = NULL;
406 key->state = MLX_HAVE_NOKEYS;
407 return 0;
408 }
409
mlx_kem_key_fromdata(MLX_KEY * key,const OSSL_PARAM params[],int include_private)410 static int mlx_kem_key_fromdata(MLX_KEY *key,
411 const OSSL_PARAM params[],
412 int include_private)
413 {
414 const OSSL_PARAM *param_prv_key = NULL, *param_pub_key;
415 const void *pubenc = NULL, *prvenc = NULL;
416 size_t pubkey_bytes, prvkey_bytes;
417 size_t publen = 0, prvlen = 0;
418
419 /* Invalid attempt to mutate a key, what is the right error to report? */
420 if (key == NULL || mlx_kem_have_pubkey(key))
421 return 0;
422 pubkey_bytes = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
423 prvkey_bytes = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
424
425 /* What does the caller want to set? */
426 param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
427 if (param_pub_key != NULL &&
428 OSSL_PARAM_get_octet_string_ptr(param_pub_key, &pubenc, &publen) != 1)
429 return 0;
430 if (include_private)
431 param_prv_key = OSSL_PARAM_locate_const(params,
432 OSSL_PKEY_PARAM_PRIV_KEY);
433 if (param_prv_key != NULL &&
434 OSSL_PARAM_get_octet_string_ptr(param_prv_key, &prvenc, &prvlen) != 1)
435 return 0;
436
437 /* The caller MUST specify at least one of the public or private keys. */
438 if (publen == 0 && prvlen == 0) {
439 ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
440 return 0;
441 }
442
443 /*
444 * When a pubkey is provided, its length MUST be correct, if a private key
445 * is also provided, the public key will be otherwise ignored. We could
446 * look for a matching encoded block, but unclear this is useful.
447 */
448 if (publen != 0 && publen != pubkey_bytes) {
449 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
450 return 0;
451 }
452 if (prvlen != 0 && prvlen != prvkey_bytes) {
453 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
454 return 0;
455 }
456
457 return load_keys(key, pubenc, publen, prvenc, prvlen);
458 }
459
mlx_kem_import(void * vkey,int selection,const OSSL_PARAM params[])460 static int mlx_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
461 {
462 MLX_KEY *key = vkey;
463 int include_private;
464
465 if (!ossl_prov_is_running() || key == NULL)
466 return 0;
467
468 if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
469 return 0;
470
471 include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
472 return mlx_kem_key_fromdata(key, params, include_private);
473 }
474
mlx_kem_gettable_params(void * provctx)475 static const OSSL_PARAM *mlx_kem_gettable_params(void *provctx)
476 {
477 static const OSSL_PARAM arr[] = {
478 OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
479 OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
480 OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
481 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
482 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
483 OSSL_PARAM_END
484 };
485
486 return arr;
487 }
488
489 /*
490 * It is assumed the key is guaranteed non-NULL here, and is from this provider
491 */
mlx_kem_get_params(void * vkey,OSSL_PARAM params[])492 static int mlx_kem_get_params(void *vkey, OSSL_PARAM params[])
493 {
494 MLX_KEY *key = vkey;
495 OSSL_PARAM *p, *pub, *prv = NULL;
496 EXPORT_CB_ARG sub_arg;
497 int selection;
498 size_t publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
499 size_t prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
500
501 /* The reported "bit" count is those of the ML-KEM key */
502 p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS);
503 if (p != NULL)
504 if (!OSSL_PARAM_set_int(p, key->minfo->bits))
505 return 0;
506
507 /* The reported security bits are those of the ML-KEM key */
508 p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS);
509 if (p != NULL)
510 if (!OSSL_PARAM_set_int(p, key->minfo->secbits))
511 return 0;
512
513 /* The ciphertext sizes are additive */
514 p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE);
515 if (p != NULL)
516 if (!OSSL_PARAM_set_int(p, key->minfo->ctext_bytes + key->xinfo->pubkey_bytes))
517 return 0;
518
519 if (!mlx_kem_have_pubkey(key))
520 return 1;
521
522 memset(&sub_arg, 0, sizeof(sub_arg));
523 pub = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
524 if (pub != NULL) {
525 if (pub->data_type != OSSL_PARAM_OCTET_STRING)
526 return 0;
527 pub->return_size = publen;
528 if (pub->data == NULL) {
529 pub = NULL;
530 } else if (pub->data_size < publen) {
531 ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
532 "public key output buffer too short: %lu < %lu",
533 (unsigned long) pub->data_size,
534 (unsigned long) publen);
535 return 0;
536 } else {
537 sub_arg.pubenc = pub->data;
538 }
539 }
540 if (mlx_kem_have_prvkey(key)) {
541 prv = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
542 if (prv != NULL) {
543 if (prv->data_type != OSSL_PARAM_OCTET_STRING)
544 return 0;
545 prv->return_size = prvlen;
546 if (prv->data == NULL) {
547 prv = NULL;
548 } else if (prv->data_size < prvlen) {
549 ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
550 "private key output buffer too short: %lu < %lu",
551 (unsigned long) prv->data_size,
552 (unsigned long) prvlen);
553 return 0;
554 } else {
555 sub_arg.prvenc = prv->data;
556 }
557 }
558 }
559 if (pub == NULL && prv == NULL)
560 return 1;
561
562 selection = prv == NULL ? 0 : OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
563 selection |= pub == NULL ? 0 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
564 if (key->xinfo->group_name != NULL)
565 selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
566
567 /* Extract sub-component key material */
568 if (!export_sub(&sub_arg, selection, key))
569 return 0;
570
571 if ((pub != NULL && sub_arg.pubcount != 2)
572 || (prv != NULL && sub_arg.prvcount != 2))
573 return 0;
574
575 return 1;
576 }
577
mlx_kem_settable_params(void * provctx)578 static const OSSL_PARAM *mlx_kem_settable_params(void *provctx)
579 {
580 static const OSSL_PARAM arr[] = {
581 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
582 OSSL_PARAM_END
583 };
584
585 return arr;
586 }
587
mlx_kem_set_params(void * vkey,const OSSL_PARAM params[])588 static int mlx_kem_set_params(void *vkey, const OSSL_PARAM params[])
589 {
590 MLX_KEY *key = vkey;
591 const OSSL_PARAM *p;
592 const void *pubenc = NULL;
593 size_t publen = 0;
594
595 if (ossl_param_is_empty(params))
596 return 1;
597
598 /* Only one settable parameter is supported */
599 p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
600 if (p == NULL)
601 return 1;
602
603 /* Key mutation is reportedly generally not allowed */
604 if (mlx_kem_have_pubkey(key)) {
605 ERR_raise_data(ERR_LIB_PROV,
606 PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
607 "keys cannot be mutated");
608 return 0;
609 }
610 /* An unlikely failure mode is the parameter having some unexpected type */
611 if (!OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen))
612 return 0;
613
614 p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
615 if (p != NULL) {
616 OPENSSL_free(key->propq);
617 key->propq = NULL;
618 if (!OSSL_PARAM_get_utf8_string(p, &key->propq, 0))
619 return 0;
620 }
621
622 if (publen != key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes) {
623 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
624 return 0;
625 }
626
627 return load_keys(key, pubenc, publen, NULL, 0);
628 }
629
mlx_kem_gen_set_params(void * vgctx,const OSSL_PARAM params[])630 static int mlx_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])
631 {
632 PROV_ML_KEM_GEN_CTX *gctx = vgctx;
633 const OSSL_PARAM *p;
634
635 if (gctx == NULL)
636 return 0;
637 if (ossl_param_is_empty(params))
638 return 1;
639
640 p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
641 if (p != NULL) {
642 if (p->data_type != OSSL_PARAM_UTF8_STRING)
643 return 0;
644 OPENSSL_free(gctx->propq);
645 if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL)
646 return 0;
647 }
648 return 1;
649 }
650
mlx_kem_gen_init(int evp_type,OSSL_LIB_CTX * libctx,int selection,const OSSL_PARAM params[])651 static void *mlx_kem_gen_init(int evp_type, OSSL_LIB_CTX *libctx,
652 int selection, const OSSL_PARAM params[])
653 {
654 PROV_ML_KEM_GEN_CTX *gctx = NULL;
655
656 /*
657 * We can only generate private keys, check that the selection is
658 * appropriate.
659 */
660 if (!ossl_prov_is_running()
661 || (selection & minimal_selection) == 0
662 || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL)
663 return NULL;
664
665 gctx->evp_type = evp_type;
666 gctx->libctx = libctx;
667 gctx->selection = selection;
668 if (mlx_kem_gen_set_params(gctx, params))
669 return gctx;
670
671 mlx_kem_gen_cleanup(gctx);
672 return NULL;
673 }
674
mlx_kem_gen_settable_params(ossl_unused void * vgctx,ossl_unused void * provctx)675 static const OSSL_PARAM *mlx_kem_gen_settable_params(ossl_unused void *vgctx,
676 ossl_unused void *provctx)
677 {
678 static OSSL_PARAM settable[] = {
679 OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
680 OSSL_PARAM_END
681 };
682
683 return settable;
684 }
685
mlx_kem_gen(void * vgctx,OSSL_CALLBACK * osslcb,void * cbarg)686 static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
687 {
688 PROV_ML_KEM_GEN_CTX *gctx = vgctx;
689 MLX_KEY *key;
690 char *propq;
691
692 if (gctx == NULL
693 || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
694 OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
695 return NULL;
696
697 /* Lose ownership of propq */
698 propq = gctx->propq;
699 gctx->propq = NULL;
700 if ((key = mlx_kem_key_new(gctx->evp_type, gctx->libctx, propq)) == NULL)
701 return NULL;
702
703 if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
704 return key;
705
706 /* For now, using the same "propq" for all components */
707 key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
708 key->minfo->algorithm_name);
709 key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
710 key->xinfo->algorithm_name,
711 key->xinfo->group_name);
712 if (key->mkey != NULL && key->xkey != NULL) {
713 key->state = MLX_HAVE_PRVKEY;
714 return key;
715 }
716
717 mlx_kem_key_free(key);
718 return NULL;
719 }
720
mlx_kem_gen_cleanup(void * vgctx)721 static void mlx_kem_gen_cleanup(void *vgctx)
722 {
723 PROV_ML_KEM_GEN_CTX *gctx = vgctx;
724
725 if (gctx == NULL)
726 return;
727 OPENSSL_free(gctx->propq);
728 OPENSSL_free(gctx);
729 }
730
mlx_kem_dup(const void * vkey,int selection)731 static void *mlx_kem_dup(const void *vkey, int selection)
732 {
733 const MLX_KEY *key = vkey;
734 MLX_KEY *ret;
735
736 if (!ossl_prov_is_running()
737 || (ret = OPENSSL_memdup(key, sizeof(*ret))) == NULL)
738 return NULL;
739
740 if (ret->propq != NULL
741 && (ret->propq = OPENSSL_strdup(ret->propq)) == NULL) {
742 OPENSSL_free(ret);
743 return NULL;
744 }
745
746 /* Absent key material, nothing left to do */
747 if (ret->mkey == NULL) {
748 if (ret->xkey == NULL)
749 return ret;
750 /* Fail if the source key is an inconsistent state */
751 OPENSSL_free(ret);
752 return NULL;
753 }
754
755 switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
756 case 0:
757 ret->xkey = ret->mkey = NULL;
758 return ret;
759 case OSSL_KEYMGMT_SELECT_KEYPAIR:
760 ret->mkey = EVP_PKEY_dup(key->mkey);
761 ret->xkey = EVP_PKEY_dup(key->xkey);
762 if (ret->xkey != NULL && ret->mkey != NULL)
763 return ret;
764 break;
765 default:
766 ERR_raise_data(ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION,
767 "duplication of partial key material not supported");
768 break;
769 }
770
771 mlx_kem_key_free(ret);
772 return NULL;
773 }
774
775 #define DECLARE_DISPATCH(name, variant) \
776 static OSSL_FUNC_keymgmt_new_fn mlx_##name##_kem_new; \
777 static void *mlx_##name##_kem_new(void *provctx) \
778 { \
779 OSSL_LIB_CTX *libctx; \
780 \
781 libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \
782 return mlx_kem_key_new(variant, libctx, NULL); \
783 } \
784 static OSSL_FUNC_keymgmt_gen_init_fn mlx_##name##_kem_gen_init; \
785 static void *mlx_##name##_kem_gen_init(void *provctx, int selection, \
786 const OSSL_PARAM params[]) \
787 { \
788 OSSL_LIB_CTX *libctx; \
789 \
790 libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \
791 return mlx_kem_gen_init(variant, libctx, selection, params); \
792 } \
793 const OSSL_DISPATCH ossl_mlx_##name##_kem_kmgmt_functions[] = { \
794 { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) mlx_##name##_kem_new }, \
795 { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) mlx_kem_key_free }, \
796 { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) mlx_kem_get_params }, \
797 { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gettable_params }, \
798 { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) mlx_kem_set_params }, \
799 { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_settable_params }, \
800 { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) mlx_kem_has }, \
801 { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) mlx_kem_match }, \
802 { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) mlx_##name##_kem_gen_init }, \
803 { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) mlx_kem_gen_set_params }, \
804 { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gen_settable_params }, \
805 { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) mlx_kem_gen }, \
806 { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) mlx_kem_gen_cleanup }, \
807 { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) mlx_kem_dup }, \
808 { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) mlx_kem_import }, \
809 { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \
810 { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) mlx_kem_export }, \
811 { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \
812 OSSL_DISPATCH_END \
813 }
814 /* See |hybrid_vtable| above */
815 DECLARE_DISPATCH(p256, 0);
816 DECLARE_DISPATCH(p384, 1);
817 #if !defined(OPENSSL_NO_ECX)
818 DECLARE_DISPATCH(x25519, 2);
819 DECLARE_DISPATCH(x448, 3);
820 #endif
821