File 1901-crypto-Add-SLH-DSA-algorithms-for-sign-verify.patch of Package erlang

From a0602a1b92efa0fa857904d1825ff6a1a5084610 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Wed, 20 Aug 2025 16:43:51 +0200
Subject: [PATCH 1/2] crypto: Add SLH-DSA algorithms for sign/verify

SLH-DSA-SHAKE-256s
SLH-DSA-SHAKE-256f
SLH-DSA-SHA2-256s
SLH-DSA-SHA2-256f
---
 lib/crypto/c_src/algorithms.c     |  16 +-
 lib/crypto/c_src/atoms.c          |  10 -
 lib/crypto/c_src/atoms.h          |   5 -
 lib/crypto/c_src/crypto.c         |   4 +-
 lib/crypto/c_src/evp.c            |  19 +-
 lib/crypto/c_src/openssl_config.h |   7 +
 lib/crypto/c_src/pkey.c           | 431 +++++++++++++++++-------------
 lib/crypto/c_src/pkey.h           |  25 +-
 lib/crypto/src/crypto.erl         |  44 ++-
 lib/crypto/test/crypto_SUITE.erl  |   6 +-
 10 files changed, 328 insertions(+), 239 deletions(-)

diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c
index 2e1465e15e..5b718f2bd3 100644
--- a/lib/crypto/c_src/algorithms.c
+++ b/lib/crypto/c_src/algorithms.c
@@ -26,6 +26,7 @@
 #include "mac.h"
 #ifdef HAS_3_0_API
 #include "digest.h"
+#include "pkey.h"
 #endif
 
 #ifdef HAS_3_0_API
@@ -160,10 +161,14 @@ void init_hash_types(ErlNifEnv* env) {
 
 ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
 {
-    unsigned int cnt  =
-        FIPS_MODE() ? algo_pubkey_fips_cnt : algo_pubkey_cnt;
+    const bool fips = FIPS_MODE();
+    unsigned int cnt  = fips ? algo_pubkey_fips_cnt : algo_pubkey_cnt;
+    ERL_NIF_TERM list = enif_make_list_from_array(env, algo_pubkey, cnt);
 
-    return enif_make_list_from_array(env, algo_pubkey, cnt);
+#ifdef HAS_3_0_API
+    list = build_pkey_type_list(env, list, fips);
+#endif
+    return list;
 }
 
 void init_pubkey_types(ErlNifEnv* env) {
@@ -193,11 +198,6 @@ void init_pubkey_types(ErlNifEnv* env) {
     algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddh");
 #endif
     algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp");
-#ifdef HAVE_ML_DSA
-    algo_pubkey[algo_pubkey_cnt++] = atom_mldsa44;
-    algo_pubkey[algo_pubkey_cnt++] = atom_mldsa65;
-    algo_pubkey[algo_pubkey_cnt++] = atom_mldsa87;
-#endif
     ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM));
 }
 
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index 41e171afbc..b1741cab46 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -151,13 +151,8 @@ ERL_NIF_TERM atom_key_id;
 ERL_NIF_TERM atom_password;
 #endif
 
-#ifdef HAVE_ML_DSA
-ERL_NIF_TERM atom_mldsa44;
-ERL_NIF_TERM atom_mldsa65;
-ERL_NIF_TERM atom_mldsa87;
 ERL_NIF_TERM atom_seed;
 ERL_NIF_TERM atom_expandedkey;
-#endif
 
 #ifdef HAVE_ML_KEM
 ERL_NIF_TERM atom_mlkem512;
@@ -288,13 +283,8 @@ int init_atoms(ErlNifEnv *env) {
     atom_password = enif_make_atom(env,"password");
 #endif
 
-#ifdef HAVE_ML_DSA
-    atom_mldsa44 = enif_make_atom(env,"mldsa44");
-    atom_mldsa65 = enif_make_atom(env,"mldsa65");
-    atom_mldsa87 = enif_make_atom(env,"mldsa87");
     atom_seed = enif_make_atom(env,"seed");
     atom_expandedkey = enif_make_atom(env,"expandedkey");
-#endif
 #ifdef HAVE_ML_KEM
     atom_mlkem512  = enif_make_atom(env,"mlkem512");
     atom_mlkem768  = enif_make_atom(env,"mlkem768");
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 4ee6a56057..928297a017 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -150,13 +150,8 @@ extern ERL_NIF_TERM atom_key_id;
 extern ERL_NIF_TERM atom_password;
 #endif
 
-#ifdef HAVE_ML_DSA
-extern ERL_NIF_TERM atom_mldsa44;
-extern ERL_NIF_TERM atom_mldsa65;
-extern ERL_NIF_TERM atom_mldsa87;
 extern ERL_NIF_TERM atom_seed;
 extern ERL_NIF_TERM atom_expandedkey;
-#endif
 #ifdef HAVE_ML_KEM
 extern ERL_NIF_TERM atom_mlkem512;
 extern ERL_NIF_TERM atom_mlkem768;
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index aad48c45eb..7cb4068063 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -102,6 +102,7 @@ static ErlNifFunc nif_funcs[] = {
     
     {"pbkdf2_hmac_nif", 5, pbkdf2_hmac_nif, 0},
     {"pkey_sign_nif", 5, pkey_sign_nif, 0},
+    {"pkey_sign_heavy_nif", 5, pkey_sign_heavy_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
     {"pkey_verify_nif", 6, pkey_verify_nif, 0},
     {"pkey_crypt_nif", 6, pkey_crypt_nif, 0},
     {"encapsulate_key_nif", 2, encapsulate_key_nif, 0},
@@ -275,9 +276,8 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
         /* Don't fail loading if the legacy provider is missing */
         prov_cnt++;
     }
-    prefetched_sign_algo_init();
-
 #endif
+    prefetched_sign_algo_init(env);
 
     if (!init_atoms(env)) {
         ret = __LINE__; goto done;
diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c
index f09eedb00e..26177659d8 100644
--- a/lib/crypto/c_src/evp.c
+++ b/lib/crypto/c_src/evp.c
@@ -38,7 +38,7 @@ ERL_NIF_TERM encapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
     ERL_NIF_TERM ret;
 
     if (!get_pkey_from_octet_string(env, argv[0], argv[1], PKEY_PUB,
-                                    &peer_pkey, &ret)) {
+                                    NULL, &peer_pkey, &ret)) {
         goto err;
     }
 
@@ -92,7 +92,7 @@ ERL_NIF_TERM decapsulate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
         assign_goto(ret, err, EXCP_ERROR_N(env, 2, "Invalid encapsulated secret"));
     }
     if (!get_pkey_from_octet_string(env, argv[0], argv[1], PKEY_PRIV,
-                                    &my_pkey, &ret)) {
+                                    NULL, &my_pkey, &ret)) {
         goto err;
     }
 
