From 095922b3bb59326c903f38f85be4fb61fb378de6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 20 Aug 2025 16:43:51 +0200 Subject: [PATCH 1/3] 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 | 18 +- 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 | 429 +++++++++++++++++------------- 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 2e1465e15e43..72d031396027 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 @@ -36,7 +37,7 @@ void init_hash_types(ErlNifEnv* env); #endif static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; -static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ +static ERL_NIF_TERM algo_pubkey[16]; /* increase when extending the list */ void init_pubkey_types(ErlNifEnv* env); static ERL_NIF_TERM algo_curve[2][89]; /* increase when extending the list */ @@ -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 41e171afbccd..b1741cab468e 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 4ee6a5605716..928297a0178a 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 aad48c45eb9f..7cb406806398 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 f09eedb00e8f..26177659d859 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 9957b58d985a..f525aeccc1af 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 f10998bafff4..9dc87e1bbe24 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")); } 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 @@ int get_pkey_from_octet_string(ErlNifEnv *env, 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 257a755aef47..ba5f7c442350 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 27dc2e51ddd9..53e392b4123b 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 6f780d467fad..cd348278f92f 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) -> From 10022f2accbd193de8d843f571738c2d46bac381 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 9 Oct 2025 17:27:55 +0200 Subject: [PATCH 2/3] crypto: Add 128 and 192 variants of SLH-DSA --- lib/crypto/c_src/pkey.c | 40 ++++++++++++++++++++++++++++++++ lib/crypto/src/crypto.erl | 12 +++++++++- lib/crypto/test/crypto_SUITE.erl | 15 ++++++++++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 9dc87e1bbe24..031778887906 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -109,6 +109,46 @@ struct pkey_type_t pkey_types[] = { }, #endif #ifdef HAVE_SLH_DSA + { + .name.atom_str = "slh_dsa_shake_128s", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_128S, + .sign.alg_str = "SLH-DSA-SHAKE-128s" + }, + { + .name.atom_str = "slh_dsa_shake_128f", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_128F, + .sign.alg_str = "SLH-DSA-SHAKE-128f" + }, + { + .name.atom_str = "slh_dsa_sha2_128s", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_128S, + .sign.alg_str = "SLH-DSA-SHA2-128s" + }, + { + .name.atom_str = "slh_dsa_sha2_128f", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_128F, + .sign.alg_str = "SLH-DSA-SHA2-128f" + }, + { + .name.atom_str = "slh_dsa_shake_192s", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_192S, + .sign.alg_str = "SLH-DSA-SHAKE-192s" + }, + { + .name.atom_str = "slh_dsa_shake_192f", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_192F, + .sign.alg_str = "SLH-DSA-SHAKE-192f" + }, + { + .name.atom_str = "slh_dsa_sha2_192s", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_192S, + .sign.alg_str = "SLH-DSA-SHA2-192s" + }, + { + .name.atom_str = "slh_dsa_sha2_192f", + .evp_pkey_id = EVP_PKEY_SLH_DSA_SHA2_192F, + .sign.alg_str = "SLH-DSA-SHA2-192f" + }, { .name.atom_str = "slh_dsa_shake_256s", .evp_pkey_id = EVP_PKEY_SLH_DSA_SHAKE_256S, diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 53e392b4123b..334ecda840a2 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -2443,7 +2443,9 @@ rand_seed_nif(_Seed) -> ?nif_stub. -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() :: slh_dsa_shake_128s | slh_dsa_shake_128f | slh_dsa_sha2_128s | slh_dsa_sha2_128f + | slh_dsa_shake_192s | slh_dsa_shake_192f | slh_dsa_sha2_192s | slh_dsa_sha2_192f + | 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(). @@ -2551,6 +2553,14 @@ 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_128s) -> true; +is_heavy(slh_dsa_shake_128f) -> true; +is_heavy(slh_dsa_sha2_128s) -> true; +is_heavy(slh_dsa_sha2_128f) -> true; +is_heavy(slh_dsa_shake_192s) -> true; +is_heavy(slh_dsa_shake_192f) -> true; +is_heavy(slh_dsa_sha2_192s) -> true; +is_heavy(slh_dsa_sha2_192f) -> true; is_heavy(slh_dsa_shake_256s) -> true; is_heavy(slh_dsa_shake_256f) -> true; is_heavy(slh_dsa_sha2_256s) -> true; diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index cd348278f92f..c94cd2ff26ce 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -1283,7 +1283,7 @@ sign_verify_oqs(_Config) -> true = lists:member(Alg, Supported), sign_verify_oqs_do(Alg) end - || Alg <- mldsa_sign_ciphers()], + || Alg <- quantum_sign_ciphers()], ok end. @@ -1301,8 +1301,19 @@ sign_verify_oqs_do(Alg) -> ok. %% Supported by OpenSSL 3.5 -mldsa_sign_ciphers() -> +quantum_sign_ciphers() -> [mldsa44, mldsa65, mldsa87, + + slh_dsa_shake_128s, + slh_dsa_shake_128f, + slh_dsa_sha2_128s, + slh_dsa_sha2_128f, + + slh_dsa_shake_192s, + slh_dsa_shake_192f, + slh_dsa_sha2_192s, + slh_dsa_sha2_192f, + slh_dsa_shake_256s, slh_dsa_shake_256f, slh_dsa_sha2_256s, From 000f0584c6504d0f91124841923326abfe63e8fb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 14 Oct 2025 17:58:50 +0200 Subject: [PATCH 3/3] wx: Fix typo in api_gen/gen_util.erl --- lib/wx/api_gen/gen_util.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wx/api_gen/gen_util.erl b/lib/wx/api_gen/gen_util.erl index 57acd1fcaaf8..f02ac47bde05 100644 --- a/lib/wx/api_gen/gen_util.erl +++ b/lib/wx/api_gen/gen_util.erl @@ -299,8 +299,8 @@ append_license(_) -> c_copyright() -> {CurrentYear,_,_} = erlang:date(), w("/*~n",[]), - w(" * %CopyrightBegin% - w(" * + w(" * %CopyrightBegin%~n",[]), + w(" *~n",[]), w(" * SPDX-License-Identifier: Apache-2.0~n",[]), w(" *~n",[]), w(" * Copyright Ericsson AB 2008-~p. All Rights Reserved.~n",[CurrentYear]),