From 217d8f5853670ae2245ad8d31faee411a68c997a Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Fri, 30 May 2025 16:17:37 +0200
Subject: [PATCH 53/59] Allow hybrid MLKEM in FIPS mode

---
 crypto/ml_kem/ml_kem.c                        | 11 ++--
 include/crypto/ml_kem.h                       |  2 +
 providers/defltprov.c                         |  8 +--
 providers/implementations/kem/mlx_kem.c       | 33 +++++++++-
 providers/implementations/keymgmt/mlx_kmgmt.c | 61 ++++++++++++++++++-
 5 files changed, 103 insertions(+), 12 deletions(-)

diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c
index 716c3bf427..6ae9c9c5b5 100644
--- a/crypto/ml_kem/ml_kem.c
+++ b/crypto/ml_kem/ml_kem.c
@@ -1613,6 +1613,7 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
 {
     const ML_KEM_VINFO *vinfo = ossl_ml_kem_get_vinfo(evp_type);
     ML_KEM_KEY *key;
+    char *adjusted_propq = NULL;
 
     if (vinfo == NULL) {
         ERR_raise_data(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT,
@@ -1623,15 +1624,17 @@ ML_KEM_KEY *ossl_ml_kem_key_new(OSSL_LIB_CTX *libctx, const char *properties,
     if ((key = OPENSSL_malloc(sizeof(*key))) == NULL)
         return NULL;
 
+    adjusted_propq = get_adjusted_propq(properties);
     key->vinfo = vinfo;
     key->libctx = libctx;
     key->prov_flags = ML_KEM_KEY_PROV_FLAGS_DEFAULT;
-    key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", properties);
-    key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", properties);
-    key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", properties);
-    key->sha3_512_md = EVP_MD_fetch(libctx, "SHA3-512", properties);
+    key->shake128_md = EVP_MD_fetch(libctx, "SHAKE128", adjusted_propq ? adjusted_propq : properties);
+    key->shake256_md = EVP_MD_fetch(libctx, "SHAKE256", adjusted_propq ? adjusted_propq : properties);
+    key->sha3_256_md = EVP_MD_fetch(libctx, "SHA3-256", adjusted_propq ? adjusted_propq : properties);
+    key->sha3_512_md = EVP_MD_fetch(libctx, "SHA3-512", adjusted_propq ? adjusted_propq : properties);
     key->d = key->z = key->rho = key->pkhash = key->encoded_dk = NULL;
     key->s = key->m = key->t = NULL;
+    OPENSSL_free(adjusted_propq);
 
     if (key->shake128_md != NULL
         && key->shake256_md != NULL
diff --git a/include/crypto/ml_kem.h b/include/crypto/ml_kem.h
index 67d55697e9..ab1aaae8ac 100644
--- a/include/crypto/ml_kem.h
+++ b/include/crypto/ml_kem.h
@@ -278,4 +278,6 @@ int ossl_ml_kem_decap(uint8_t *shared_secret, size_t slen,
 __owur
 int ossl_ml_kem_pubkey_cmp(const ML_KEM_KEY *key1, const ML_KEM_KEY *key2);
 
+char *get_adjusted_propq(const char *propq);
+
 #endif  /* OPENSSL_HEADER_ML_KEM_H */
diff --git a/providers/defltprov.c b/providers/defltprov.c
index eee2178b41..0dba017f3f 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -517,8 +517,8 @@ static const OSSL_ALGORITHM deflt_asym_kem[] = {
     { "X448MLKEM1024", "provider=default", ossl_mlx_kem_asym_kem_functions },
 # endif
 # if !defined(OPENSSL_NO_EC)
-    { "SecP256r1MLKEM768", "provider=default", ossl_mlx_kem_asym_kem_functions },
-    { "SecP384r1MLKEM1024", "provider=default", ossl_mlx_kem_asym_kem_functions },
+    { "SecP256r1MLKEM768", "provider=default,fips=yes", ossl_mlx_kem_asym_kem_functions },
+    { "SecP384r1MLKEM1024", "provider=default,fips=yes", ossl_mlx_kem_asym_kem_functions },
 # endif
 #endif
     { NULL, NULL, NULL }
@@ -597,9 +597,9 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
       PROV_DESCS_X448MLKEM1024 },
 # endif
 # if !defined(OPENSSL_NO_EC)
-    { PROV_NAMES_SecP256r1MLKEM768, "provider=default", ossl_mlx_p256_kem_kmgmt_functions,
+    { PROV_NAMES_SecP256r1MLKEM768, "provider=default,fips=yes", ossl_mlx_p256_kem_kmgmt_functions,
       PROV_DESCS_SecP256r1MLKEM768 },
-    { PROV_NAMES_SecP384r1MLKEM1024, "provider=default", ossl_mlx_p384_kem_kmgmt_functions,
+    { PROV_NAMES_SecP384r1MLKEM1024, "provider=default,fips=yes", ossl_mlx_p384_kem_kmgmt_functions,
       PROV_DESCS_SecP384r1MLKEM1024 },
 # endif
 #endif
diff --git a/providers/implementations/kem/mlx_kem.c b/providers/implementations/kem/mlx_kem.c
index 197c345d85..08fbf99a76 100644
--- a/providers/implementations/kem/mlx_kem.c
+++ b/providers/implementations/kem/mlx_kem.c
@@ -19,6 +19,7 @@
 #include "prov/mlx_kem.h"
 #include "prov/provider_ctx.h"
 #include "prov/providercommon.h"
+#include <string.h>
 
 static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;
 static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;
@@ -103,6 +104,28 @@ mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
     return 1;
 }
 
+char *get_adjusted_propq(const char *propq)
+{
+    char *adjusted_propq = NULL;
+    const char *nofips = "-fips";
+    size_t len = propq ? strlen(propq) + 1 + strlen(nofips) + 1 :
+                                             strlen(nofips) + 1;
+    char *ptr = NULL;
+
+    adjusted_propq = OPENSSL_zalloc(len);
+    if (adjusted_propq != NULL) {
+        ptr = adjusted_propq;
+        if (propq && strlen(propq) > 0) {
+            memcpy(ptr, propq, strlen(propq));
+            ptr += strlen(propq);
+            *ptr = ',';
+            ptr++;
+        }
+        memcpy(ptr, nofips, strlen(nofips));
+    }
+    return adjusted_propq;
+}
+
 static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
                                unsigned char *shsec, size_t *slen)
 {
@@ -115,6 +138,7 @@ static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
     uint8_t *sbuf;
     int ml_kem_slot = key->xinfo->ml_kem_slot;
     int ret = 0;
+    char *adjusted_propq = NULL;
 
     if (!mlx_kem_have_pubkey(key)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
@@ -167,7 +191,8 @@ static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
     encap_slen = ML_KEM_SHARED_SECRET_BYTES;
     cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
     sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
-    ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
+    adjusted_propq = get_adjusted_propq(key->propq);
+    ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, adjusted_propq ? adjusted_propq : key->propq);
     if (ctx == NULL
         || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
         || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)
@@ -237,6 +262,7 @@ static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
  end:
     EVP_PKEY_free(xkey);
     EVP_PKEY_CTX_free(ctx);
+    OPENSSL_free(adjusted_propq);
     return ret;
 }
 
@@ -252,6 +278,7 @@ static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
     size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
     int ml_kem_slot = key->xinfo->ml_kem_slot;
     int ret = 0;
+    char *adjusted_propq = NULL;
 
     if (!mlx_kem_have_prvkey(key)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
@@ -287,7 +314,8 @@ static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
     decap_slen = ML_KEM_SHARED_SECRET_BYTES;
     cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
     sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
-    ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
+    adjusted_propq = get_adjusted_propq(key->propq);
+    ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, adjusted_propq ? adjusted_propq : key->propq);
     if (ctx == NULL
         || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
         || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)
@@ -325,6 +353,7 @@ static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
  end:
     EVP_PKEY_CTX_free(ctx);
     EVP_PKEY_free(xkey);
+    OPENSSL_free(adjusted_propq);
     return ret;
 }
 
diff --git a/providers/implementations/keymgmt/mlx_kmgmt.c b/providers/implementations/keymgmt/mlx_kmgmt.c
index bea8783276..aeef0c8f84 100644
--- a/providers/implementations/keymgmt/mlx_kmgmt.c
+++ b/providers/implementations/keymgmt/mlx_kmgmt.c
@@ -156,6 +156,52 @@ typedef struct export_cb_arg_st {
     size_t   prvlen;
 } EXPORT_CB_ARG;
 
+#ifndef FIPS_MODULE
+# include <openssl/bn.h>
+# include <openssl/ec.h>
+static size_t decompress_pub_key(void *pub, size_t compressed_len, size_t decompressed_len)
+{
+    EC_GROUP *group = NULL;
+    EC_POINT *point = NULL;
+    BN_CTX *ctx = NULL;
+    size_t len = compressed_len;
+    int group_nid = NID_undef;
+
+    switch (len) {
+    case 33:
+         group_nid = NID_X9_62_prime256v1;
+       break;
+    case 49:
+         group_nid = NID_secp384r1;
+       break;
+    default:
+       return len;
+       break;
+    }
+
+    ctx = BN_CTX_new();
+    group = EC_GROUP_new_by_curve_name(group_nid);
+    if (ctx == NULL || group == NULL)
+        goto err;
+
+    point = EC_POINT_new(group);
+    if (point == NULL)
+        goto err;
+
+    if (!EC_POINT_oct2point(group, point, pub, len, ctx))
+        goto err;
+
+    len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, pub, decompressed_len, ctx);
+
+err:
+    EC_POINT_free(point);
+    EC_GROUP_free(group);
+    BN_CTX_free(ctx);
+
+    return len;
+}
+#endif
+
 /* Copy any exported key material into its storage slot */
 static int export_sub_cb(const OSSL_PARAM *params, void *varg)
 {
@@ -176,6 +222,10 @@ static int export_sub_cb(const OSSL_PARAM *params, void *varg)
 
         if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1)
             return 0;
+#ifndef FIPS_MODULE
+        if (len < sub_arg->publen)
+            len = decompress_pub_key(pub, len, sub_arg->publen);
+#endif
         if (len != sub_arg->publen) {
             ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
                            "Unexpected %s public key length %lu != %lu",
@@ -344,12 +394,14 @@ load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,
     void *val;
     int ml_kem_slot = key->xinfo->ml_kem_slot;
     int ret = 0;
+    char *adjusted_propq = NULL;
 
     if (slot == ml_kem_slot) {
         alg = key->minfo->algorithm_name;
         ppkey = &key->mkey;
         off = slot * xbytes;
         len = mbytes;
+        adjusted_propq = get_adjusted_propq(propq);
     } else {
         alg = key->xinfo->algorithm_name;
         group = (char *) key->xinfo->group_name;
@@ -359,7 +411,8 @@ load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,
     }
     val = (void *)(in + off);
 
-    if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL
+    if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg,
+                                          adjusted_propq ? adjusted_propq : propq)) == NULL
         || EVP_PKEY_fromdata_init(ctx) <= 0)
         goto err;
     parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len);
@@ -370,6 +423,7 @@ load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,
         ret = 1;
 
  err:
+    OPENSSL_free(adjusted_propq);
     EVP_PKEY_CTX_free(ctx);
     return ret;
 }
@@ -688,6 +742,7 @@ static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
     PROV_ML_KEM_GEN_CTX *gctx = vgctx;
     MLX_KEY *key;
     char *propq;
+    char *adjusted_propq = NULL;
 
     if (gctx == NULL
         || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
@@ -704,8 +759,10 @@ static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
         return key;
 
     /* For now, using the same "propq" for all components */
-    key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
+    adjusted_propq = get_adjusted_propq(propq);
+    key->mkey = EVP_PKEY_Q_keygen(key->libctx, adjusted_propq ? adjusted_propq : key->propq,
                                   key->minfo->algorithm_name);
+    OPENSSL_free(adjusted_propq);
     key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
                                   key->xinfo->algorithm_name,
                                   key->xinfo->group_name);
-- 
2.51.0