@@ -229,8 +229,12 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
     ErlNifBinary prv_key;
     size_t key_len;
     unsigned char *out_pub = NULL, *out_priv = NULL;
+    struct pkey_type_t *pkey_type = get_pkey_type(argv[0]);
 
-    if (argv[0] == atom_x25519)
+    if (pkey_type) {
+        type = pkey_type->evp_pkey_id;
+    }
+    else if (argv[0] == atom_x25519)
         type = EVP_PKEY_X25519;
 #ifdef HAVE_X448
     else if (argv[0] == atom_x448)
@@ -242,15 +246,6 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
     else if (argv[0] == atom_ed448)
         type = EVP_PKEY_ED448;
 #endif
-#ifdef HAVE_ML_DSA
-    else if (argv[0] == atom_mldsa44) {
-        type = EVP_PKEY_ML_DSA_44;
-    } else if (argv[0] == atom_mldsa65) {
-        type = EVP_PKEY_ML_DSA_65;
-    } else if (argv[0] == atom_mldsa87) {
-        type = EVP_PKEY_ML_DSA_87;
-    }
-#endif
 #ifdef HAVE_ML_KEM
     else if (argv[0] == atom_mlkem512) {
         type = NID_ML_KEM_512;
diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h
index 9957b58d98..f525aeccc1 100644
--- a/lib/crypto/c_src/openssl_config.h
+++ b/lib/crypto/c_src/openssl_config.h
@@ -389,6 +389,10 @@
 #endif
 #endif
 
+#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,4,0)
+#  define HAS_PREFETCH_SIGN_INIT
+#endif
+
 #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,5,0)
 #  ifndef OPENSSL_NO_ML_KEM
 #    define HAVE_ML_KEM
@@ -396,6 +400,9 @@
 #  ifndef OPENSSL_NO_ML_DSA
 #    define HAVE_ML_DSA
 #  endif
+#  ifndef OPENSSL_NO_SLH_DSA
+#    define HAVE_SLH_DSA
+#  endif
 #endif
 
 #if defined(HAS_ENGINE_SUPPORT)
diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c
index f10998baff..6646a69f3e 100644
--- a/lib/crypto/c_src/pkey.c
+++ b/lib/crypto/c_src/pkey.c
@@ -49,28 +49,33 @@ static int check_pkey_algorithm_type(ErlNifEnv *env,
                                      int allow_unknown,
                                      ERL_NIF_TERM *err_return);
 static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
+                                const struct pkey_type_t *pkey_type,
                                 int type_arg_num, ERL_NIF_TERM type,
 				const EVP_MD **md,
                                 ERL_NIF_TERM *err_return);
 static int get_pkey_sign_digest(ErlNifEnv *env,
                                 const ERL_NIF_TERM argv[],
                                 int algorithm_arg_num, int type_arg_num, int data_arg_num,
+                                const struct pkey_type_t *pkey_type,
 				unsigned char *md_value, const EVP_MD **mdp,
 				unsigned char **tbsp, size_t *tbslenp,
                                 ERL_NIF_TERM *err_return);
 static int get_pkey_sign_options(ErlNifEnv *env,
                                  const ERL_NIF_TERM argv[],
                                  int algorithm_arg_num, int options_arg_num,
+                                 const struct pkey_type_t *pkey_type,
                                  const EVP_MD *md, PKeySignOptions *opt,
                                  ERL_NIF_TERM *err_return);
 static int get_pkey_private_key(ErlNifEnv *env,
                                const ERL_NIF_TERM argv[],
                                int algorithm_arg_num, int key_arg_num,
+                               struct pkey_type_t *pkey_type,
 			       EVP_PKEY **pkey,
                                ERL_NIF_TERM *err_return);
 static int get_pkey_public_key(ErlNifEnv *env,
                                const ERL_NIF_TERM argv[],
                                int algorithm_arg_num, int key_arg_num,
+                               struct pkey_type_t *pkey_type,
 			       EVP_PKEY **pkey,
                                ERL_NIF_TERM *err_return);
 static int get_pkey_crypt_options(ErlNifEnv *env,
@@ -82,38 +87,87 @@ static int get_pkey_crypt_options(ErlNifEnv *env,
 static size_t size_of_RSA(EVP_PKEY *pkey);
 #endif
 
+struct pkey_type_t pkey_types[] = {
 #ifdef HAVE_ML_DSA
-static EVP_SIGNATURE* the_mldsa44_algo;
-static EVP_SIGNATURE* the_mldsa65_algo;
-static EVP_SIGNATURE* the_mldsa87_algo;
+    {
+        .name.atom_str = "mldsa44",
+        .evp_pkey_id = EVP_PKEY_ML_DSA_44,
+        .sign.alg_str = "mldsa44",
+        .allow_seed = true
+    },
+    {
+        .name.atom_str = "mldsa65",
+        .evp_pkey_id = EVP_PKEY_ML_DSA_65,
+        .sign.alg_str = "mldsa65",
+        .allow_seed = true
+    },
+    {
+        .name.atom_str = "mldsa87",
+        .evp_pkey_id = EVP_PKEY_ML_DSA_87,
+        .sign.alg_str = "mldsa87",
+        .allow_seed = true
+    },
+#endif
+#ifdef HAVE_SLH_DSA
+    {
+        .name.atom_str = "slh_dsa_shake_256s",
+        .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_256S,
+        .sign.alg_str = "SLH-DSA-SHAKE-256s"
+    },
+    {
+        .name.atom_str = "slh_dsa_shake_256f",
+        .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_256F,
+        .sign.alg_str = "SLH-DSA-SHAKE-256f"
+    },
+    {
+        .name.atom_str = "slh_dsa_sha2_256s",
+        .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_256S,
+        .sign.alg_str = "SLH-DSA-SHA2-256s"
+    },
+    {
+        .name.atom_str = "slh_dsa_sha2_256f",
+        .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_256F,
+        .sign.alg_str = "SLH-DSA-SHA2-256f"
+    },
 #endif
 
-void prefetched_sign_algo_init(void)
+    {.name.atom_str = NULL}
+};
+struct pkey_type_t *pkey_types_end;
+
+
+void prefetched_sign_algo_init(ErlNifEnv* env)
 {
-#ifdef HAVE_ML_DSA
-    the_mldsa44_algo = EVP_SIGNATURE_fetch(NULL, "mldsa44", NULL);
-    the_mldsa65_algo = EVP_SIGNATURE_fetch(NULL, "mldsa65", NULL);
-    the_mldsa87_algo = EVP_SIGNATURE_fetch(NULL, "mldsa87", NULL);
+    struct pkey_type_t* p;
+    for (p = pkey_types; p->name.atom_str; p++) {
+        p->name.atom = enif_make_atom(env, p->name.atom_str);
+#ifdef HAS_PREFETCH_SIGN_INIT
+        p->sign.alg = EVP_SIGNATURE_fetch(NULL, p->sign.alg_str, NULL);
 #endif
+    }
+    pkey_types_end = p;
 }
 
-#ifdef HAS_3_0_API
-static EVP_SIGNATURE* get_prefetched_sign_algo(ERL_NIF_TERM alg_atom)
+struct pkey_type_t* get_pkey_type(ERL_NIF_TERM alg_atom)
 {
-#ifdef HAVE_ML_DSA
-    if (alg_atom == atom_mldsa44) {
-        return the_mldsa44_algo;
-    }
-    else if (alg_atom == atom_mldsa65) {
-        return the_mldsa65_algo;
-    }
-    else if (alg_atom == atom_mldsa87) {
-        return the_mldsa87_algo;
+    for (struct pkey_type_t* p = pkey_types; p != pkey_types_end; p++) {
+        if (alg_atom == p->name.atom) {
+            return p;
+        }
     }
-#endif
     return NULL;
 }
-#endif
+
+ERL_NIF_TERM build_pkey_type_list(ErlNifEnv* env, ERL_NIF_TERM tail, bool fips)
+{
+    ERL_NIF_TERM list = tail;
+    if (!fips) {
+        for (struct pkey_type_t* p = pkey_types; p != pkey_types_end; p++) {
+            list = enif_make_list_cell(env, p->name.atom, list);
+        }
+    }
+    return list;
+}
 
 static int check_pkey_algorithm_type(ErlNifEnv *env,
                                      int alg_arg_num, ERL_NIF_TERM algorithm,
@@ -156,7 +210,9 @@ static int check_pkey_algorithm_type(ErlNifEnv *env,
 }
 
 
-static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
+static int get_pkey_digest_type(ErlNifEnv *env,
+                                ERL_NIF_TERM algorithm,
+                                const struct pkey_type_t *pkey_type,
                                 int type_arg_num, ERL_NIF_TERM type,
 				const EVP_MD **md,
                                 ERL_NIF_TERM *err_return)
@@ -168,13 +224,9 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
         if (algorithm == atom_rsa) {
             return 1;
         }
-#ifdef HAVE_ML_DSA
-        if (algorithm == atom_mldsa44 ||
-            algorithm == atom_mldsa65 ||
-            algorithm == atom_mldsa87) {
+        if (pkey_type && algorithm == pkey_type->name.atom) {
             return 1;
         }
-#endif
     }
 
     if (algorithm == atom_eddsa) /* Type was skipped for eddsa in < OTP-25
@@ -200,7 +252,9 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm,
 
 static int get_pkey_sign_digest(ErlNifEnv *env,
                                 const ERL_NIF_TERM argv[],
-                                int algorithm_arg_num, int type_arg_num, int data_arg_num,
+                                int algorithm_arg_num,
+                                int type_arg_num, int data_arg_num,
+                                const struct pkey_type_t *pkey_type,
 				unsigned char *md_value, const EVP_MD **mdp,
 				unsigned char **tbsp, size_t *tbslenp,
                                 ERL_NIF_TERM *err_return)
@@ -224,7 +278,8 @@ static int get_pkey_sign_digest(ErlNifEnv *env,
         goto err; /* An exception is present in ret */
     }
     
-    if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
+    if (!get_pkey_digest_type(env,
+                              argv[algorithm_arg_num], pkey_type,
                               type_arg_num, argv[type_arg_num],
                               &md, err_return))
         goto err; /* An exception is present in ret */
@@ -292,6 +347,7 @@ static int get_pkey_sign_digest(ErlNifEnv *env,
 static int get_pkey_sign_options(ErlNifEnv *env,
                                  const ERL_NIF_TERM argv[],
                                  int algorithm_arg_num, int options_arg_num,
+                                 const struct pkey_type_t *pkey_type,
                                  const EVP_MD *md, PKeySignOptions *opt,
                                  ERL_NIF_TERM *err_return)
 {
@@ -331,6 +387,7 @@ static int get_pkey_sign_options(ErlNifEnv *env,
                 assign_goto(*err_return, err, EXCP_BADARG_N(env, options_arg_num, "Atom expected as argument to option rsa_mgf1_md"));
 
             if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
+                                      pkey_type,
                                       options_arg_num, tpl_terms[1],
                                       &opt_md, err_return))
                 goto err; /* An exception is present in ret */
@@ -380,6 +437,7 @@ static int get_pkey_private_key(ErlNifEnv *env,
                                 const ERL_NIF_TERM argv[],
                                 int algorithm_arg_num,
                                 int key_arg_num,
+                                struct pkey_type_t *pkey_type,
                                 EVP_PKEY **pkey,
                                 ERL_NIF_TERM *err_return)
 {
@@ -440,29 +498,29 @@ static int get_pkey_private_key(ErlNifEnv *env,
 #else
         enum pkey_format_t pkey_format = PKEY_PRIV;
         ERL_NIF_TERM key_bin = argv[key_arg_num];
+        const ERL_NIF_TERM* tpl_array;
+        int tpl_arity;
 
-#  ifdef HAVE_ML_DSA
-        if (argv[algorithm_arg_num] == atom_mldsa44 ||
-            argv[algorithm_arg_num] == atom_mldsa65 ||
-            argv[algorithm_arg_num] == atom_mldsa87) {
-            int tpl_arity;
-            const ERL_NIF_TERM* tpl_array;
+        if (enif_get_tuple(env, argv[key_arg_num], &tpl_arity, &tpl_array)
+            && tpl_arity == 2) {
 
-            if (!enif_get_tuple(env, argv[key_arg_num], &tpl_arity, &tpl_array)
-                || tpl_arity != 2) {
-                assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "MLDSA key not 2-tuple"));
-            }
             if (tpl_array[0] == atom_seed) {
-                pkey_format = PKEY_PRIV_SEED;
+                if (pkey_type && pkey_type->allow_seed) {
+                    pkey_format = PKEY_PRIV_SEED;
+                }
+                else {
+                    assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Seed key not allowed"));
+                }
             }
             else if (tpl_array[0] != atom_expandedkey) {
-                assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Invalid MLDSA key tuple"));
+                assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Invalid key tuple format"));
             }
             key_bin = tpl_array[1];
         }
-#  endif
         if (!get_pkey_from_octet_string(env, argv[algorithm_arg_num], key_bin,
-                                        pkey_format, pkey, err_return)) {
+                                        pkey_format,
+                                        pkey_type,
+                                        pkey, err_return)) {
             goto err;
         }
 #endif
@@ -490,10 +548,10 @@ int get_pkey_from_octet_string(ErlNifEnv *env,
                                ERL_NIF_TERM alg_atom,
                                ERL_NIF_TERM key_bin,
                                enum pkey_format_t pkey_format,
+                               struct pkey_type_t *pkey_type,
                                EVP_PKEY **pkey_p,
                                ERL_NIF_TERM *ret_p)
 {
-    char type[40];
     OSSL_PARAM params[2];
     int i = 0;
     EVP_PKEY *pkey = NULL;
@@ -502,9 +560,23 @@ int get_pkey_from_octet_string(ErlNifEnv *env,
     const char* key_type;
     int selection;
 
-    if (!enif_get_atom(env, alg_atom, type, sizeof(type), ERL_NIF_UTF8)) {
-        assign_goto(*ret_p, err, EXCP_ERROR_N(env, 0, "Invalid key type"));
+    if (pkey_type) {
+        ctx = EVP_PKEY_CTX_new_id(pkey_type->evp_pkey_id, NULL);
+        if (!ctx) {
+            assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't create PKEY_CTX from id"));
+        }
     }
+    else {
+        char type[40];
+        if (!enif_get_atom(env, alg_atom, type, sizeof(type), ERL_NIF_UTF8)) {
+            assign_goto(*ret_p, err, EXCP_ERROR_N(env, 0, "Invalid key type"));
+        }
+        ctx = EVP_PKEY_CTX_new_from_name(NULL, type, NULL);
+        if (!ctx) {
+            assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't create PKEY_CTX from name"));
+        }
+    }
+
     if (!enif_inspect_binary(env, key_bin, &keyb)) {
         assign_goto(*ret_p, err, EXCP_ERROR_N(env, 1, "Invalid public key"));
     }
@@ -529,11 +601,6 @@ int get_pkey_from_octet_string(ErlNifEnv *env,
     params[i++] = OSSL_PARAM_construct_octet_string(key_type, keyb.data, keyb.size);
     params[i++] = OSSL_PARAM_construct_end();
 
-    ctx = EVP_PKEY_CTX_new_from_name(NULL, type, NULL);
-    if (!ctx) {
-        assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't create PKEY_CTX from name"));
-    }
-
     if (EVP_PKEY_fromdata_init(ctx) <= 0) {
         assign_goto(*ret_p, err, EXCP_ERROR(env, "Can't init fromdata"));
     }
@@ -562,6 +629,7 @@ err:
 static int get_pkey_public_key(ErlNifEnv *env,
                                const ERL_NIF_TERM argv[],
                                int algorithm_arg_num, int key_arg_num,
+                               struct pkey_type_t *pkey_type,
 			       EVP_PKEY **pkey,
                                ERL_NIF_TERM *err_return)
 {
@@ -618,7 +686,7 @@ static int get_pkey_public_key(ErlNifEnv *env,
     } else {
 #ifdef HAS_3_0_API
         if (!get_pkey_from_octet_string(env, argv[algorithm_arg_num], argv[key_arg_num],
-                                        PKEY_PUB, pkey, err_return)) {
+                                        PKEY_PUB, pkey_type, pkey, err_return)) {
             goto err;
         }
 #else
@@ -645,6 +713,12 @@ static int get_pkey_public_key(ErlNifEnv *env,
     goto done;
 }
 
+ERL_NIF_TERM pkey_sign_heavy_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+    // We are running on a dirty CPU scheduler.
+    return pkey_sign_nif(env, argc, argv);
+}
+
 ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
 {/* (Algorithm, Type, Data|{digest,Digest}, Key|#{}, Options) */
     int sig_bin_alloc = 0;
@@ -653,6 +727,7 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
     unsigned char md_value[EVP_MAX_MD_SIZE];
     EVP_PKEY *pkey = NULL;
 #ifdef HAS_EVP_PKEY_CTX
+    struct pkey_type_t *pkey_type = get_pkey_type(argv[0]);
     EVP_PKEY_CTX *ctx = NULL;
     size_t siglen;
 #else
@@ -675,46 +750,63 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
     if (enif_is_map(env, argv[3]))
         assign_goto(ret, err, EXCP_BADARG_N(env, 3, "No engine support"));
 #endif
-    if (!get_pkey_sign_digest(env, argv, 0, 1, 2, md_value, &md, &tbs, &tbslen, &ret))
+    if (!get_pkey_sign_digest(env, argv, 0, 1, 2, pkey_type, md_value, &md, &tbs, &tbslen, &ret))
         goto err; /* An exception is present in ret */
 
-    if (!get_pkey_sign_options(env, argv, 0, 4, md, &sig_opt, &ret))
+    if (!get_pkey_sign_options(env, argv, 0, 4, pkey_type, md, &sig_opt, &ret))
         goto err; /* An exception is present in ret */
 
 #ifdef HAS_EVP_PKEY_CTX
-    { /* EVP_MD_CTX */
-        if (!get_pkey_private_key(env, argv, 0, 3, &pkey, &ret))
-            goto err; /* An exception is present in ret */
+    if (!get_pkey_private_key(env, argv, 0, 3, pkey_type, &pkey, &ret))
+        goto err; /* An exception is present in ret */
 
-        if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL)
-            assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
+    if (argv[0] == atom_eddsa) {
+# ifdef HAVE_EDDSA
+        if (!FIPS_MODE()) {
+            EVP_MD_CTX *mdctx = NULL;
+            if ((mdctx = EVP_MD_CTX_new()) == NULL)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_MD_CTX_new"));
 
-        if (argv[0] != atom_eddsa) {
-#ifdef HAS_3_0_API
-            EVP_SIGNATURE *sig_alg = get_prefetched_sign_algo(argv[0]);
+            if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSignInit"));
+            if (EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSign"));
+            if (!enif_alloc_binary(siglen, &sig_bin))
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate binary"));
+            sig_bin_alloc = 1;
 
-            if (!sig_alg) {
-                if (EVP_PKEY_sign_init(ctx) != 1) {
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_init"));
-                }
-            }
-            else {
-#  ifdef HAVE_ML_DSA
-                if (EVP_PKEY_sign_message_init(ctx, sig_alg, NULL) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_message_init"));
-#  else
-                assign_goto(ret, err, EXCP_ERROR(env, "Unknown signature algorithm"));
-#  endif
+            if (EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen) != 1) {
+                if (mdctx)
+                    EVP_MD_CTX_free(mdctx);
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSign"));
             }
-#else
+            if (mdctx)
+                EVP_MD_CTX_free(mdctx);
+        }
+        else
+# endif
+            assign_goto(ret, err, EXCP_NOTSUP_N(env, 0, "eddsa not supported"));
+    }
+    else {
+        if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) {
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
+        }
+# ifdef HAS_PREFETCH_SIGN_INIT
+        if (pkey_type && pkey_type->sign.alg) {
+            if (EVP_PKEY_sign_message_init(ctx, pkey_type->sign.alg, NULL) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_message_init"));
+        }
+        else
+# endif
+        {
             if (EVP_PKEY_sign_init(ctx) != 1) {
                 assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign_init"));
             }
-#endif
-            if (md != NULL) {
-                if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
-            }
+        }
+
+        if (md != NULL) {
+            if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
         }
 
         if (argv[0] == atom_rsa) {
@@ -738,46 +830,19 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
 # endif
         }
 
-        if (argv[0] == atom_eddsa) {
-# ifdef HAVE_EDDSA
-            if (!FIPS_MODE()) {
-                EVP_MD_CTX *mdctx = NULL;
-                if ((mdctx = EVP_MD_CTX_new()) == NULL)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_MD_CTX_new"));
+        if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) != 1)
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign"));
 
-                if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSignInit"));
-                if (EVP_DigestSign(mdctx, NULL, &siglen, tbs, tbslen) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSign"));
-                if (!enif_alloc_binary(siglen, &sig_bin))
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate binary"));
-                sig_bin_alloc = 1;
-
-                if (EVP_DigestSign(mdctx, sig_bin.data, &siglen, tbs, tbslen) != 1) {
-                    if (mdctx)
-                        EVP_MD_CTX_free(mdctx);
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestSign"));
-                }
-                if (mdctx)
-                    EVP_MD_CTX_free(mdctx);
-            }
-            else
-# endif
-                assign_goto(ret, err, EXCP_NOTSUP_N(env, 0, "eddsa not supported"));
-        } else {
-            if (EVP_PKEY_sign(ctx, NULL, &siglen, tbs, tbslen) != 1)
-                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign"));
+        if (!enif_alloc_binary(siglen, &sig_bin))
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate binary"));
+        sig_bin_alloc = 1;
 
-            if (!enif_alloc_binary(siglen, &sig_bin))
-                assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate binary"));
-            sig_bin_alloc = 1;
-
-            if (md != NULL) {
-                ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md));
-            }
-            if (EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen) != 1)
-                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign"));
+        if (md != NULL) {
+            ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md));
         }
+        if (EVP_PKEY_sign(ctx, sig_bin.data, &siglen, tbs, tbslen) != 1)
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_sign"));
+    }
 
     ERL_VALGRIND_MAKE_MEM_DEFINED(sig_bin.data, siglen);
     if (siglen != sig_bin.size) {
@@ -797,7 +862,7 @@ ERL_NIF_TERM pkey_sign_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
     if (pkey)
         EVP_PKEY_free(pkey);
     return ret;
-    }
+
     /* End of HAS_EVP_PKEY_CTX */
 #else
     /* Old interface - before EVP_PKEY_CTX */
@@ -909,68 +974,74 @@ ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]
     ERL_NIF_TERM ret = atom_undefined;
 
 #ifdef HAS_EVP_PKEY_CTX
+    struct pkey_type_t *pkey_type = get_pkey_type(argv[0]);
     EVP_PKEY_CTX *ctx = NULL;
 #else
     RSA *rsa = NULL;
 # ifdef HAVE_DSA
-        DSA *dsa = NULL;
+    DSA *dsa = NULL;
 # endif
 # ifdef HAVE_EC
-        EC_KEY *ec = NULL;
+    EC_KEY *ec = NULL;
 # endif
 #endif  // HAS_EVP_PKEY_CTX
 
-
 #ifndef HAS_ENGINE_SUPPORT
     if (enif_is_map(env, argv[3]))
         assign_goto(ret, err, EXCP_BADARG_N(env, 3, "No engine support"));
 #endif
 
-    if (!get_pkey_sign_digest(env, argv, 0, 1, 2, md_value, &md, &tbs, &tbslen, &ret))
+    if (!get_pkey_sign_digest(env, argv, 0, 1, 2, pkey_type, md_value, &md, &tbs, &tbslen, &ret))
         goto err; /* An exception is present in ret */
 
-    if (!get_pkey_sign_options(env, argv, 0, 5, md, &sig_opt, &ret))
+    if (!get_pkey_sign_options(env, argv, 0, 5, pkey_type, md, &sig_opt, &ret))
         goto err; /* An exception is present in ret */
 
     if (!enif_inspect_binary(env, argv[3], &sig_bin))
         assign_goto(ret, err, EXCP_BADARG_N(env, 3, "Expected a binary"));
 
 #ifdef HAS_EVP_PKEY_CTX
-    /* EVP_PKEY_CTX */
-    {
-        if (!get_pkey_public_key(env, argv, 0, 4, &pkey, &ret))
-            goto err; /* An exception is present in ret */
+    if (!get_pkey_public_key(env, argv, 0, 4, pkey_type, &pkey, &ret))
+        goto err; /* An exception is present in ret */
 
-        if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL)
-            assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
+    if (argv[0] == atom_eddsa) {
+# ifdef HAVE_EDDSA
+        EVP_MD_CTX *mdctx = NULL;
+        if (!FIPS_MODE()) {
+            if ((mdctx = EVP_MD_CTX_new()) == NULL)
+                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_MD_CTX_new"));
 
-        if (argv[0] != atom_eddsa) {
-#ifdef HAS_3_0_API
-            EVP_SIGNATURE *sig_alg = get_prefetched_sign_algo(argv[0]);
+            if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestVerifyInit"));
 
-            if (!sig_alg) {
-                if (EVP_PKEY_verify_init(ctx) != 1) {
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_init"));
-                }
-            }
-            else {
-#  ifdef HAVE_ML_DSA
-                if (EVP_PKEY_verify_message_init(ctx, sig_alg, NULL) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_message_init"));
-#  else
-                assign_goto(ret, err, EXCP_ERROR(env, "Unknown signature algorithm"));
-#  endif
-            }
-#else
+            result = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen);
+            if (mdctx)
+                EVP_MD_CTX_free(mdctx);
+        }
+        else
+# endif /* HAVE_EDDSA */
+            assign_goto(ret, err, EXCP_NOTSUP_N(env, 0, "eddsa not supported"));
+    }
+    else {
+        if ((ctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) {
+            assign_goto(ret, err, EXCP_ERROR(env, "Can't allocate new EVP_PKEY_CTX"));
+        }
+# ifdef HAS_PREFETCH_SIGN_INIT
+        if (pkey_type && pkey_type->sign.alg) {
+            if (EVP_PKEY_verify_message_init(ctx, pkey_type->sign.alg, NULL) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_message_init"));
+        }
+        else
+# endif
+        {
             if (EVP_PKEY_verify_init(ctx) != 1) {
                 assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_verify_init"));
-            }            
-#endif
-            if (md != NULL) {
-                if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
             }
         }
+        if (md != NULL) {
+            if (EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
+                assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_PKEY_CTX_set_signature_md"));
+        }
 
         if (argv[0] == atom_rsa) {
             if (EVP_PKEY_CTX_set_rsa_padding(ctx, sig_opt.rsa_padding) != 1)
@@ -993,46 +1064,28 @@ ERL_NIF_TERM pkey_verify_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]
 # endif
         }
 
-        if (argv[0] == atom_eddsa) {
-# ifdef HAVE_EDDSA
-            EVP_MD_CTX *mdctx = NULL;
-            if (!FIPS_MODE()) {
-                if ((mdctx = EVP_MD_CTX_new()) == NULL)
-                     assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_MD_CTX_new"));
-
-                if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1)
-                    assign_goto(ret, err, EXCP_ERROR(env, "Can't EVP_DigestVerifyInit"));
-
-                result = EVP_DigestVerify(mdctx, sig_bin.data, sig_bin.size, tbs, tbslen);
-                if (mdctx)
-                    EVP_MD_CTX_free(mdctx);
-            }
-            else
-# endif /* HAVE_EDDSA */
-                assign_goto(ret, err, EXCP_NOTSUP_N(env, 0, "eddsa not supported"));
-        } else {
-            /* RSA or DSS */
-            if (md != NULL) {
-                ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md));
-            }
-            result = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen);
+        /* RSA or DSS */
+        if (md != NULL) {
+            ERL_VALGRIND_ASSERT_MEM_DEFINED(tbs, EVP_MD_size(md));
         }
+        result = EVP_PKEY_verify(ctx, sig_bin.data, sig_bin.size, tbs, tbslen);
+    }
 
-        ret = (result == 1 ? atom_true : atom_false);
+    ret = (result == 1 ? atom_true : atom_false);
 
-    err:
-        if (ctx)
-            EVP_PKEY_CTX_free(ctx);
-        if (pkey)
-            EVP_PKEY_free(pkey);
+err:
+    if (ctx)
+        EVP_PKEY_CTX_free(ctx);
+    if (pkey)
+        EVP_PKEY_free(pkey);
+
+    return ret;
 
-        return ret;
-    }
     /* End of HAS_EVP_PKEY_CTX */
 #else
     /* Old interface - before EVP_PKEY_CTX */
     {
-        if (!get_pkey_public_key(env, argv, 0, 4, &pkey, &ret))
+        if (!get_pkey_public_key(env, argv, 0, 4, NULL, &pkey, &ret))
             goto err; /* An exception is present in ret */
 
         if (argv[0] == atom_rsa) {
@@ -1149,7 +1202,7 @@ static int get_pkey_crypt_options(ErlNifEnv *env,
             if (!enif_is_atom(env, tpl_terms[1]))
                 assign_goto(*err_return, err, EXCP_BADARG_N(env, options_arg_num, "Atom expected as argument to option signature_md"));
 
-            if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
+            if (!get_pkey_digest_type(env, argv[algorithm_arg_num], NULL,
                                       options_arg_num, tpl_terms[1],
                                       &opt_md, err_return))
                 goto err; /* An exception is present in ret */
@@ -1163,7 +1216,7 @@ static int get_pkey_crypt_options(ErlNifEnv *env,
             if (tpl_terms[1] != atom_sha)
                 assign_goto(*err_return, err, EXCP_BADARG_N(env, options_arg_num, "Only 'sha' is supported in option rsa_mgf1_md"));
 #endif
-            if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
+            if (!get_pkey_digest_type(env, argv[algorithm_arg_num], NULL,
                                       options_arg_num, tpl_terms[1],
                                       &opt_md, err_return))
                 goto err; /* An exception is present in ret */
@@ -1185,7 +1238,7 @@ static int get_pkey_crypt_options(ErlNifEnv *env,
             if (tpl_terms[1] != atom_sha)
                 assign_goto(*err_return, err, EXCP_BADARG_N(env, options_arg_num, "Only 'sha' is supported in option rsa_oaep_md"));
 #endif
-            if (!get_pkey_digest_type(env, argv[algorithm_arg_num],
+            if (!get_pkey_digest_type(env, argv[algorithm_arg_num], NULL,
                                       options_arg_num, tpl_terms[1],
                                       &opt_md, err_return))
                 goto err; /* An exception is present in ret */
@@ -1254,10 +1307,10 @@ ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
 #endif
 
     if (is_private) {
-        if (!get_pkey_private_key(env, argv, 0, 2, &pkey, &ret))
+        if (!get_pkey_private_key(env, argv, 0, 2, NULL, &pkey, &ret))
             goto err; /* An exception is present in ret */
     } else {
-        if (!get_pkey_public_key(env, argv, 0, 2, &pkey, &ret))
+        if (!get_pkey_public_key(env, argv, 0, 2, NULL, &pkey, &ret))
             goto err;  /* An exception is present in ret */
     }
 
@@ -1497,7 +1550,7 @@ ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
     if (!check_pkey_algorithm_type(env, 0, argv[0], 0, &ret))
         goto err; /* An exception is present in ret */
     
-    if (!get_pkey_private_key(env, argv, 0, 1, &pkey, &ret)) // handles engine
+    if (!get_pkey_private_key(env, argv, 0, 1, NULL, &pkey, &ret)) // handles engine
         goto err; /* An exception is present in ret */
 
     if (argv[0] == atom_rsa) {
diff --git a/lib/crypto/c_src/pkey.h b/lib/crypto/c_src/pkey.h
index 257a755aef..ba5f7c4423 100644
--- a/lib/crypto/c_src/pkey.h
+++ b/lib/crypto/c_src/pkey.h
@@ -25,23 +25,44 @@
 
 #include "common.h"
 
-void prefetched_sign_algo_init(void);
+void prefetched_sign_algo_init(ErlNifEnv*);
 
-#ifdef HAS_3_0_API
 enum pkey_format_t {
     PKEY_PUB  = 0,
     PKEY_PRIV = 1,
     PKEY_PRIV_SEED = 2
 };
+
+struct pkey_type_t {
+    union {
+        const char* atom_str;   // before init
+        ERL_NIF_TERM atom;      // after init
+    }name;
+    const int evp_pkey_id;
+    union {
+        const char* alg_str;    // before init
+#ifdef HAS_PREFETCH_SIGN_INIT
+        EVP_SIGNATURE* alg;     // after init
+#endif
+    } sign;
+    const bool allow_seed;
+};
+
+struct pkey_type_t* get_pkey_type(ERL_NIF_TERM alg_atom);
+ERL_NIF_TERM build_pkey_type_list(ErlNifEnv* env, ERL_NIF_TERM tail, bool fips);
+
+#ifdef HAS_3_0_API
 int get_pkey_from_octet_string(ErlNifEnv*,
                                ERL_NIF_TERM alg_atom,
                                ERL_NIF_TERM key_bin,
                                enum pkey_format_t,
+                               struct pkey_type_t *pkey_type,
                                EVP_PKEY **pkey_p,
                                ERL_NIF_TERM *ret_p);
 #endif
 
 ERL_NIF_TERM pkey_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM pkey_sign_heavy_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 ERL_NIF_TERM pkey_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 27dc2e51dd..53e392b412 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -279,6 +279,7 @@ end
        strong_rand_bytes_nif/1, strong_rand_range_nif/1, rand_uniform_nif/2,
        mod_exp_nif/4, do_exor/2, hash_equals_nif/2, pbkdf2_hmac_nif/5,
        pkey_sign_nif/5, pkey_verify_nif/6, pkey_crypt_nif/6,
+       pkey_sign_heavy_nif/5,
        encapsulate_key_nif/2, decapsulate_key_nif/3,
        rsa_generate_key_nif/2, dh_generate_key_nif/4, dh_compute_key_nif/3,
        evp_compute_key_nif/3, evp_generate_key_nif/2, privkey_to_pubkey_nif/2,
@@ -823,7 +824,7 @@ stop() ->
                              Hashs :: [sha1() | sha2() | sha3() | sha3_xof() | blake2() | ripemd160 | sm3 | compatibility_only_hash()],
                              Ciphers :: [cipher()],
                              KEMs :: [kem()],
-                             PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m | mldsa()],
+                             PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m | mldsa() | slh_dsa()],
                              Macs :: [hmac | cmac | poly1305],
                              Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()],
                              RSAopts :: [rsa_sign_verify_opt() | rsa_opt()] .
@@ -2438,10 +2439,13 @@ rand_seed_nif(_Seed) -> ?nif_stub.
 %%%================================================================
 -doc "Algorithms for sign and verify.".
 -doc(#{group => <<"Public Key Sign and Verify">>}).
--type pk_sign_verify_algs() :: rsa | dss | ecdsa | eddsa | mldsa().
+-type pk_sign_verify_algs() :: rsa | dss | ecdsa | eddsa | mldsa() | slh_dsa().
 -type mldsa() :: mldsa44 | mldsa65 | mldsa87.
 -type mldsa_private() :: {seed | expandedkey, binary()}.
 -type mldsa_public() :: binary().
+-type slh_dsa() :: slh_dsa_shake_256s | slh_dsa_shake_256f | slh_dsa_sha2_256s | slh_dsa_sha2_256f.
+-type slh_dsa_private() :: binary().
+-type slh_dsa_public() :: binary().
 
 -doc(#{group => <<"Public Key Sign and Verify">>,
        equiv => rsa_sign_verify_padding()}).
@@ -2488,6 +2492,7 @@ Options for sign and verify.
                            | [ecdsa_private() | ecdsa_params()]
                            | [eddsa_private() | eddsa_params()]
                            | mldsa_private()
+                           | slh_dsa_private()
                            | engine_key_ref(),
                       Signature :: binary() .
 
@@ -2522,18 +2527,36 @@ See also `public_key:sign/3`.
                            | [ecdsa_private() | ecdsa_params()]
                            | [eddsa_private() | eddsa_params()]
                            | mldsa_private()
+                           | slh_dsa_private()
                            | engine_key_ref(),
                       Options :: pk_sign_verify_opts(),
                       Signature :: binary() .
 
-sign(Algorithm0, Type0, Data, Key, Options) ->
+sign(Algorithm0, Type0, Data, Key0, Options) ->
     {Algorithm, Type} = sign_verify_compatibility(Algorithm0, Type0, Data),
-    ?nif_call(pkey_sign_nif(Algorithm, Type, Data, format_pkey(Algorithm, Key), Options),
-              {1, 2, 3, 4, 5},
-              [Algorithm0, Type0, Data, Key, Options]).
+    Key = format_pkey(Algorithm, Key0),
+    case is_heavy(Algorithm) of
+        false ->
+            ?nif_call(pkey_sign_nif(Algorithm, Type, Data, Key, Options),
+                      {1, 2, 3, 4, 5},
+                      [Algorithm0, Type0, Data, Key0, Options]);
+        true ->
+            ?nif_call(pkey_sign_heavy_nif(Algorithm, Type, Data, Key, Options),
+                      {1, 2, 3, 4, 5},
+                      [Algorithm0, Type0, Data, Key0, Options])
+    end.
+
 
 pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub.
 
+pkey_sign_heavy_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub.
+
+is_heavy(slh_dsa_shake_256s) -> true;
+is_heavy(slh_dsa_shake_256f) -> true;
+is_heavy(slh_dsa_sha2_256s) -> true;
+is_heavy(slh_dsa_sha2_256f) -> true;
+is_heavy(_) -> false.
+
 %%%----------------------------------------------------------------
 %%% Verify
 
@@ -2554,6 +2577,7 @@ pkey_sign_nif(_Algorithm, _Type, _Digest, _Key, _Options) -> ?nif_stub.
                              | [ecdsa_public() | ecdsa_params()]
                              | [eddsa_public() | eddsa_params()]
                              | mldsa_public()
+                             | slh_dsa_public()
                              | engine_key_ref(),
                         Result :: boolean().
 
@@ -2588,6 +2612,7 @@ See also `public_key:verify/4`.
                              | [ecdsa_public() | ecdsa_params()]
                              | [eddsa_public() | eddsa_params()]
                              | mldsa_public()
+                             | slh_dsa_public()
                              | engine_key_ref(),
                         Options :: pk_sign_verify_opts(),
                         Result :: boolean().
@@ -2793,11 +2818,10 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_
        since => <<"OTP R16B01">>}).
 -spec generate_key(Type, Params)
                  -> {PublicKey, PrivKeyOut}
-                        when Type :: dh | ecdh | eddh | eddsa | rsa | mldsa() | mlkem512 | mlkem768 | mlkem1024| srp,
+                        when Type :: dh | ecdh | eddh | eddsa | rsa | mldsa() | mlkem512 | mlkem768 | mlkem1024 | slh_dsa() | srp,
                              PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
                              PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
-                             Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_gen_params() | []
-                                       .
+                             Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_gen_params() | [].
 generate_key(Type, Params) ->
     generate_key(Type, Params, undefined).
 
@@ -2825,7 +2849,7 @@ Uses the [3-tuple style](`m:crypto#error_3tup`) for error handling.
        since => <<"OTP R16B01">>}).
 -spec generate_key(Type, Params, PrivKeyIn)
                  -> {PublicKey, PrivKeyOut}
-                        when Type :: dh | ecdh | eddh | eddsa | rsa | mldsa() | mlkem512 | mlkem768 | mlkem1024 | srp,
+                        when Type :: dh | ecdh | eddh | eddsa | rsa | mldsa() | mlkem512 | mlkem768 | mlkem1024 | srp | slh_dsa(),
                              PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
                              PrivKeyIn :: undefined | dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
                              PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 6f780d467f..cd348278f9 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -1302,7 +1302,11 @@ sign_verify_oqs_do(Alg) ->
 
 %% Supported by OpenSSL 3.5
 mldsa_sign_ciphers() ->
-    [mldsa44, mldsa65, mldsa87].
+    [mldsa44, mldsa65, mldsa87,
+     slh_dsa_shake_256s,
+     slh_dsa_shake_256f,
+     slh_dsa_sha2_256s,
+     slh_dsa_sha2_256f].
 
 %%--------------------------------------------------------------------
 use_all_ec_sign_verify(_Config) ->
-- 
2.51.0

openSUSE Build Service is sponsored by