Path: blob/main/crypto/openssl/providers/implementations/kem/mlx_kem.c
109304 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/crypto.h>12#include <openssl/err.h>13#include <openssl/evp.h>14#include <openssl/params.h>15#include <openssl/proverr.h>16#include <openssl/rand.h>17#include "prov/implementations.h"18#include "prov/mlx_kem.h"19#include "prov/provider_ctx.h"20#include "prov/providercommon.h"2122static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;23static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;24static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init;25static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate;26static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init;27static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate;28static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params;29static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params;3031typedef struct {32OSSL_LIB_CTX *libctx;33MLX_KEY *key;34int op;35} PROV_MLX_KEM_CTX;3637static void *mlx_kem_newctx(void *provctx)38{39PROV_MLX_KEM_CTX *ctx;4041if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)42return NULL;4344ctx->libctx = PROV_LIBCTX_OF(provctx);45ctx->key = NULL;46ctx->op = 0;47return ctx;48}4950static void mlx_kem_freectx(void *vctx)51{52OPENSSL_free(vctx);53}5455static int mlx_kem_init(void *vctx, int op, void *key,56ossl_unused const OSSL_PARAM params[])57{58PROV_MLX_KEM_CTX *ctx = vctx;5960if (!ossl_prov_is_running())61return 0;62ctx->key = key;63ctx->op = op;64return 1;65}6667static int68mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])69{70MLX_KEY *key = vkey;7172if (!mlx_kem_have_pubkey(key)) {73ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);74return 0;75}76return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);77}7879static int80mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])81{82MLX_KEY *key = vkey;8384if (!mlx_kem_have_prvkey(key)) {85ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);86return 0;87}88return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);89}9091static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx,92ossl_unused void *provctx)93{94static const OSSL_PARAM params[] = { OSSL_PARAM_END };9596return params;97}9899static int100mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])101{102return 1;103}104105static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,106unsigned char *shsec, size_t *slen)107{108MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;109EVP_PKEY_CTX *ctx = NULL;110EVP_PKEY *xkey = NULL;111size_t encap_clen;112size_t encap_slen;113uint8_t *cbuf;114uint8_t *sbuf;115int ml_kem_slot = key->xinfo->ml_kem_slot;116int ret = 0;117118if (!mlx_kem_have_pubkey(key)) {119ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);120goto end;121}122encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;123encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;124125if (ctext == NULL) {126if (clen == NULL && slen == NULL)127return 0;128if (clen != NULL)129*clen = encap_clen;130if (slen != NULL)131*slen = encap_slen;132return 1;133}134if (shsec == NULL) {135ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,136"null shared-secret output buffer");137return 0;138}139140if (clen == NULL) {141ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,142"null ciphertext input/output length pointer");143return 0;144} else if (*clen < encap_clen) {145ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,146"ciphertext buffer too small");147return 0;148} else {149*clen = encap_clen;150}151152if (slen == NULL) {153ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,154"null shared secret input/output length pointer");155return 0;156} else if (*slen < encap_slen) {157ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,158"shared-secret buffer too small");159return 0;160} else {161*slen = encap_slen;162}163164/* ML-KEM encapsulation */165encap_clen = key->minfo->ctext_bytes;166encap_slen = ML_KEM_SHARED_SECRET_BYTES;167cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;168sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;169ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);170if (ctx == NULL171|| EVP_PKEY_encapsulate_init(ctx, NULL) <= 0172|| EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)173goto end;174if (encap_clen != key->minfo->ctext_bytes) {175ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,176"unexpected %s ciphertext output size: %lu",177key->minfo->algorithm_name, (unsigned long)encap_clen);178goto end;179}180if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) {181ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,182"unexpected %s shared secret output size: %lu",183key->minfo->algorithm_name, (unsigned long)encap_slen);184goto end;185}186EVP_PKEY_CTX_free(ctx);187188/*-189* ECDHE encapsulation190*191* Generate own ephemeral private key and add its public key to ctext.192*193* Note, we could support a settable parameter that sets an extant ECDH194* keypair as the keys to use in encap, making it possible to reuse the195* same (TLS client) ECDHE keypair for both the classical EC keyshare and a196* corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need197* know that this is a hybrid, and that it can partly reuse the same keys198* as another group for which a keyshare will be sent. Deferred until we199* support generating multiple keyshares, there's a workable keyshare200* prediction specification, and the optimisation is justified.201*/202cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;203encap_clen = key->xinfo->pubkey_bytes;204ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);205if (ctx == NULL206|| EVP_PKEY_keygen_init(ctx) <= 0207|| EVP_PKEY_keygen(ctx, &xkey) <= 0208|| EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,209cbuf, encap_clen, &encap_clen)210<= 0)211goto end;212if (encap_clen != key->xinfo->pubkey_bytes) {213ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,214"unexpected %s public key output size: %lu",215key->xinfo->algorithm_name, (unsigned long)encap_clen);216goto end;217}218EVP_PKEY_CTX_free(ctx);219220/* Derive the ECDH shared secret */221encap_slen = key->xinfo->shsec_bytes;222sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;223ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);224if (ctx == NULL225|| EVP_PKEY_derive_init(ctx) <= 0226|| EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0227|| EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)228goto end;229if (encap_slen != key->xinfo->shsec_bytes) {230ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,231"unexpected %s shared secret output size: %lu",232key->xinfo->algorithm_name, (unsigned long)encap_slen);233goto end;234}235236ret = 1;237end:238EVP_PKEY_free(xkey);239EVP_PKEY_CTX_free(ctx);240return ret;241}242243static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,244const uint8_t *ctext, size_t clen)245{246MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;247EVP_PKEY_CTX *ctx = NULL;248EVP_PKEY *xkey = NULL;249const uint8_t *cbuf;250uint8_t *sbuf;251size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;252size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;253int ml_kem_slot = key->xinfo->ml_kem_slot;254int ret = 0;255256if (!mlx_kem_have_prvkey(key)) {257ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);258return 0;259}260261if (shsec == NULL) {262if (slen == NULL)263return 0;264*slen = decap_slen;265return 1;266}267268/* For now tolerate newly-deprecated NULL length pointers. */269if (slen == NULL) {270slen = &decap_slen;271} else if (*slen < decap_slen) {272ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,273"shared-secret buffer too small");274return 0;275} else {276*slen = decap_slen;277}278if (clen != decap_clen) {279ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,280"wrong decapsulation input ciphertext size: %lu",281(unsigned long)clen);282return 0;283}284285/* ML-KEM decapsulation */286decap_clen = key->minfo->ctext_bytes;287decap_slen = ML_KEM_SHARED_SECRET_BYTES;288cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;289sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;290ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);291if (ctx == NULL292|| EVP_PKEY_decapsulate_init(ctx, NULL) <= 0293|| EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)294goto end;295if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {296ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,297"unexpected %s shared secret output size: %lu",298key->minfo->algorithm_name, (unsigned long)decap_slen);299goto end;300}301EVP_PKEY_CTX_free(ctx);302303/* ECDH decapsulation */304decap_clen = key->xinfo->pubkey_bytes;305decap_slen = key->xinfo->shsec_bytes;306cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;307sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;308ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);309if (ctx == NULL310|| (xkey = EVP_PKEY_new()) == NULL311|| EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0312|| EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0313|| EVP_PKEY_derive_init(ctx) <= 0314|| EVP_PKEY_derive_set_peer(ctx, xkey) <= 0315|| EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)316goto end;317if (decap_slen != key->xinfo->shsec_bytes) {318ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,319"unexpected %s shared secret output size: %lu",320key->xinfo->algorithm_name, (unsigned long)decap_slen);321goto end;322}323324ret = 1;325end:326EVP_PKEY_CTX_free(ctx);327EVP_PKEY_free(xkey);328return ret;329}330331const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {332{ OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)mlx_kem_newctx },333{ OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)mlx_kem_encapsulate_init },334{ OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)mlx_kem_encapsulate },335{ OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)mlx_kem_decapsulate_init },336{ OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)mlx_kem_decapsulate },337{ OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)mlx_kem_freectx },338{ OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)mlx_kem_set_ctx_params },339{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)mlx_kem_settable_ctx_params },340OSSL_DISPATCH_END341};342343344