Path: blob/main/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c
48292 views
/*1* Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include <openssl/core_dispatch.h>10#include <openssl/core_names.h>11#include <openssl/err.h>12#include <openssl/param_build.h>13#include <openssl/params.h>14#include <openssl/proverr.h>15#include <openssl/rand.h>16#include <openssl/self_test.h>17#include "internal/nelem.h"18#include "internal/param_build_set.h"19#include "prov/implementations.h"20#include "prov/mlx_kem.h"21#include "prov/provider_ctx.h"22#include "prov/providercommon.h"23#include "prov/securitycheck.h"2425static OSSL_FUNC_keymgmt_gen_fn mlx_kem_gen;26static OSSL_FUNC_keymgmt_gen_cleanup_fn mlx_kem_gen_cleanup;27static OSSL_FUNC_keymgmt_gen_set_params_fn mlx_kem_gen_set_params;28static OSSL_FUNC_keymgmt_gen_settable_params_fn mlx_kem_gen_settable_params;29static OSSL_FUNC_keymgmt_get_params_fn mlx_kem_get_params;30static OSSL_FUNC_keymgmt_gettable_params_fn mlx_kem_gettable_params;31static OSSL_FUNC_keymgmt_set_params_fn mlx_kem_set_params;32static OSSL_FUNC_keymgmt_settable_params_fn mlx_kem_settable_params;33static OSSL_FUNC_keymgmt_has_fn mlx_kem_has;34static OSSL_FUNC_keymgmt_match_fn mlx_kem_match;35static OSSL_FUNC_keymgmt_import_fn mlx_kem_import;36static OSSL_FUNC_keymgmt_export_fn mlx_kem_export;37static OSSL_FUNC_keymgmt_import_types_fn mlx_kem_imexport_types;38static OSSL_FUNC_keymgmt_export_types_fn mlx_kem_imexport_types;39static OSSL_FUNC_keymgmt_dup_fn mlx_kem_dup;4041static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS42| OSSL_KEYMGMT_SELECT_PRIVATE_KEY;4344/* Must match DECLARE_DISPATCH invocations at the end of the file */45static const ECDH_VINFO hybrid_vtable[] = {46{ "EC", "P-256", 65, 32, 32, 1, EVP_PKEY_ML_KEM_768 },47{ "EC", "P-384", 97, 48, 48, 1, EVP_PKEY_ML_KEM_1024 },48#if !defined(OPENSSL_NO_ECX)49{ "X25519", NULL, 32, 32, 32, 0, EVP_PKEY_ML_KEM_768 },50{ "X448", NULL, 56, 56, 56, 0, EVP_PKEY_ML_KEM_1024 },51#endif52};5354typedef struct mlx_kem_gen_ctx_st {55OSSL_LIB_CTX *libctx;56char *propq;57int selection;58unsigned int evp_type;59} PROV_ML_KEM_GEN_CTX;6061static void mlx_kem_key_free(void *vkey)62{63MLX_KEY *key = vkey;6465if (key == NULL)66return;67OPENSSL_free(key->propq);68EVP_PKEY_free(key->mkey);69EVP_PKEY_free(key->xkey);70OPENSSL_free(key);71}7273/* Takes ownership of propq */74static void *75mlx_kem_key_new(unsigned int v, OSSL_LIB_CTX *libctx, char *propq)76{77MLX_KEY *key = NULL;78unsigned int ml_kem_variant;7980if (!ossl_prov_is_running()81|| v >= OSSL_NELEM(hybrid_vtable)82|| (key = OPENSSL_malloc(sizeof(*key))) == NULL)83goto err;8485ml_kem_variant = hybrid_vtable[v].ml_kem_variant;86key->libctx = libctx;87key->minfo = ossl_ml_kem_get_vinfo(ml_kem_variant);88key->xinfo = &hybrid_vtable[v];89key->xkey = key->mkey = NULL;90key->state = MLX_HAVE_NOKEYS;91key->propq = propq;92return key;9394err:95OPENSSL_free(propq);96return NULL;97}9899100static int mlx_kem_has(const void *vkey, int selection)101{102const MLX_KEY *key = vkey;103104/* A NULL key MUST fail to have anything */105if (!ossl_prov_is_running() || key == NULL)106return 0;107108switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {109case 0:110return 1;111case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:112return mlx_kem_have_pubkey(key);113default:114return mlx_kem_have_prvkey(key);115}116}117118static int mlx_kem_match(const void *vkey1, const void *vkey2, int selection)119{120const MLX_KEY *key1 = vkey1;121const MLX_KEY *key2 = vkey2;122int have_pub1 = mlx_kem_have_pubkey(key1);123int have_pub2 = mlx_kem_have_pubkey(key2);124125if (!ossl_prov_is_running())126return 0;127128/* Compare domain parameters */129if (key1->xinfo != key2->xinfo)130return 0;131132if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR))133return 1;134135if (have_pub1 ^ have_pub2)136return 0;137138/* As in other providers, equal when both have no key material. */139if (!have_pub1)140return 1;141142return EVP_PKEY_eq(key1->mkey, key2->mkey)143&& EVP_PKEY_eq(key1->xkey, key2->xkey);144}145146typedef struct export_cb_arg_st {147const char *algorithm_name;148uint8_t *pubenc;149uint8_t *prvenc;150int pubcount;151int prvcount;152size_t puboff;153size_t prvoff;154size_t publen;155size_t prvlen;156} EXPORT_CB_ARG;157158/* Copy any exported key material into its storage slot */159static int export_sub_cb(const OSSL_PARAM *params, void *varg)160{161EXPORT_CB_ARG *sub_arg = varg;162const OSSL_PARAM *p = NULL;163size_t len;164165/*166* The caller will decide whether anything essential is missing, but, if167* some key material was returned, it should have the right (parameter)168* data type and length.169*/170if (ossl_param_is_empty(params))171return 1;172if (sub_arg->pubenc != NULL173&& (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) {174void *pub = sub_arg->pubenc + sub_arg->puboff;175176if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1)177return 0;178if (len != sub_arg->publen) {179ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,180"Unexpected %s public key length %lu != %lu",181sub_arg->algorithm_name, (unsigned long) len,182sub_arg->publen);183return 0;184}185++sub_arg->pubcount;186}187if (sub_arg->prvenc != NULL188&& (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) {189void *prv = sub_arg->prvenc + sub_arg->prvoff;190191if (OSSL_PARAM_get_octet_string(p, &prv, sub_arg->prvlen, &len) != 1)192return 0;193if (len != sub_arg->prvlen) {194ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,195"Unexpected %s private key length %lu != %lu",196sub_arg->algorithm_name, (unsigned long) len,197(unsigned long) sub_arg->publen);198return 0;199}200++sub_arg->prvcount;201}202return 1;203}204205static int206export_sub(EXPORT_CB_ARG *sub_arg, int selection, MLX_KEY *key)207{208int slot;209210/*211* The caller is responsible for initialising only the pubenc and prvenc212* pointer fields, the rest are set here or in the callback.213*/214sub_arg->pubcount = 0;215sub_arg->prvcount = 0;216217for (slot = 0; slot < 2; ++slot) {218int ml_kem_slot = key->xinfo->ml_kem_slot;219EVP_PKEY *pkey;220221/* Export the parts of each component into its storage slot */222if (slot == ml_kem_slot) {223pkey = key->mkey;224sub_arg->algorithm_name = key->minfo->algorithm_name;225sub_arg->puboff = slot * key->xinfo->pubkey_bytes;226sub_arg->prvoff = slot * key->xinfo->prvkey_bytes;227sub_arg->publen = key->minfo->pubkey_bytes;228sub_arg->prvlen = key->minfo->prvkey_bytes;229} else {230pkey = key->xkey;231sub_arg->algorithm_name = key->xinfo->algorithm_name;232sub_arg->puboff = (1 - ml_kem_slot) * key->minfo->pubkey_bytes;233sub_arg->prvoff = (1 - ml_kem_slot) * key->minfo->prvkey_bytes;234sub_arg->publen = key->xinfo->pubkey_bytes;235sub_arg->prvlen = key->xinfo->prvkey_bytes;236}237if (!EVP_PKEY_export(pkey, selection, export_sub_cb, (void *)sub_arg))238return 0;239}240return 1;241}242243static int mlx_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,244void *cbarg)245{246MLX_KEY *key = vkey;247OSSL_PARAM_BLD *tmpl = NULL;248OSSL_PARAM *params = NULL;249size_t publen;250size_t prvlen;251int ret = 0;252EXPORT_CB_ARG sub_arg;253254if (!ossl_prov_is_running() || key == NULL)255return 0;256257if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)258return 0;259260/* Fail when no key material has yet been provided */261if (!mlx_kem_have_pubkey(key)) {262ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);263return 0;264}265publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;266prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;267memset(&sub_arg, 0, sizeof(sub_arg));268269if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {270sub_arg.pubenc = OPENSSL_malloc(publen);271if (sub_arg.pubenc == NULL)272goto err;273}274275if (mlx_kem_have_prvkey(key)276&& (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {277/*278* Allocated on the secure heap if configured, this is detected in279* ossl_param_build_set_octet_string(), which will then also use the280* secure heap.281*/282sub_arg.prvenc = OPENSSL_secure_zalloc(prvlen);283if (sub_arg.prvenc == NULL)284goto err;285}286287tmpl = OSSL_PARAM_BLD_new();288if (tmpl == NULL)289goto err;290291/* Extract sub-component key material */292if (!export_sub(&sub_arg, selection, key))293goto err;294295if (sub_arg.pubenc != NULL && sub_arg.pubcount == 2296&& !ossl_param_build_set_octet_string(297tmpl, NULL, OSSL_PKEY_PARAM_PUB_KEY, sub_arg.pubenc, publen))298goto err;299300if (sub_arg.prvenc != NULL && sub_arg.prvcount == 2301&& !ossl_param_build_set_octet_string(302tmpl, NULL, OSSL_PKEY_PARAM_PRIV_KEY, sub_arg.prvenc, prvlen))303goto err;304305params = OSSL_PARAM_BLD_to_param(tmpl);306if (params == NULL)307goto err;308309ret = param_cb(params, cbarg);310OSSL_PARAM_free(params);311312err:313OSSL_PARAM_BLD_free(tmpl);314OPENSSL_secure_clear_free(sub_arg.prvenc, prvlen);315OPENSSL_free(sub_arg.pubenc);316return ret;317}318319static const OSSL_PARAM *mlx_kem_imexport_types(int selection)320{321static const OSSL_PARAM key_types[] = {322OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),323OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),324OSSL_PARAM_END325};326327if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)328return key_types;329return NULL;330}331332static int333load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,334int selection, MLX_KEY *key, int slot, const uint8_t *in,335int mbytes, int xbytes)336{337EVP_PKEY_CTX *ctx;338EVP_PKEY **ppkey;339OSSL_PARAM parr[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };340const char *alg;341char *group = NULL;342size_t off, len;343void *val;344int ml_kem_slot = key->xinfo->ml_kem_slot;345int ret = 0;346347if (slot == ml_kem_slot) {348alg = key->minfo->algorithm_name;349ppkey = &key->mkey;350off = slot * xbytes;351len = mbytes;352} else {353alg = key->xinfo->algorithm_name;354group = (char *) key->xinfo->group_name;355ppkey = &key->xkey;356off = (1 - ml_kem_slot) * mbytes;357len = xbytes;358}359val = (void *)(in + off);360361if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL362|| EVP_PKEY_fromdata_init(ctx) <= 0)363goto err;364parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len);365if (group != NULL)366parr[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,367group, 0);368if (EVP_PKEY_fromdata(ctx, ppkey, selection, parr) > 0)369ret = 1;370371err:372EVP_PKEY_CTX_free(ctx);373return ret;374}375376static int377load_keys(MLX_KEY *key,378const uint8_t *pubenc, size_t publen,379const uint8_t *prvenc, size_t prvlen)380{381int slot;382383for (slot = 0; slot < 2; ++slot) {384if (prvlen) {385/* Ignore public keys when private provided */386if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PRIV_KEY,387minimal_selection, key, slot, prvenc,388key->minfo->prvkey_bytes, key->xinfo->prvkey_bytes))389goto err;390} else if (publen) {391/* Absent private key data, import public keys */392if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PUB_KEY,393minimal_selection, key, slot, pubenc,394key->minfo->pubkey_bytes, key->xinfo->pubkey_bytes))395goto err;396}397}398key->state = prvlen ? MLX_HAVE_PRVKEY : MLX_HAVE_PUBKEY;399return 1;400401err:402EVP_PKEY_free(key->mkey);403EVP_PKEY_free(key->xkey);404key->xkey = key->mkey = NULL;405key->state = MLX_HAVE_NOKEYS;406return 0;407}408409static int mlx_kem_key_fromdata(MLX_KEY *key,410const OSSL_PARAM params[],411int include_private)412{413const OSSL_PARAM *param_prv_key = NULL, *param_pub_key;414const void *pubenc = NULL, *prvenc = NULL;415size_t pubkey_bytes, prvkey_bytes;416size_t publen = 0, prvlen = 0;417418/* Invalid attempt to mutate a key, what is the right error to report? */419if (key == NULL || mlx_kem_have_pubkey(key))420return 0;421pubkey_bytes = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;422prvkey_bytes = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;423424/* What does the caller want to set? */425param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);426if (param_pub_key != NULL &&427OSSL_PARAM_get_octet_string_ptr(param_pub_key, &pubenc, &publen) != 1)428return 0;429if (include_private)430param_prv_key = OSSL_PARAM_locate_const(params,431OSSL_PKEY_PARAM_PRIV_KEY);432if (param_prv_key != NULL &&433OSSL_PARAM_get_octet_string_ptr(param_prv_key, &prvenc, &prvlen) != 1)434return 0;435436/* The caller MUST specify at least one of the public or private keys. */437if (publen == 0 && prvlen == 0) {438ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);439return 0;440}441442/*443* When a pubkey is provided, its length MUST be correct, if a private key444* is also provided, the public key will be otherwise ignored. We could445* look for a matching encoded block, but unclear this is useful.446*/447if (publen != 0 && publen != pubkey_bytes) {448ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);449return 0;450}451if (prvlen != 0 && prvlen != prvkey_bytes) {452ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);453return 0;454}455456return load_keys(key, pubenc, publen, prvenc, prvlen);457}458459static int mlx_kem_import(void *vkey, int selection, const OSSL_PARAM params[])460{461MLX_KEY *key = vkey;462int include_private;463464if (!ossl_prov_is_running() || key == NULL)465return 0;466467if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)468return 0;469470include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;471return mlx_kem_key_fromdata(key, params, include_private);472}473474static const OSSL_PARAM *mlx_kem_gettable_params(void *provctx)475{476static const OSSL_PARAM arr[] = {477OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),478OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),479OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),480OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),481OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),482OSSL_PARAM_END483};484485return arr;486}487488/*489* It is assumed the key is guaranteed non-NULL here, and is from this provider490*/491static int mlx_kem_get_params(void *vkey, OSSL_PARAM params[])492{493MLX_KEY *key = vkey;494OSSL_PARAM *p, *pub, *prv = NULL;495EXPORT_CB_ARG sub_arg;496int selection;497size_t publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;498size_t prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;499500/* The reported "bit" count is those of the ML-KEM key */501p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS);502if (p != NULL)503if (!OSSL_PARAM_set_int(p, key->minfo->bits))504return 0;505506/* The reported security bits are those of the ML-KEM key */507p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS);508if (p != NULL)509if (!OSSL_PARAM_set_int(p, key->minfo->secbits))510return 0;511512/* The ciphertext sizes are additive */513p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE);514if (p != NULL)515if (!OSSL_PARAM_set_int(p, key->minfo->ctext_bytes + key->xinfo->pubkey_bytes))516return 0;517518if (!mlx_kem_have_pubkey(key))519return 1;520521memset(&sub_arg, 0, sizeof(sub_arg));522pub = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);523if (pub != NULL) {524if (pub->data_type != OSSL_PARAM_OCTET_STRING)525return 0;526pub->return_size = publen;527if (pub->data == NULL) {528pub = NULL;529} else if (pub->data_size < publen) {530ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,531"public key output buffer too short: %lu < %lu",532(unsigned long) pub->data_size,533(unsigned long) publen);534return 0;535} else {536sub_arg.pubenc = pub->data;537}538}539if (mlx_kem_have_prvkey(key)) {540prv = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);541if (prv != NULL) {542if (prv->data_type != OSSL_PARAM_OCTET_STRING)543return 0;544prv->return_size = prvlen;545if (prv->data == NULL) {546prv = NULL;547} else if (prv->data_size < prvlen) {548ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,549"private key output buffer too short: %lu < %lu",550(unsigned long) prv->data_size,551(unsigned long) prvlen);552return 0;553} else {554sub_arg.prvenc = prv->data;555}556}557}558if (pub == NULL && prv == NULL)559return 1;560561selection = prv == NULL ? 0 : OSSL_KEYMGMT_SELECT_PRIVATE_KEY;562selection |= pub == NULL ? 0 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY;563if (key->xinfo->group_name != NULL)564selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;565566/* Extract sub-component key material */567if (!export_sub(&sub_arg, selection, key))568return 0;569570if ((pub != NULL && sub_arg.pubcount != 2)571|| (prv != NULL && sub_arg.prvcount != 2))572return 0;573574return 1;575}576577static const OSSL_PARAM *mlx_kem_settable_params(void *provctx)578{579static const OSSL_PARAM arr[] = {580OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),581OSSL_PARAM_END582};583584return arr;585}586587static int mlx_kem_set_params(void *vkey, const OSSL_PARAM params[])588{589MLX_KEY *key = vkey;590const OSSL_PARAM *p;591const void *pubenc = NULL;592size_t publen = 0;593594if (ossl_param_is_empty(params))595return 1;596597/* Only one settable parameter is supported */598p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);599if (p == NULL)600return 1;601602/* Key mutation is reportedly generally not allowed */603if (mlx_kem_have_pubkey(key)) {604ERR_raise_data(ERR_LIB_PROV,605PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,606"keys cannot be mutated");607return 0;608}609/* An unlikely failure mode is the parameter having some unexpected type */610if (!OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen))611return 0;612613p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);614if (p != NULL) {615OPENSSL_free(key->propq);616key->propq = NULL;617if (!OSSL_PARAM_get_utf8_string(p, &key->propq, 0))618return 0;619}620621if (publen != key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes) {622ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);623return 0;624}625626return load_keys(key, pubenc, publen, NULL, 0);627}628629static int mlx_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])630{631PROV_ML_KEM_GEN_CTX *gctx = vgctx;632const OSSL_PARAM *p;633634if (gctx == NULL)635return 0;636if (ossl_param_is_empty(params))637return 1;638639p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);640if (p != NULL) {641if (p->data_type != OSSL_PARAM_UTF8_STRING)642return 0;643OPENSSL_free(gctx->propq);644if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL)645return 0;646}647return 1;648}649650static void *mlx_kem_gen_init(int evp_type, OSSL_LIB_CTX *libctx,651int selection, const OSSL_PARAM params[])652{653PROV_ML_KEM_GEN_CTX *gctx = NULL;654655/*656* We can only generate private keys, check that the selection is657* appropriate.658*/659if (!ossl_prov_is_running()660|| (selection & minimal_selection) == 0661|| (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL)662return NULL;663664gctx->evp_type = evp_type;665gctx->libctx = libctx;666gctx->selection = selection;667if (mlx_kem_gen_set_params(gctx, params))668return gctx;669670mlx_kem_gen_cleanup(gctx);671return NULL;672}673674static const OSSL_PARAM *mlx_kem_gen_settable_params(ossl_unused void *vgctx,675ossl_unused void *provctx)676{677static OSSL_PARAM settable[] = {678OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),679OSSL_PARAM_END680};681682return settable;683}684685static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)686{687PROV_ML_KEM_GEN_CTX *gctx = vgctx;688MLX_KEY *key;689char *propq;690691if (gctx == NULL692|| (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==693OSSL_KEYMGMT_SELECT_PUBLIC_KEY)694return NULL;695696/* Lose ownership of propq */697propq = gctx->propq;698gctx->propq = NULL;699if ((key = mlx_kem_key_new(gctx->evp_type, gctx->libctx, propq)) == NULL)700return NULL;701702if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)703return key;704705/* For now, using the same "propq" for all components */706key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,707key->minfo->algorithm_name);708key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,709key->xinfo->algorithm_name,710key->xinfo->group_name);711if (key->mkey != NULL && key->xkey != NULL) {712key->state = MLX_HAVE_PRVKEY;713return key;714}715716mlx_kem_key_free(key);717return NULL;718}719720static void mlx_kem_gen_cleanup(void *vgctx)721{722PROV_ML_KEM_GEN_CTX *gctx = vgctx;723724if (gctx == NULL)725return;726OPENSSL_free(gctx->propq);727OPENSSL_free(gctx);728}729730static void *mlx_kem_dup(const void *vkey, int selection)731{732const MLX_KEY *key = vkey;733MLX_KEY *ret;734735if (!ossl_prov_is_running()736|| (ret = OPENSSL_memdup(key, sizeof(*ret))) == NULL)737return NULL;738739if (ret->propq != NULL740&& (ret->propq = OPENSSL_strdup(ret->propq)) == NULL) {741OPENSSL_free(ret);742return NULL;743}744745/* Absent key material, nothing left to do */746if (ret->mkey == NULL) {747if (ret->xkey == NULL)748return ret;749/* Fail if the source key is an inconsistent state */750OPENSSL_free(ret);751return NULL;752}753754switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {755case 0:756ret->xkey = ret->mkey = NULL;757return ret;758case OSSL_KEYMGMT_SELECT_KEYPAIR:759ret->mkey = EVP_PKEY_dup(key->mkey);760ret->xkey = EVP_PKEY_dup(key->xkey);761if (ret->xkey != NULL && ret->mkey != NULL)762return ret;763break;764default:765ERR_raise_data(ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION,766"duplication of partial key material not supported");767break;768}769770mlx_kem_key_free(ret);771return NULL;772}773774#define DECLARE_DISPATCH(name, variant) \775static OSSL_FUNC_keymgmt_new_fn mlx_##name##_kem_new; \776static void *mlx_##name##_kem_new(void *provctx) \777{ \778OSSL_LIB_CTX *libctx; \779\780libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \781return mlx_kem_key_new(variant, libctx, NULL); \782} \783static OSSL_FUNC_keymgmt_gen_init_fn mlx_##name##_kem_gen_init; \784static void *mlx_##name##_kem_gen_init(void *provctx, int selection, \785const OSSL_PARAM params[]) \786{ \787OSSL_LIB_CTX *libctx; \788\789libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \790return mlx_kem_gen_init(variant, libctx, selection, params); \791} \792const OSSL_DISPATCH ossl_mlx_##name##_kem_kmgmt_functions[] = { \793{ OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) mlx_##name##_kem_new }, \794{ OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) mlx_kem_key_free }, \795{ OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) mlx_kem_get_params }, \796{ OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gettable_params }, \797{ OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) mlx_kem_set_params }, \798{ OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_settable_params }, \799{ OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) mlx_kem_has }, \800{ OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) mlx_kem_match }, \801{ OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) mlx_##name##_kem_gen_init }, \802{ OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) mlx_kem_gen_set_params }, \803{ OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gen_settable_params }, \804{ OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) mlx_kem_gen }, \805{ OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) mlx_kem_gen_cleanup }, \806{ OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) mlx_kem_dup }, \807{ OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) mlx_kem_import }, \808{ OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \809{ OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) mlx_kem_export }, \810{ OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \811OSSL_DISPATCH_END \812}813/* See |hybrid_vtable| above */814DECLARE_DISPATCH(p256, 0);815DECLARE_DISPATCH(p384, 1);816#if !defined(OPENSSL_NO_ECX)817DECLARE_DISPATCH(x25519, 2);818DECLARE_DISPATCH(x448, 3);819#endif820821822