Path: blob/main/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c
109501 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/params.h>12#include <openssl/err.h>13#include <openssl/proverr.h>14#include <openssl/provider.h>15#include <openssl/rand.h>16#include <openssl/self_test.h>17#include <openssl/param_build.h>18#include "crypto/ml_kem.h"19#include "internal/fips.h"20#include "internal/param_build_set.h"21#include "prov/implementations.h"22#include "prov/providercommon.h"23#include "prov/provider_ctx.h"24#include "prov/securitycheck.h"25#include "prov/ml_kem.h"2627static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new;28static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new;29static OSSL_FUNC_keymgmt_new_fn ml_kem_1024_new;30static OSSL_FUNC_keymgmt_gen_fn ml_kem_gen;31static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_512_gen_init;32static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_768_gen_init;33static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_1024_gen_init;34static OSSL_FUNC_keymgmt_gen_cleanup_fn ml_kem_gen_cleanup;35static OSSL_FUNC_keymgmt_gen_set_params_fn ml_kem_gen_set_params;36static OSSL_FUNC_keymgmt_gen_settable_params_fn ml_kem_gen_settable_params;37#ifndef FIPS_MODULE38static OSSL_FUNC_keymgmt_load_fn ml_kem_load;39#endif40static OSSL_FUNC_keymgmt_get_params_fn ml_kem_get_params;41static OSSL_FUNC_keymgmt_gettable_params_fn ml_kem_gettable_params;42static OSSL_FUNC_keymgmt_set_params_fn ml_kem_set_params;43static OSSL_FUNC_keymgmt_settable_params_fn ml_kem_settable_params;44static OSSL_FUNC_keymgmt_has_fn ml_kem_has;45static OSSL_FUNC_keymgmt_match_fn ml_kem_match;46static OSSL_FUNC_keymgmt_validate_fn ml_kem_validate;47static OSSL_FUNC_keymgmt_import_fn ml_kem_import;48static OSSL_FUNC_keymgmt_export_fn ml_kem_export;49static OSSL_FUNC_keymgmt_import_types_fn ml_kem_imexport_types;50static OSSL_FUNC_keymgmt_export_types_fn ml_kem_imexport_types;51static OSSL_FUNC_keymgmt_dup_fn ml_kem_dup;5253static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS54| OSSL_KEYMGMT_SELECT_PRIVATE_KEY;5556typedef struct ml_kem_gen_ctx_st {57PROV_CTX *provctx;58char *propq;59int selection;60int evp_type;61uint8_t seedbuf[ML_KEM_SEED_BYTES];62uint8_t *seed;63} PROV_ML_KEM_GEN_CTX;6465static int ml_kem_pairwise_test(const ML_KEM_KEY *key, int key_flags)66{67#ifdef FIPS_MODULE68OSSL_SELF_TEST *st = NULL;69OSSL_CALLBACK *cb = NULL;70void *cbarg = NULL;71#endif72unsigned char entropy[ML_KEM_RANDOM_BYTES];73unsigned char secret[ML_KEM_SHARED_SECRET_BYTES];74unsigned char out[ML_KEM_SHARED_SECRET_BYTES];75unsigned char *ctext = NULL;76const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key);77int operation_result = 0;78int ret = 0;7980/* Unless we have both a public and private key, we can't do the test */81if (!ossl_ml_kem_have_prvkey(key)82|| !ossl_ml_kem_have_pubkey(key)83|| (key_flags & ML_KEM_KEY_PCT_TYPE) == 0)84return 1;85#ifdef FIPS_MODULE86/* During self test, it is a waste to do this test */87if (ossl_fips_self_testing())88return 1;8990/*91* The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`92* is NULL.93*/94OSSL_SELF_TEST_get_callback(key->libctx, &cb, &cbarg);9596st = OSSL_SELF_TEST_new(cb, cbarg);97if (st == NULL)98return 0;99100OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT,101OSSL_SELF_TEST_DESC_PCT_ML_KEM);102#endif /* FIPS_MODULE */103104ctext = OPENSSL_malloc(v->ctext_bytes);105if (ctext == NULL)106goto err;107108memset(out, 0, sizeof(out));109110/*111* The pairwise test is skipped unless either RANDOM or FIXED entropy PCTs112* are enabled.113*/114if (key_flags & ML_KEM_KEY_RANDOM_PCT) {115operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,116secret, sizeof(secret), key);117} else {118memset(entropy, 0125, sizeof(entropy));119operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes,120secret, sizeof(secret),121entropy, sizeof(entropy),122key);123}124if (operation_result != 1)125goto err;126127#ifdef FIPS_MODULE128OSSL_SELF_TEST_oncorrupt_byte(st, ctext);129#endif130131operation_result = ossl_ml_kem_decap(out, sizeof(out), ctext, v->ctext_bytes,132key);133if (operation_result != 1 || memcmp(out, secret, sizeof(out)) != 0)134goto err;135136ret = 1;137err:138#ifdef FIPS_MODULE139OSSL_SELF_TEST_onend(st, ret);140OSSL_SELF_TEST_free(st);141#else142if (ret == 0) {143ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,144"public part of %s private key fails to match private",145v->algorithm_name);146}147#endif148OPENSSL_free(ctext);149return ret;150}151152ML_KEM_KEY *ossl_prov_ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)153{154ML_KEM_KEY *key;155156if (!ossl_prov_is_running())157return NULL;158/*159* When decoding, if the key ends up "loaded" into the same provider, these160* are the correct config settings, otherwise, new values will be assigned161* on import into a different provider. The "load" API does not pass along162* the provider context.163*/164if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) {165const char *pct_type = ossl_prov_ctx_get_param(166ctx, OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE, "random");167168if (ossl_prov_ctx_get_bool_param(169ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1))170key->prov_flags |= ML_KEM_KEY_RETAIN_SEED;171else172key->prov_flags &= ~ML_KEM_KEY_RETAIN_SEED;173if (ossl_prov_ctx_get_bool_param(174ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1))175key->prov_flags |= ML_KEM_KEY_PREFER_SEED;176else177key->prov_flags &= ~ML_KEM_KEY_PREFER_SEED;178if (OPENSSL_strcasecmp(pct_type, "random") == 0)179key->prov_flags |= ML_KEM_KEY_RANDOM_PCT;180else if (OPENSSL_strcasecmp(pct_type, "fixed") == 0)181key->prov_flags |= ML_KEM_KEY_FIXED_PCT;182else183key->prov_flags &= ~ML_KEM_KEY_PCT_TYPE;184}185return key;186}187188static int ml_kem_has(const void *vkey, int selection)189{190const ML_KEM_KEY *key = vkey;191192/* A NULL key MUST fail to have anything */193if (!ossl_prov_is_running() || key == NULL)194return 0;195196switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {197case 0:198return 1;199case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:200return ossl_ml_kem_have_pubkey(key);201default:202return ossl_ml_kem_have_prvkey(key);203}204}205206static int ml_kem_match(const void *vkey1, const void *vkey2, int selection)207{208const ML_KEM_KEY *key1 = vkey1;209const ML_KEM_KEY *key2 = vkey2;210211if (!ossl_prov_is_running())212return 0;213214/* All we have that can be compared is key material */215if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR))216return 1;217218return ossl_ml_kem_pubkey_cmp(key1, key2);219}220221static int ml_kem_validate(const void *vkey, int selection, int check_type)222{223const ML_KEM_KEY *key = vkey;224225if (!ml_kem_has(key, selection))226return 0;227228if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)229return ml_kem_pairwise_test(key, ML_KEM_KEY_RANDOM_PCT);230return 1;231}232233static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,234void *cbarg)235{236ML_KEM_KEY *key = vkey;237OSSL_PARAM_BLD *tmpl = NULL;238OSSL_PARAM *params = NULL;239const ML_KEM_VINFO *v;240uint8_t *pubenc = NULL, *prvenc = NULL, *seedenc = NULL;241size_t prvlen = 0, seedlen = 0;242int ret = 0;243244if (!ossl_prov_is_running() || key == NULL)245return 0;246247if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)248return 0;249250v = ossl_ml_kem_key_vinfo(key);251if (!ossl_ml_kem_have_pubkey(key)) {252/* Fail when no key material can be returned */253if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0254|| !ossl_ml_kem_decoded_key(key)) {255ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);256return 0;257}258} else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {259pubenc = OPENSSL_malloc(v->pubkey_bytes);260if (pubenc == NULL261|| !ossl_ml_kem_encode_public_key(pubenc, v->pubkey_bytes, key))262goto err;263}264265if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {266/*267* The seed and/or private key material are allocated on the secure268* heap if configured, ossl_param_build_set_octet_string(), will then269* also use the secure heap.270*/271if (ossl_ml_kem_have_seed(key)) {272seedlen = ML_KEM_SEED_BYTES;273if ((seedenc = OPENSSL_secure_zalloc(seedlen)) == NULL274|| !ossl_ml_kem_encode_seed(seedenc, seedlen, key))275goto err;276}277if (ossl_ml_kem_have_prvkey(key)) {278prvlen = v->prvkey_bytes;279if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL280|| !ossl_ml_kem_encode_private_key(prvenc, prvlen, key))281goto err;282} else if (ossl_ml_kem_have_dkenc(key)) {283prvlen = v->prvkey_bytes;284if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL)285goto err;286memcpy(prvenc, key->encoded_dk, prvlen);287}288}289290tmpl = OSSL_PARAM_BLD_new();291if (tmpl == NULL)292goto err;293294/* The (d, z) seed, when available and private keys are requested. */295if (seedenc != NULL296&& !ossl_param_build_set_octet_string(297tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen))298goto err;299300/* The private key in the FIPS 203 |dk| format, when requested. */301if (prvenc != NULL302&& !ossl_param_build_set_octet_string(303tmpl, params, OSSL_PKEY_PARAM_PRIV_KEY, prvenc, prvlen))304goto err;305306/* The public key on request; it is always available when either is */307if (pubenc != NULL308&& !ossl_param_build_set_octet_string(309tmpl, params, OSSL_PKEY_PARAM_PUB_KEY, pubenc, v->pubkey_bytes))310goto err;311312params = OSSL_PARAM_BLD_to_param(tmpl);313if (params == NULL)314goto err;315316ret = param_cb(params, cbarg);317OSSL_PARAM_free(params);318319err:320OSSL_PARAM_BLD_free(tmpl);321OPENSSL_secure_clear_free(seedenc, seedlen);322OPENSSL_secure_clear_free(prvenc, prvlen);323OPENSSL_free(pubenc);324return ret;325}326327static const OSSL_PARAM *ml_kem_imexport_types(int selection)328{329static const OSSL_PARAM key_types[] = {330OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),331OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),332OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),333OSSL_PARAM_END334};335336if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)337return key_types;338return NULL;339}340341static int check_seed(const uint8_t *seed, const uint8_t *prvenc,342ML_KEM_KEY *key)343{344size_t zlen = ML_KEM_RANDOM_BYTES;345346if (memcmp(seed + ML_KEM_SEED_BYTES - zlen,347prvenc + key->vinfo->prvkey_bytes - zlen, zlen)348== 0)349return 1;350ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,351"private %s key implicit rejection secret does"352" not match seed",353key->vinfo->algorithm_name);354return 0;355}356357static int check_prvenc(const uint8_t *prvenc, ML_KEM_KEY *key)358{359size_t len = key->vinfo->prvkey_bytes;360uint8_t *buf = OPENSSL_malloc(len);361int ret = 0;362363if (buf != NULL364&& ossl_ml_kem_encode_private_key(buf, len, key))365ret = memcmp(buf, prvenc, len) == 0;366OPENSSL_clear_free(buf, len);367if (ret)368return 1;369370if (buf != NULL)371ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,372"explicit %s private key does not match seed",373key->vinfo->algorithm_name);374ossl_ml_kem_key_reset(key);375return 0;376}377378static int ml_kem_key_fromdata(ML_KEM_KEY *key,379const OSSL_PARAM params[],380int include_private)381{382const OSSL_PARAM *p = NULL;383const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL;384size_t publen = 0, prvlen = 0, seedlen = 0, puboff;385const ML_KEM_VINFO *v;386387/* Invalid attempt to mutate a key, what is the right error to report? */388if (key == NULL || ossl_ml_kem_have_pubkey(key))389return 0;390v = ossl_ml_kem_key_vinfo(key);391392/*393* When a private key is provided, without a seed, any public key also394* provided will be ignored (apart from length), just as with the seed.395*/396if (include_private) {397/*398* When a seed is provided, the private and public keys may be ignored,399* after validating just their lengths. Comparing encodings or hashes400* when applicable is possible, but not currently implemented.401*/402p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);403if (p != NULL404&& OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1)405return 0;406if (seedlen != 0 && seedlen != ML_KEM_SEED_BYTES) {407ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);408return 0;409}410p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);411if (p != NULL412&& OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1)413return 0;414if (prvlen != 0 && prvlen != v->prvkey_bytes) {415ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);416return 0;417}418}419420/* Used only when no seed or private key is provided. */421p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);422if (p != NULL423&& OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1)424return 0;425if (publen != 0 && publen != v->pubkey_bytes) {426ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);427return 0;428}429430/* The caller MUST specify at least one of seed, private or public keys. */431if (seedlen == 0 && publen == 0 && prvlen == 0) {432ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);433return 0;434}435436/* Check any explicit public key against embedded value in private key */437if (publen > 0 && prvlen > 0) {438/* point to the ek offset in dk = DKpke||ek||H(ek)||z */439puboff = prvlen - ML_KEM_RANDOM_BYTES - ML_KEM_PKHASH_BYTES - publen;440if (memcmp(pubenc, (unsigned char *)prvenc + puboff, publen) != 0) {441ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,442"explicit %s public key does not match private",443v->algorithm_name);444return 0;445}446}447448if (seedlen != 0449&& (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {450if (prvlen != 0 && !check_seed(seedenc, prvenc, key))451return 0;452if (!ossl_ml_kem_set_seed(seedenc, seedlen, key)453|| !ossl_ml_kem_genkey(NULL, 0, key))454return 0;455return prvlen == 0 || check_prvenc(prvenc, key);456} else if (prvlen != 0) {457return ossl_ml_kem_parse_private_key(prvenc, prvlen, key);458}459return ossl_ml_kem_parse_public_key(pubenc, publen, key);460}461462static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[])463{464ML_KEM_KEY *key = vkey;465int include_private;466int res;467468if (!ossl_prov_is_running() || key == NULL)469return 0;470471if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)472return 0;473474include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;475res = ml_kem_key_fromdata(key, params, include_private);476if (res > 0 && include_private477&& !ml_kem_pairwise_test(key, key->prov_flags)) {478#ifdef FIPS_MODULE479ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT_IMPORT);480#endif481ossl_ml_kem_key_reset(key);482res = 0;483}484return res;485}486487static const OSSL_PARAM *ml_kem_gettable_params(void *provctx)488{489static const OSSL_PARAM arr[] = {490OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),491OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),492OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),493/* Exported for import */494OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),495/* Exported to EVP_PKEY_get_raw_private_key() */496OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),497/* Exported to EVP_PKEY_get_raw_public_key() */498OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),499/* Needed by EVP_PKEY_get1_encoded_public_key() */500OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),501OSSL_PARAM_END502};503504return arr;505}506507#ifndef FIPS_MODULE508static void *ml_kem_load(const void *reference, size_t reference_sz)509{510ML_KEM_KEY *key = NULL;511uint8_t *encoded_dk = NULL;512uint8_t seed[ML_KEM_SEED_BYTES];513514if (ossl_prov_is_running() && reference_sz == sizeof(key)) {515/* The contents of the reference is the address to our object */516key = *(ML_KEM_KEY **)reference;517encoded_dk = key->encoded_dk;518key->encoded_dk = NULL;519/* We grabbed, so we detach it */520*(ML_KEM_KEY **)reference = NULL;521if (encoded_dk != NULL522&& ossl_ml_kem_encode_seed(seed, sizeof(seed), key)523&& !check_seed(seed, encoded_dk, key))524goto err;525/* Generate the key now, if it holds only a stashed seed. */526if (ossl_ml_kem_have_seed(key)527&& (encoded_dk == NULL528|| (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {529if (!ossl_ml_kem_genkey(NULL, 0, key)530|| (encoded_dk != NULL && !check_prvenc(encoded_dk, key)))531goto err;532} else if (encoded_dk != NULL) {533if (!ossl_ml_kem_parse_private_key(encoded_dk,534key->vinfo->prvkey_bytes, key)) {535ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,536"error parsing %s private key",537key->vinfo->algorithm_name);538goto err;539}540if (!ml_kem_pairwise_test(key, key->prov_flags))541goto err;542}543OPENSSL_free(encoded_dk);544return key;545}546547err:548OPENSSL_free(encoded_dk);549ossl_ml_kem_key_free(key);550return NULL;551}552#endif553554/*555* It is assumed the key is guaranteed non-NULL here, and is from this provider556*/557static int ml_kem_get_params(void *vkey, OSSL_PARAM params[])558{559ML_KEM_KEY *key = vkey;560const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key);561OSSL_PARAM *p;562const char *pubparams[] = {563OSSL_PKEY_PARAM_PUB_KEY,564OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY565};566int i;567568p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS);569if (p != NULL)570if (!OSSL_PARAM_set_int(p, v->bits))571return 0;572573p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS);574if (p != NULL)575if (!OSSL_PARAM_set_int(p, v->secbits))576return 0;577578p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE);579if (p != NULL)580if (!OSSL_PARAM_set_int(p, v->ctext_bytes))581return 0;582583if (ossl_ml_kem_have_pubkey(key)) {584uint8_t *pubenc = NULL;585586for (i = 0; i < 2; ++i) {587p = OSSL_PARAM_locate(params, pubparams[i]);588if (p == NULL)589continue;590if (p->data_type != OSSL_PARAM_OCTET_STRING)591return 0;592p->return_size = v->pubkey_bytes;593if (p->data == NULL)594continue;595if (p->data_size < p->return_size)596return 0;597if (pubenc != NULL) {598memcpy(p->data, pubenc, p->return_size);599continue;600}601if (!ossl_ml_kem_encode_public_key(p->data, p->return_size, key))602return 0;603pubenc = p->data;604}605}606607p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);608if (p != NULL && ossl_ml_kem_have_prvkey(key)) {609if (p->data_type != OSSL_PARAM_OCTET_STRING)610return 0;611p->return_size = v->prvkey_bytes;612if (p->data != NULL) {613if (p->data_size < p->return_size)614return 0;615if (!ossl_ml_kem_encode_private_key(p->data, p->return_size, key))616return 0;617}618}619620p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_KEM_SEED);621if (p != NULL && ossl_ml_kem_have_seed(key)) {622if (p->data_type != OSSL_PARAM_OCTET_STRING)623return 0;624p->return_size = ML_KEM_SEED_BYTES;625if (p->data != NULL) {626if (p->data_size < p->return_size)627return 0;628if (!ossl_ml_kem_encode_seed(p->data, p->return_size, key))629return 0;630}631}632633return 1;634}635636static const OSSL_PARAM *ml_kem_settable_params(void *provctx)637{638static const OSSL_PARAM arr[] = {639/* Used in TLS via EVP_PKEY_set1_encoded_public_key(). */640OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),641OSSL_PARAM_END642};643644return arr;645}646647static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[])648{649ML_KEM_KEY *key = vkey;650const OSSL_PARAM *p;651const void *pubenc = NULL;652size_t publen = 0;653654if (ossl_param_is_empty(params))655return 1;656657p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);658if (p != NULL659&& (OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1660|| publen != key->vinfo->pubkey_bytes)) {661ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);662return 0;663}664665if (publen == 0)666return 1;667668/* Key mutation is reportedly generally not allowed */669if (ossl_ml_kem_have_pubkey(key)) {670ERR_raise_data(ERR_LIB_PROV,671PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,672"ML-KEM keys cannot be mutated");673return 0;674}675676return ossl_ml_kem_parse_public_key(pubenc, publen, key);677}678679static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])680{681PROV_ML_KEM_GEN_CTX *gctx = vgctx;682const OSSL_PARAM *p;683684if (gctx == NULL)685return 0;686if (ossl_param_is_empty(params))687return 1;688689p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);690if (p != NULL) {691if (p->data_type != OSSL_PARAM_UTF8_STRING)692return 0;693OPENSSL_free(gctx->propq);694if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL)695return 0;696}697698p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);699if (p != NULL) {700size_t len = ML_KEM_SEED_BYTES;701702gctx->seed = gctx->seedbuf;703if (OSSL_PARAM_get_octet_string(p, (void **)&gctx->seed, len, &len)704&& len == ML_KEM_SEED_BYTES)705return 1;706707/* Possibly, but less likely wrong data type */708ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);709gctx->seed = NULL;710return 0;711}712713return 1;714}715716static void *ml_kem_gen_init(void *provctx, int selection,717const OSSL_PARAM params[], int evp_type)718{719PROV_ML_KEM_GEN_CTX *gctx = NULL;720721/*722* We can only generate private keys, check that the selection is723* appropriate.724*/725if (!ossl_prov_is_running()726|| (selection & minimal_selection) == 0727|| (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL)728return NULL;729730gctx->selection = selection;731gctx->evp_type = evp_type;732gctx->provctx = provctx;733if (ml_kem_gen_set_params(gctx, params))734return gctx;735736ml_kem_gen_cleanup(gctx);737return NULL;738}739740static const OSSL_PARAM *ml_kem_gen_settable_params(ossl_unused void *vgctx,741ossl_unused void *provctx)742{743static OSSL_PARAM settable[] = {744OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),745OSSL_PARAM_END746};747return settable;748}749750static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)751{752PROV_ML_KEM_GEN_CTX *gctx = vgctx;753ML_KEM_KEY *key;754uint8_t *nopub = NULL;755uint8_t *seed;756int genok = 0;757758if (gctx == NULL759|| (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_PUBLIC_KEY)760return NULL;761seed = gctx->seed;762key = ossl_prov_ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);763if (key == NULL)764return NULL;765766if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)767return key;768769if (seed != NULL && !ossl_ml_kem_set_seed(seed, ML_KEM_SEED_BYTES, key))770return NULL;771genok = ossl_ml_kem_genkey(nopub, 0, key);772773/* Erase the single-use seed */774if (seed != NULL)775OPENSSL_cleanse(seed, ML_KEM_SEED_BYTES);776gctx->seed = NULL;777778if (genok) {779#ifdef FIPS_MODULE780if (!ml_kem_pairwise_test(key, ML_KEM_KEY_FIXED_PCT)) {781ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);782ossl_ml_kem_key_free(key);783return NULL;784}785#endif /* FIPS_MODULE */786return key;787}788789ossl_ml_kem_key_free(key);790return NULL;791}792793static void ml_kem_gen_cleanup(void *vgctx)794{795PROV_ML_KEM_GEN_CTX *gctx = vgctx;796797if (gctx == NULL)798return;799800if (gctx->seed != NULL)801OPENSSL_cleanse(gctx->seed, ML_KEM_RANDOM_BYTES);802OPENSSL_free(gctx->propq);803OPENSSL_free(gctx);804}805806static void *ml_kem_dup(const void *vkey, int selection)807{808const ML_KEM_KEY *key = vkey;809810if (!ossl_prov_is_running())811return NULL;812813return ossl_ml_kem_key_dup(key, selection);814}815816#ifndef FIPS_MODULE817#define DISPATCH_LOAD_FN \818{ OSSL_FUNC_KEYMGMT_LOAD, (OSSL_FUNC)ml_kem_load },819#else820#define DISPATCH_LOAD_FN /* Non-FIPS only */821#endif822823#define DECLARE_VARIANT(bits) \824static void *ml_kem_##bits##_new(void *provctx) \825{ \826return ossl_prov_ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \827} \828static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \829const OSSL_PARAM params[]) \830{ \831return ml_kem_gen_init(provctx, selection, params, \832EVP_PKEY_ML_KEM_##bits); \833} \834const OSSL_DISPATCH ossl_ml_kem_##bits##_keymgmt_functions[] = { \835{ OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC)ml_kem_##bits##_new }, \836{ OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC)ossl_ml_kem_key_free }, \837{ OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC)ml_kem_get_params }, \838{ OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC)ml_kem_gettable_params }, \839{ OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC)ml_kem_set_params }, \840{ OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC)ml_kem_settable_params }, \841{ OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC)ml_kem_has }, \842{ OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC)ml_kem_match }, \843{ OSSL_FUNC_KEYMGMT_VALIDATE, (OSSL_FUNC)ml_kem_validate }, \844{ OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC)ml_kem_##bits##_gen_init }, \845{ OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC)ml_kem_gen_set_params }, \846{ OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC)ml_kem_gen_settable_params }, \847{ OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC)ml_kem_gen }, \848{ OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC)ml_kem_gen_cleanup }, \849DISPATCH_LOAD_FN { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC)ml_kem_dup }, \850{ OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC)ml_kem_import }, \851{ OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC)ml_kem_imexport_types }, \852{ OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC)ml_kem_export }, \853{ OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC)ml_kem_imexport_types }, \854OSSL_DISPATCH_END \855}856DECLARE_VARIANT(512);857DECLARE_VARIANT(768);858DECLARE_VARIANT(1024);859860861