diff --git a/0058-Allow-hybrid-MLKEM-in-FIPS-mode.patch b/0058-Allow-hybrid-MLKEM-in-FIPS-mode.patch new file mode 100644 index 0000000..b139ecc --- /dev/null +++ b/0058-Allow-hybrid-MLKEM-in-FIPS-mode.patch @@ -0,0 +1,302 @@ +From 26ad3b905a6d4b1fa50b304f21f67aa0d35265e9 Mon Sep 17 00:00:00 2001 +From: Dmitry Belyavskiy +Date: Fri, 30 May 2025 16:17:37 +0200 +Subject: [PATCH 58/58] 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 ec75233435..8d0cc1a82c 100644 +--- a/crypto/ml_kem/ml_kem.c ++++ b/crypto/ml_kem/ml_kem.c +@@ -1581,6 +1581,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) + return NULL; +@@ -1588,15 +1589,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 + + 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 ++# include ++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.49.0 + diff --git a/openssl.spec b/openssl.spec index c7f09fb..d7d9edd 100644 --- a/openssl.spec +++ b/openssl.spec @@ -97,6 +97,9 @@ Patch0054: 0054-crypto-disable-OSSL_PARAM_REAL-on-UEFI.patch Patch0055: 0055-hashfunc-add-stddef.h-include.patch Patch0056: 0056-rio-add-RIO_POLL_METHOD_NONE.patch Patch0057: 0057-apps-x509.c-Fix-the-addreject-option-adding-trust-in.patch +%if ( %{defined rhel} && (! %{defined centos}) ) +Patch0058: 0058-Allow-hybrid-MLKEM-in-FIPS-mode.patch +%endif License: Apache-2.0 URL: http://www.openssl.org/ @@ -438,6 +441,8 @@ touch $RPM_BUILD_ROOT/%{_prefix}/include/openssl/engine.h * Mon Jun 02 2025 Dmitry Belyavskiy - 1:3.5.0-5 - Compact patches for better maintainability Related: RHEL-80811 +- Make hybrid MLKEM work with our FIPS provider (3.0.7) + Resolves: RHEL-94614 * Thu May 22 2025 Dmitry Belyavskiy - 1:3.5.0-4 - Fix regressions caused by rebase to OpenSSL 3.5