Path: blob/main/crypto/openssl/providers/implementations/kem/mlx_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 <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) <= 0)210goto end;211if (encap_clen != key->xinfo->pubkey_bytes) {212ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,213"unexpected %s public key output size: %lu",214key->xinfo->algorithm_name, (unsigned long) encap_clen);215goto end;216}217EVP_PKEY_CTX_free(ctx);218219/* Derive the ECDH shared secret */220encap_slen = key->xinfo->shsec_bytes;221sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;222ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);223if (ctx == NULL224|| EVP_PKEY_derive_init(ctx) <= 0225|| EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0226|| EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)227goto end;228if (encap_slen != key->xinfo->shsec_bytes) {229ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,230"unexpected %s shared secret output size: %lu",231key->xinfo->algorithm_name, (unsigned long) encap_slen);232goto end;233}234235ret = 1;236end:237EVP_PKEY_free(xkey);238EVP_PKEY_CTX_free(ctx);239return ret;240}241242static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,243const uint8_t *ctext, size_t clen)244{245MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key;246EVP_PKEY_CTX *ctx = NULL;247EVP_PKEY *xkey = NULL;248const uint8_t *cbuf;249uint8_t *sbuf;250size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;251size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;252int ml_kem_slot = key->xinfo->ml_kem_slot;253int ret = 0;254255if (!mlx_kem_have_prvkey(key)) {256ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);257return 0;258}259260if (shsec == NULL) {261if (slen == NULL)262return 0;263*slen = decap_slen;264return 1;265}266267/* For now tolerate newly-deprecated NULL length pointers. */268if (slen == NULL) {269slen = &decap_slen;270} else if (*slen < decap_slen) {271ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,272"shared-secret buffer too small");273return 0;274} else {275*slen = decap_slen;276}277if (clen != decap_clen) {278ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,279"wrong decapsulation input ciphertext size: %lu",280(unsigned long) clen);281return 0;282}283284/* ML-KEM decapsulation */285decap_clen = key->minfo->ctext_bytes;286decap_slen = ML_KEM_SHARED_SECRET_BYTES;287cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;288sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;289ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);290if (ctx == NULL291|| EVP_PKEY_decapsulate_init(ctx, NULL) <= 0292|| EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)293goto end;294if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {295ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,296"unexpected %s shared secret output size: %lu",297key->minfo->algorithm_name, (unsigned long) decap_slen);298goto end;299}300EVP_PKEY_CTX_free(ctx);301302/* ECDH decapsulation */303decap_clen = key->xinfo->pubkey_bytes;304decap_slen = key->xinfo->shsec_bytes;305cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;306sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;307ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);308if (ctx == NULL309|| (xkey = EVP_PKEY_new()) == NULL310|| EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0311|| EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0312|| EVP_PKEY_derive_init(ctx) <= 0313|| EVP_PKEY_derive_set_peer(ctx, xkey) <= 0314|| EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)315goto end;316if (decap_slen != key->xinfo->shsec_bytes) {317ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,318"unexpected %s shared secret output size: %lu",319key->xinfo->algorithm_name, (unsigned long) decap_slen);320goto end;321}322323ret = 1;324end:325EVP_PKEY_CTX_free(ctx);326EVP_PKEY_free(xkey);327return ret;328}329330const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {331{ OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) mlx_kem_newctx },332{ OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) mlx_kem_encapsulate_init },333{ OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) mlx_kem_encapsulate },334{ OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) mlx_kem_decapsulate_init },335{ OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) mlx_kem_decapsulate },336{ OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) mlx_kem_freectx },337{ OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) mlx_kem_set_ctx_params },338{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) mlx_kem_settable_ctx_params },339OSSL_DISPATCH_END340};341342343