Home | History | Annotate | Line # | Download | only in kem
      1 /*
      2  * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5  * this file except in compliance with the License.  You can obtain a copy
      6  * in the file LICENSE in the source distribution or at
      7  * https://www.openssl.org/source/license.html
      8  */
      9 
     10 #include <openssl/core_dispatch.h>
     11 #include <openssl/core_names.h>
     12 #include <openssl/crypto.h>
     13 #include <openssl/err.h>
     14 #include <openssl/evp.h>
     15 #include <openssl/params.h>
     16 #include <openssl/proverr.h>
     17 #include <openssl/rand.h>
     18 #include "prov/implementations.h"
     19 #include "prov/mlx_kem.h"
     20 #include "prov/provider_ctx.h"
     21 #include "prov/providercommon.h"
     22 
     23 static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;
     24 static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;
     25 static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init;
     26 static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate;
     27 static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init;
     28 static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate;
     29 static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params;
     30 static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params;
     31 
     32 typedef struct {
     33     OSSL_LIB_CTX *libctx;
     34     MLX_KEY *key;
     35     int op;
     36 } PROV_MLX_KEM_CTX;
     37 
     38 static void *mlx_kem_newctx(void *provctx)
     39 {
     40     PROV_MLX_KEM_CTX *ctx;
     41 
     42     if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
     43         return NULL;
     44 
     45     ctx->libctx = PROV_LIBCTX_OF(provctx);
     46     ctx->key = NULL;
     47     ctx->op = 0;
     48     return ctx;
     49 }
     50 
     51 static void mlx_kem_freectx(void *vctx)
     52 {
     53     OPENSSL_free(vctx);
     54 }
     55 
     56 static int mlx_kem_init(void *vctx, int op, void *key,
     57     ossl_unused const OSSL_PARAM params[])
     58 {
     59     PROV_MLX_KEM_CTX *ctx = vctx;
     60 
     61     if (!ossl_prov_is_running())
     62         return 0;
     63     ctx->key = key;
     64     ctx->op = op;
     65     return 1;
     66 }
     67 
     68 static int
     69 mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
     70 {
     71     MLX_KEY *key = vkey;
     72 
     73     if (!mlx_kem_have_pubkey(key)) {
     74         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
     75         return 0;
     76     }
     77     return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
     78 }
     79 
     80 static int
     81 mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
     82 {
     83     MLX_KEY *key = vkey;
     84 
     85     if (!mlx_kem_have_prvkey(key)) {
     86         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
     87         return 0;
     88     }
     89     return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
     90 }
     91 
     92 static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx,
     93     ossl_unused void *provctx)
     94 {
     95     static const OSSL_PARAM params[] = { OSSL_PARAM_END };
     96 
     97     return params;
     98 }
     99 
    100 static int
    101 mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
    102 {
    103     return 1;
    104 }
    105 
    106 static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
    107     unsigned char *shsec, size_t *slen)
    108 {
    109     MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;
    110     EVP_PKEY_CTX *ctx = NULL;
    111     EVP_PKEY *xkey = NULL;
    112     size_t encap_clen;
    113     size_t encap_slen;
    114     uint8_t *cbuf;
    115     uint8_t *sbuf;
    116     int ml_kem_slot = key->xinfo->ml_kem_slot;
    117     int ret = 0;
    118 
    119     if (!mlx_kem_have_pubkey(key)) {
    120         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
    121         goto end;
    122     }
    123     encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
    124     encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
    125 
    126     if (ctext == NULL) {
    127         if (clen == NULL && slen == NULL)
    128             return 0;
    129         if (clen != NULL)
    130             *clen = encap_clen;
    131         if (slen != NULL)
    132             *slen = encap_slen;
    133         return 1;
    134     }
    135     if (shsec == NULL) {
    136         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
    137             "null shared-secret output buffer");
    138         return 0;
    139     }
    140 
    141     if (clen == NULL) {
    142         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
    143             "null ciphertext input/output length pointer");
    144         return 0;
    145     } else if (*clen < encap_clen) {
    146         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
    147             "ciphertext buffer too small");
    148         return 0;
    149     } else {
    150         *clen = encap_clen;
    151     }
    152 
    153     if (slen == NULL) {
    154         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
    155             "null shared secret input/output length pointer");
    156         return 0;
    157     } else if (*slen < encap_slen) {
    158         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
    159             "shared-secret buffer too small");
    160         return 0;
    161     } else {
    162         *slen = encap_slen;
    163     }
    164 
    165     /* ML-KEM encapsulation */
    166     encap_clen = key->minfo->ctext_bytes;
    167     encap_slen = ML_KEM_SHARED_SECRET_BYTES;
    168     cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
    169     sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
    170     ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
    171     if (ctx == NULL
    172         || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
    173         || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)
    174         goto end;
    175     if (encap_clen != key->minfo->ctext_bytes) {
    176         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    177             "unexpected %s ciphertext output size: %lu",
    178             key->minfo->algorithm_name, (unsigned long)encap_clen);
    179         goto end;
    180     }
    181     if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) {
    182         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    183             "unexpected %s shared secret output size: %lu",
    184             key->minfo->algorithm_name, (unsigned long)encap_slen);
    185         goto end;
    186     }
    187     EVP_PKEY_CTX_free(ctx);
    188 
    189     /*-
    190      * ECDHE encapsulation
    191      *
    192      * Generate own ephemeral private key and add its public key to ctext.
    193      *
    194      * Note, we could support a settable parameter that sets an extant ECDH
    195      * keypair as the keys to use in encap, making it possible to reuse the
    196      * same (TLS client) ECDHE keypair for both the classical EC keyshare and a
    197      * corresponding ECDHE + ML-KEM keypair.  But the TLS layer would then need
    198      * know that this is a hybrid, and that it can partly reuse the same keys
    199      * as another group for which a keyshare will be sent.  Deferred until we
    200      * support generating multiple keyshares, there's a workable keyshare
    201      * prediction specification, and the optimisation is justified.
    202      */
    203     cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
    204     encap_clen = key->xinfo->pubkey_bytes;
    205     ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
    206     if (ctx == NULL
    207         || EVP_PKEY_keygen_init(ctx) <= 0
    208         || EVP_PKEY_keygen(ctx, &xkey) <= 0
    209         || EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
    210                cbuf, encap_clen, &encap_clen)
    211             <= 0)
    212         goto end;
    213     if (encap_clen != key->xinfo->pubkey_bytes) {
    214         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    215             "unexpected %s public key output size: %lu",
    216             key->xinfo->algorithm_name, (unsigned long)encap_clen);
    217         goto end;
    218     }
    219     EVP_PKEY_CTX_free(ctx);
    220 
    221     /* Derive the ECDH shared secret */
    222     encap_slen = key->xinfo->shsec_bytes;
    223     sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
    224     ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);
    225     if (ctx == NULL
    226         || EVP_PKEY_derive_init(ctx) <= 0
    227         || EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0
    228         || EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)
    229         goto end;
    230     if (encap_slen != key->xinfo->shsec_bytes) {
    231         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    232             "unexpected %s shared secret output size: %lu",
    233             key->xinfo->algorithm_name, (unsigned long)encap_slen);
    234         goto end;
    235     }
    236 
    237     ret = 1;
    238 end:
    239     EVP_PKEY_free(xkey);
    240     EVP_PKEY_CTX_free(ctx);
    241     return ret;
    242 }
    243 
    244 static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
    245     const uint8_t *ctext, size_t clen)
    246 {
    247     MLX_KEY *key = ((PROV_MLX_KEM_CTX *)vctx)->key;
    248     EVP_PKEY_CTX *ctx = NULL;
    249     EVP_PKEY *xkey = NULL;
    250     const uint8_t *cbuf;
    251     uint8_t *sbuf;
    252     size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
    253     size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
    254     int ml_kem_slot = key->xinfo->ml_kem_slot;
    255     int ret = 0;
    256 
    257     if (!mlx_kem_have_prvkey(key)) {
    258         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
    259         return 0;
    260     }
    261 
    262     if (shsec == NULL) {
    263         if (slen == NULL)
    264             return 0;
    265         *slen = decap_slen;
    266         return 1;
    267     }
    268 
    269     /* For now tolerate newly-deprecated NULL length pointers. */
    270     if (slen == NULL) {
    271         slen = &decap_slen;
    272     } else if (*slen < decap_slen) {
    273         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
    274             "shared-secret buffer too small");
    275         return 0;
    276     } else {
    277         *slen = decap_slen;
    278     }
    279     if (clen != decap_clen) {
    280         ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,
    281             "wrong decapsulation input ciphertext size: %lu",
    282             (unsigned long)clen);
    283         return 0;
    284     }
    285 
    286     /* ML-KEM decapsulation */
    287     decap_clen = key->minfo->ctext_bytes;
    288     decap_slen = ML_KEM_SHARED_SECRET_BYTES;
    289     cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
    290     sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
    291     ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
    292     if (ctx == NULL
    293         || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
    294         || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)
    295         goto end;
    296     if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {
    297         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    298             "unexpected %s shared secret output size: %lu",
    299             key->minfo->algorithm_name, (unsigned long)decap_slen);
    300         goto end;
    301     }
    302     EVP_PKEY_CTX_free(ctx);
    303 
    304     /* ECDH decapsulation */
    305     decap_clen = key->xinfo->pubkey_bytes;
    306     decap_slen = key->xinfo->shsec_bytes;
    307     cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
    308     sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
    309     ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
    310     if (ctx == NULL
    311         || (xkey = EVP_PKEY_new()) == NULL
    312         || EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0
    313         || EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0
    314         || EVP_PKEY_derive_init(ctx) <= 0
    315         || EVP_PKEY_derive_set_peer(ctx, xkey) <= 0
    316         || EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)
    317         goto end;
    318     if (decap_slen != key->xinfo->shsec_bytes) {
    319         ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
    320             "unexpected %s shared secret output size: %lu",
    321             key->xinfo->algorithm_name, (unsigned long)decap_slen);
    322         goto end;
    323     }
    324 
    325     ret = 1;
    326 end:
    327     EVP_PKEY_CTX_free(ctx);
    328     EVP_PKEY_free(xkey);
    329     return ret;
    330 }
    331 
    332 const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {
    333     { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC)mlx_kem_newctx },
    334     { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC)mlx_kem_encapsulate_init },
    335     { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC)mlx_kem_encapsulate },
    336     { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC)mlx_kem_decapsulate_init },
    337     { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC)mlx_kem_decapsulate },
    338     { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC)mlx_kem_freectx },
    339     { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC)mlx_kem_set_ctx_params },
    340     { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC)mlx_kem_settable_ctx_params },
    341     OSSL_DISPATCH_END
    342 };
    343