Path: blob/main/crypto/openssl/providers/implementations/kem/ml_kem_kem.c
48383 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 <string.h>10#include <openssl/crypto.h>11#include <openssl/evp.h>12#include <openssl/core_dispatch.h>13#include <openssl/core_names.h>14#include <openssl/params.h>15#include <openssl/err.h>16#include <openssl/proverr.h>17#include "crypto/ml_kem.h"18#include "prov/provider_ctx.h"19#include "prov/implementations.h"20#include "prov/securitycheck.h"21#include "prov/providercommon.h"2223static OSSL_FUNC_kem_newctx_fn ml_kem_newctx;24static OSSL_FUNC_kem_freectx_fn ml_kem_freectx;25static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init;26static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate;27static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init;28static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate;29static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params;30static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params;3132typedef struct {33ML_KEM_KEY *key;34uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];35uint8_t *entropy;36int op;37} PROV_ML_KEM_CTX;3839static void *ml_kem_newctx(void *provctx)40{41PROV_ML_KEM_CTX *ctx;4243if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)44return NULL;4546ctx->key = NULL;47ctx->entropy = NULL;48ctx->op = 0;49return ctx;50}5152static void ml_kem_freectx(void *vctx)53{54PROV_ML_KEM_CTX *ctx = vctx;5556if (ctx->entropy != NULL)57OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);58OPENSSL_free(ctx);59}6061static int ml_kem_init(void *vctx, int op, void *key,62const OSSL_PARAM params[])63{64PROV_ML_KEM_CTX *ctx = vctx;6566if (!ossl_prov_is_running())67return 0;68ctx->key = key;69ctx->op = op;70return ml_kem_set_ctx_params(vctx, params);71}7273static int ml_kem_encapsulate_init(void *vctx, void *vkey,74const OSSL_PARAM params[])75{76ML_KEM_KEY *key = vkey;7778if (!ossl_ml_kem_have_pubkey(key)) {79ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);80return 0;81}82return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);83}8485static int ml_kem_decapsulate_init(void *vctx, void *vkey,86const OSSL_PARAM params[])87{88ML_KEM_KEY *key = vkey;8990if (!ossl_ml_kem_have_prvkey(key)) {91ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);92return 0;93}94return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);95}9697static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])98{99PROV_ML_KEM_CTX *ctx = vctx;100const OSSL_PARAM *p;101102if (ctx == NULL)103return 0;104105if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) {106/* Decapsulation is deterministic */107OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);108ctx->entropy = NULL;109}110111if (ossl_param_is_empty(params))112return 1;113114/* Encapsulation ephemeral input key material "ikmE" */115if (ctx->op == EVP_PKEY_OP_ENCAPSULATE116&& (p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME)) != NULL) {117size_t len = ML_KEM_RANDOM_BYTES;118119ctx->entropy = ctx->entropy_buf;120if (OSSL_PARAM_get_octet_string(p, (void **)&ctx->entropy,121len, &len)122&& len == ML_KEM_RANDOM_BYTES)123return 1;124125/* Possibly, but much less likely wrong type */126ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);127ctx->entropy = NULL;128return 0;129}130131return 1;132}133134static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,135ossl_unused void *provctx)136{137static const OSSL_PARAM params[] = {138OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),139OSSL_PARAM_END140};141142return params;143}144145static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,146unsigned char *shsec, size_t *slen)147{148PROV_ML_KEM_CTX *ctx = vctx;149ML_KEM_KEY *key = ctx->key;150const ML_KEM_VINFO *v;151size_t encap_clen;152size_t encap_slen;153int ret = 0;154155if (!ossl_ml_kem_have_pubkey(key)) {156ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);157goto end;158}159v = ossl_ml_kem_key_vinfo(key);160encap_clen = v->ctext_bytes;161encap_slen = ML_KEM_SHARED_SECRET_BYTES;162163if (ctext == NULL) {164if (clen == NULL && slen == NULL)165return 0;166if (clen != NULL)167*clen = encap_clen;168if (slen != NULL)169*slen = encap_slen;170return 1;171}172if (shsec == NULL) {173ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,174"NULL shared-secret buffer");175goto end;176}177178if (clen == NULL) {179ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,180"null ciphertext input/output length pointer");181goto end;182} else if (*clen < encap_clen) {183ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,184"ciphertext buffer too small");185goto end;186} else {187*clen = encap_clen;188}189190if (slen == NULL) {191ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,192"null shared secret input/output length pointer");193goto end;194} else if (*slen < encap_slen) {195ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,196"shared-secret buffer too small");197goto end;198} else {199*slen = encap_slen;200}201202if (ctx->entropy != NULL)203ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,204ctx->entropy, ML_KEM_RANDOM_BYTES, key);205else206ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);207208end:209/*210* One shot entropy, each encapsulate call must either provide a new211* "ikmE", or else will use a random value. If a caller sets an explicit212* ikmE once for testing, and later performs multiple encapsulations213* without again calling encapsulate_init(), these should not share the214* original entropy.215*/216if (ctx->entropy != NULL) {217OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);218ctx->entropy = NULL;219}220return ret;221}222223static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,224const uint8_t *ctext, size_t clen)225{226PROV_ML_KEM_CTX *ctx = vctx;227ML_KEM_KEY *key = ctx->key;228size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;229230if (!ossl_ml_kem_have_prvkey(key)) {231ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);232return 0;233}234235if (shsec == NULL) {236if (slen == NULL)237return 0;238*slen = ML_KEM_SHARED_SECRET_BYTES;239return 1;240}241242/* For now tolerate newly-deprecated NULL length pointers. */243if (slen == NULL) {244slen = &decap_slen;245} else if (*slen < decap_slen) {246ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,247"shared-secret buffer too small");248return 0;249} else {250*slen = decap_slen;251}252253/* ML-KEM decap handles incorrect ciphertext lengths internally */254return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);255}256257const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {258{ OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) ml_kem_newctx },259{ OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) ml_kem_encapsulate_init },260{ OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) ml_kem_encapsulate },261{ OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) ml_kem_decapsulate_init },262{ OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) ml_kem_decapsulate },263{ OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) ml_kem_freectx },264{ OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) ml_kem_set_ctx_params },265{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) ml_kem_settable_ctx_params },266OSSL_DISPATCH_END267};268269270