From aa72b5e9449d49928dd1f4ee704107482d3499ff Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Tue, 14 Oct 2025 04:36:06 +0200 Subject: [PATCH 01/15] Infrastructure for returning additional fields in crypto:supports/0, /1 fips_forbidden_* impl for digests fips_forbidden_* impl for public keys fips_forbidden_* impl for ciphers fips_forbidden_* impl for MACs fips_forbidden_* impl for KEM fips_forbidden_* impl for curves Use stdint/stdbool types already available FIPS doc page updated --- lib/crypto/c_src/aead.c | 4 +- lib/crypto/c_src/algorithms.c | 656 +++++++++++++++++++++++++--------- lib/crypto/c_src/algorithms.h | 15 + lib/crypto/c_src/api_ng.c | 2 +- lib/crypto/c_src/cipher.c | 128 ++++--- lib/crypto/c_src/cipher.h | 42 +-- lib/crypto/c_src/common.h | 1 - lib/crypto/c_src/crypto.c | 19 +- lib/crypto/c_src/dh.c | 7 +- lib/crypto/c_src/digest.c | 79 ++-- lib/crypto/c_src/digest.h | 25 +- lib/crypto/c_src/fips.c | 14 +- lib/crypto/c_src/hash.c | 12 +- lib/crypto/c_src/mac.c | 132 ++++--- lib/crypto/c_src/mac.h | 2 +- lib/crypto/c_src/pkey.c | 2 +- lib/crypto/doc/guides/fips.md | 110 +++--- lib/crypto/src/crypto.erl | 165 ++++++--- 18 files changed, 964 insertions(+), 451 deletions(-) diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c index 9cb2ff8e18b6..5e04d176da67 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -111,7 +111,7 @@ ERL_NIF_TERM aead_cipher_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a {ret = EXCP_BADARG_N(env, 0, "Bad cipher"); goto done;} if (! (ctx_res->cipherp->flags & AEAD_CIPHER) ) {ret = EXCP_BADARG_N(env, 0, "Not aead cipher"); goto done;} - if (CIPHER_FORBIDDEN_IN_FIPS(ctx_res->cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(ctx_res->cipherp)) {ret = EXCP_NOTSUP_N(env, 0, "Forbidden in FIPS"); goto done;} #if defined(HAVE_GCM_EVP_DECRYPT_BUG) @@ -210,7 +210,7 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] {ret = EXCP_BADARG_N(env, 0, "Bad cipher"); goto done;} if (! (cipherp->flags & AEAD_CIPHER) ) {ret = EXCP_BADARG_N(env, 0, "Not aead cipher"); goto done;} - if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(cipherp)) {ret = EXCP_NOTSUP_N(env, 0, "Forbidden in FIPS"); goto done;} #if defined(HAVE_GCM_EVP_DECRYPT_BUG) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 2e1465e15e43..cc341fa54665 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -20,34 +20,108 @@ * %CopyrightEnd% */ -#include "common.h" #include "algorithms.h" #include "cipher.h" +#include "common.h" #include "mac.h" +#include "pkey.h" + +#include #ifdef HAS_3_0_API #include "digest.h" #endif #ifdef HAS_3_0_API #else -static unsigned int algo_hash_cnt, algo_hash_fips_cnt; +static size_t algo_hash_cnt, algo_hash_fips_cnt; static ERL_NIF_TERM algo_hash[17]; /* increase when extending the list */ 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 */ +struct pkey_availability_t { + const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ + unsigned flags; /* combination of PKEY_AVAIL_FLAGS */ + ERL_NIF_TERM atom; /* added to results when the user is querying */ +}; + +enum PKEY_AVAIL_FLAGS { + FIPS_PKEY_NOT_AVAIL = 1, + FIPS_FORBIDDEN_PKEY_KEYGEN = 2, /* not available by name */ + FIPS_FORBIDDEN_PKEY_SIGN = 4, /* not available for signing */ + FIPS_FORBIDDEN_PKEY_VERIFY = 8, /* not available for verification */ + FIPS_FORBIDDEN_PKEY_ENCRYPT = 16, /* not available for encryption */ + FIPS_FORBIDDEN_PKEY_DERIVE = 32, /* not available for key derivation */ + FIPS_FORBIDDEN_PKEY_ALL = FIPS_FORBIDDEN_PKEY_KEYGEN | FIPS_FORBIDDEN_PKEY_SIGN | + FIPS_FORBIDDEN_PKEY_VERIFY | FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE +}; + +#ifdef FIPS_SUPPORT +# define IS_PUBKEY_FORBIDDEN_IN_FIPS(p) (((p)->flags == FIPS_FORBIDDEN_PKEY_ALL || (p)->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE()) +#else +# define IS_PUBKEY_FORBIDDEN_IN_FIPS(P) false +#endif + +/* Stores all known public key algorithms with their FIPS unavailability flag if FIPS is enabled */ +static struct pkey_availability_array_t { + size_t count; + struct pkey_availability_t algorithm[12]; /* increase when extending the list */ +} algo_pubkey; + +struct kem_availability_t { + const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ + unsigned flags; /* combination of KEM_AVAIL_FLAGS */ + ERL_NIF_TERM atom; /* as returned to the library user on a query */ +}; + +enum KEM_AVAIL_FLAGS { + FIPS_KEM_NOT_AVAIL = 1, +}; + +#ifdef FIPS_SUPPORT +# define IS_KEM_FORBIDDEN_IN_FIPS(p) (((p)->flags & FIPS_KEM_NOT_AVAIL) == FIPS_KEM_NOT_AVAIL && FIPS_MODE()) +#else +# define IS_KEM_FORBIDDEN_IN_FIPS(P) false +#endif + +/* Stores all known KEM algorithms with their FIPS unavailability flag if + * FIPS is enabled */ +static struct kem_availability_array_t { + size_t count; + struct kem_availability_t algorithm[3]; /* increase when extending the list */ +} algo_kem; + void init_pubkey_types(ErlNifEnv* env); +void init_kem_types(void); -static ERL_NIF_TERM algo_curve[2][89]; /* increase when extending the list */ -static ErlNifMutex* mtx_init_curve_types; -static int get_curve_cnt(ErlNifEnv* env, int fips); +struct curve_availability_t { + const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ + unsigned flags; /* combination of CURVE_AVAIL_FLAGS */ + ERL_NIF_TERM atom; /* as returned to the library user on a query */ +}; -static unsigned int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; -static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ -void init_rsa_opts_types(ErlNifEnv* env); +enum CURVE_AVAIL_FLAGS { + FIPS_CURVE_INIT_FAILED = 1, /* could not find by name or initialize */ +}; + +#ifdef FIPS_SUPPORT +# define IS_CURVE_FORBIDDEN_IN_FIPS(p) ((p)->flags != 0 && FIPS_MODE()) +#else +# define IS_CURVE_FORBIDDEN_IN_FIPS(P) false +#endif +static struct curve_availability_array_t { + ssize_t count; /* Negative -1 serves as a flag for lazy initiazlilization */ + /* [0] contains non-FIPS, and [1] contains FIPS curve details */ + struct curve_availability_t algorithms[89]; /* increase when extending the list */ + ErlNifMutex* mtx_init_curve_types; +} algo_curve = {-1, {0}, NULL}; + +static size_t curves_lazy_init(ErlNifEnv* env, bool fips_enabled); + +static size_t algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; +static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ +void init_rsa_opts_types(ErlNifEnv* env); void init_algorithms_types(ErlNifEnv* env) @@ -57,6 +131,7 @@ void init_algorithms_types(ErlNifEnv* env) init_hash_types(env); #endif init_pubkey_types(env); + init_kem_types(); init_rsa_opts_types(env); /* ciphers and macs are initiated statically */ } @@ -64,17 +139,17 @@ void init_algorithms_types(ErlNifEnv* env) int create_curve_mutex(void) { - if (!mtx_init_curve_types) { - mtx_init_curve_types = enif_mutex_create("init_curve_types"); + if (!algo_curve.mtx_init_curve_types) { + algo_curve.mtx_init_curve_types = enif_mutex_create("init_curve_types"); } - return !!mtx_init_curve_types; + return !!algo_curve.mtx_init_curve_types; } void destroy_curve_mutex(void) { - if (mtx_init_curve_types) { - enif_mutex_destroy(mtx_init_curve_types); - mtx_init_curve_types = NULL; + if (algo_curve.mtx_init_curve_types) { + enif_mutex_destroy(algo_curve.mtx_init_curve_types); + algo_curve.mtx_init_curve_types = NULL; } } @@ -85,7 +160,7 @@ void destroy_curve_mutex(void) ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef HAS_3_0_API - return digest_types_as_list(env); + return digest_types_as_list(env, false); #else unsigned int cnt = FIPS_MODE() ? algo_hash_fips_cnt : algo_hash_cnt; @@ -150,7 +225,7 @@ void init_hash_types(ErlNifEnv* env) { algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); #endif - ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(algo_hash[0])); } #endif @@ -158,56 +233,192 @@ void init_hash_types(ErlNifEnv* env) { Public key algorithms */ +static ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) { + ERL_NIF_TERM hd = enif_make_list(env, 0); + + for (size_t i = 0; i < algo_pubkey.count; i++) { + struct pkey_availability_t* algo = &algo_pubkey.algorithm[i]; + + /* Any of the forbidden flags is not set, then something is available */ + if (IS_PUBKEY_FORBIDDEN_IN_FIPS(algo) == fips_forbidden) { + hd = enif_make_list_cell(env, algo->atom, hd); + } + } + return hd; +} + 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; + /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false */ + return pubkey_algorithms_as_list(env, false); +} - return enif_make_list_from_array(env, algo_pubkey, cnt); +static void add_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, + const unsigned unavailable, ERL_NIF_TERM atom) +{ + struct pkey_availability_t* algo = &algo_pubkey.algorithm[algo_pubkey.count]; + algo->str_v3 = str_v3; + algo->flags = unavailable; + + if (!atom) atom = enif_make_atom(env, str_v3); + algo->atom = atom; + + algo_pubkey.count++; +} + +/* + * for FIPS will attempt to initialize the pubkey context to verify whether the + * algorithm is allowed, for non-FIPS the old behavior - always allow. + * Pass 0 for atom to create one right here. + */ +static void probe_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, ERL_NIF_TERM atom) { + unsigned unavailable = 0; +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); + /* failed: algorithm not available, do not add */ + if (ctx) { + if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ + unavailable |= FIPS_FORBIDDEN_PKEY_KEYGEN; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_sign_init(ctx) <= 0) { /* can't sign */ + unavailable |= FIPS_FORBIDDEN_PKEY_SIGN; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_verify_init(ctx) <= 0) { /* can't verify */ + unavailable |= FIPS_FORBIDDEN_PKEY_VERIFY; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_encrypt_init(ctx) <= 0) { /* can't encrypt/decrypt */ + unavailable |= FIPS_FORBIDDEN_PKEY_ENCRYPT; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_derive_init(ctx) <= 0) { /* can't derive */ + unavailable |= FIPS_FORBIDDEN_PKEY_DERIVE; + } + EVP_PKEY_CTX_free(ctx); + } else { + unavailable |= FIPS_PKEY_NOT_AVAIL; + } +#endif /* FIPS_SUPPORT && HAS_3_0_API */ + add_pubkey_algorithm(env, str_v3, unavailable, atom); } void init_pubkey_types(ErlNifEnv* env) { // Validated algorithms first - algo_pubkey_cnt = 0; - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); + algo_pubkey.count = 0; + probe_pubkey_algorithm(env, "rsa", 0); #ifdef HAVE_DSA - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); + probe_pubkey_algorithm(env, "dss", 0); #endif + #ifdef HAVE_DH - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); + probe_pubkey_algorithm(env, "dh", 0); #endif + #if defined(HAVE_EC) #if !defined(OPENSSL_NO_EC2M) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); + probe_pubkey_algorithm(env, "ec_gf2m", 0); #endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); + probe_pubkey_algorithm(env, "ecdsa", 0); + probe_pubkey_algorithm(env, "ecdh", 0); #endif + // Non-validated algorithms follow - algo_pubkey_fips_cnt = algo_pubkey_cnt; // Don't know if Edward curves are fips validated #if defined(HAVE_EDDSA) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddsa"); + probe_pubkey_algorithm(env, "eddsa", 0); #endif #if defined(HAVE_EDDH) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddh"); + probe_pubkey_algorithm(env, "eddh", 0); #endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); + probe_pubkey_algorithm(env, "srp", 0); #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; + probe_pubkey_algorithm(env, "mldsa44", atom_mldsa44); + probe_pubkey_algorithm(env, "mldsa65", atom_mldsa65); + probe_pubkey_algorithm(env, "mldsa87", atom_mldsa87); +#endif + /* When adding a new algorithm, update the size of algo_pubkey.algorithm array */ + ASSERT(algo_pubkey.count <= sizeof(algo_pubkey.algorithm)/sizeof(algo_pubkey.algorithm[0])); +} + +#ifdef HAVE_ML_KEM +static void add_kem_algorithm(const char* str_v3, const unsigned unavail_flags, ERL_NIF_TERM atom) { + struct kem_availability_t* algo = &algo_kem.algorithm[algo_kem.count]; + algo->str_v3 = str_v3; + algo->flags = unavail_flags; + algo->atom = atom; + algo_kem.count++; +} +#endif + +#ifdef HAVE_ML_KEM +/* + * for FIPS will attempt to initialize the KEM context to verify whether the + * algorithm is allowed, for non-FIPS the old behavior - always allow. + */ +static void probe_kem_algorithm(const char* str_v3, ERL_NIF_TERM atom) { + unsigned unavailable = 0; +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + EVP_KEM *kem = EVP_KEM_fetch(NULL, str_v3, "fips=yes"); + if (!kem) { + return; /* not available by name */ + } + + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); + /* failed: algorithm not available, do not add */ + if (ctx) { + if (EVP_PKEY_encapsulate_init(ctx, NULL) == 1) { + EVP_PKEY_CTX_free(ctx); + } else { + unavailable |= FIPS_KEM_NOT_AVAIL; + } + } + + EVP_KEM_free(kem); +#endif /* FIPS_SUPPORT && HAS_3_0_API */ + + add_kem_algorithm(str_v3, unavailable, atom); +} +#endif + +void init_kem_types(void) { + algo_kem.count = 0; +#ifdef HAVE_ML_KEM + probe_kem_algorithm("mlkem512", atom_mlkem512); + probe_kem_algorithm("mlkem768", atom_mlkem768); + probe_kem_algorithm("mlkem1024", atom_mlkem1024); + /* When adding a new algorithm, update the size of algo_kem.algorithm array */ #endif - ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_kem.count <= sizeof(algo_kem.algorithm)/sizeof(algo_kem.algorithm[0])); +} + +static ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) +{ + ERL_NIF_TERM hd = enif_make_list(env, 0); + + for (size_t i = 0; i < algo_kem.count; i++) { + struct kem_availability_t* p = &algo_kem.algorithm[i]; + if (IS_KEM_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { + hd = enif_make_list_cell(env, p->atom, hd); + } + } + + return hd; } ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef HAVE_ML_KEM - return enif_make_list3(env, - atom_mlkem512, - atom_mlkem768, - atom_mlkem1024); + return kem_algorithms_as_list(env, false); #else return enif_make_list(env, 0); #endif @@ -220,7 +431,8 @@ ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - return cipher_types_as_list(env); /* Exclude old api ciphers */ + /* Exclude old API ciphers. Filter the results by IS_CIPHER_FORBIDDEN_IN_FIPS() == false */ + return cipher_types_as_list(env, false); } @@ -230,7 +442,7 @@ ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - return mac_types_as_list(env); + return mac_types_as_list(env, false); } @@ -238,409 +450,453 @@ ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) Curves */ -ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) { - int fips_mode; - int algo_curve_cnt; + ERL_NIF_TERM hd = enif_make_list(env, 0); + + for (size_t i = 0; i < algo_curve.count; i++) { + struct curve_availability_t *p = &algo_curve.algorithms[i]; + + if (IS_CURVE_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { + hd = enif_make_list_cell(env, p->atom, hd); + } + } - fips_mode = FIPS_MODE(); - algo_curve_cnt = get_curve_cnt(env, fips_mode); + return hd; +} - return enif_make_list_from_array(env, algo_curve[fips_mode], algo_curve_cnt); +ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + curves_lazy_init(env, FIPS_MODE()); + return curve_algorithms_as_list(env, false); } -static int init_curves(ErlNifEnv* env, int fips); +static void init_curves(ErlNifEnv* env, bool fips); #if defined(HAVE_EC) -static int valid_curve(int nid); +static bool is_curve_valid_by_nid(int nid); #endif -int get_curve_cnt(ErlNifEnv* env, int fips) { - static int algo_curve_cnt = -1; - static int algo_curve_fips_cnt = -1; - int cnt = 0; - if (0 == fips && algo_curve_cnt >= 0) { - return algo_curve_cnt; +/* Perform late lazy init of curve algorithms, hence the need for mutex */ +static size_t curves_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + size_t result = 0; + if (algo_curve.count >= 0) return algo_curve.count; + + enif_mutex_lock(algo_curve.mtx_init_curve_types); + if (algo_curve.count < 0) { + init_curves(env, fips_enabled); /* also updates algo_curve.count[0] or [1] */ + result = algo_curve.count; } + enif_mutex_unlock(algo_curve.mtx_init_curve_types); + + return result; +} + +static void add_curve(ErlNifEnv* env, const char* str_v3, + const unsigned unavail_flags) +{ + struct curve_availability_t* curve = &algo_curve.algorithms[algo_curve.count]; + curve->str_v3 = str_v3; + curve->atom = enif_make_atom(env, str_v3); + curve->flags = unavail_flags; + algo_curve.count++; +} - if (1 == fips && algo_curve_fips_cnt >= 0) { - return algo_curve_fips_cnt; +static void add_curve_if_supported(const int nid, ErlNifEnv* env, bool fips_enabled, const char* str_v3) { + /* Some curves can be pre-checked by their NID */ + if (nid && !is_curve_valid_by_nid(nid)) { /* passing NID=0 will skip this check */ + return; } - enif_mutex_lock(mtx_init_curve_types); - if (1 == fips) { - if (algo_curve_fips_cnt < 0) { - algo_curve_fips_cnt = init_curves(env, 1); - } - cnt = algo_curve_fips_cnt; - } else { - if (algo_curve_cnt < 0) { - algo_curve_cnt = init_curves(env, 0); +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + { + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + unsigned unavail_flags = 0; + + /* This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added */ + if (fips_enabled) { + OSSL_PARAM params[2]; + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "fips=yes"); + if (!pctx) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; /* EC keygen context not available */ + } + if (EVP_PKEY_keygen_init(pctx) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; + } + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char *)str_v3, 0); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, params) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; + } + if (EVP_PKEY_generate(pctx, &pkey) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + } } - cnt = algo_curve_cnt; + add_anyway: + add_curve(env, str_v3, unavail_flags); + EVP_PKEY_free(pkey); /* NULL is allowed */ + EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ } - enif_mutex_unlock(mtx_init_curve_types); - - return cnt; +#else + add_curve(env, fips_enabled, str_v3, 0); +#endif } -int init_curves(ErlNifEnv* env, int fips) { +void init_curves(ErlNifEnv* env, const bool fips) { #if defined(HAVE_EC) - int cnt = 0; + algo_curve.count = 0; #ifdef NID_secp160k1 - if (valid_curve(NID_secp160k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160k1"); + add_curve_if_supported(NID_secp160k1, env, fips, "secp160k1"); #else #endif #ifdef NID_secp160r1 - if (valid_curve(NID_secp160r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160r1"); + add_curve_if_supported(NID_secp160r1, env, fips, "secp160r1"); #else #endif #ifdef NID_secp160r2 - if (valid_curve(NID_secp160r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160r2"); + add_curve_if_supported(NID_secp160r2, env, fips, "secp160r2"); #else #endif #ifdef NID_secp192k1 - if (valid_curve(NID_secp192k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp192k1"); + add_curve_if_supported(NID_secp192k1, env, fips, "secp192k1"); #else #endif #ifdef NID_secp224k1 - if (valid_curve(NID_secp224k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp224k1"); + add_curve_if_supported(NID_secp224k1, env, fips, "secp224k1"); #else #endif #ifdef NID_secp224r1 - if (valid_curve(NID_secp224r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp224r1"); + add_curve_if_supported(NID_secp224r1, env, fips, "secp224r1"); #else #endif #ifdef NID_secp256k1 - if (valid_curve(NID_secp256k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp256k1"); + add_curve_if_supported(NID_secp256k1, env, fips, "secp256k1"); #else #endif #ifdef NID_secp384r1 - if (valid_curve(NID_secp384r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp384r1"); + add_curve_if_supported(NID_secp384r1, env, fips, "secp384r1"); #else #endif #ifdef NID_secp521r1 - if (valid_curve(NID_secp521r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp521r1"); + add_curve_if_supported(NID_secp521r1, env, fips, "secp521r1"); #else #endif #ifdef NID_X9_62_prime192v1 - if (valid_curve(NID_X9_62_prime192v1)) { - algo_curve[fips][cnt++] = enif_make_atom(env,"secp192r1"); - algo_curve[fips][cnt++] = enif_make_atom(env,"prime192v1"); - } + add_curve_if_supported(NID_X9_62_prime192v1, env, fips, "secp192r1"); + add_curve_if_supported(NID_X9_62_prime192v1, env, fips, "prime192v1"); #else #endif #ifdef NID_X9_62_prime192v2 - if (valid_curve(NID_X9_62_prime192v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime192v2"); + add_curve_if_supported(NID_X9_62_prime192v2, env, fips, "prime192v2"); #else #endif #ifdef NID_X9_62_prime192v3 - if (valid_curve(NID_X9_62_prime192v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime192v3"); + add_curve_if_supported(NID_X9_62_prime192v3, env, fips, "prime192v3"); #else #endif #ifdef NID_X9_62_prime239v1 - if (valid_curve(NID_X9_62_prime239v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime239v1"); + add_curve_if_supported(NID_X9_62_prime239v1, env, fips, "prime239v1"); #else #endif #ifdef NID_X9_62_prime239v2 - if (valid_curve(NID_X9_62_prime239v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime239v2"); + add_curve_if_supported(NID_X9_62_prime239v2, env, fips, "prime239v2"); #else #endif #ifdef NID_X9_62_prime239v3 - if (valid_curve(NID_X9_62_prime239v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime239v3"); + add_curve_if_supported(NID_X9_62_prime239v3, env, fips, "prime239v3"); #else #endif #ifdef NID_X9_62_prime256v1 - if (valid_curve(NID_X9_62_prime256v1)) { - algo_curve[fips][cnt++] = enif_make_atom(env,"secp256r1"); - algo_curve[fips][cnt++] = enif_make_atom(env,"prime256v1"); - } + add_curve_if_supported(NID_X9_62_prime256v1, env, fips, "secp256r1"); + add_curve_if_supported(NID_X9_62_prime256v1, env, fips, "prime256v1"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls7 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls7)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls7"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls7, env, fips, "wtls7"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls9 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls9)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls9"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls9, env, fips, "wtls9"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls12 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls12)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls12"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls12, env, fips, "wtls12"); #else #endif #ifdef NID_brainpoolP160r1 - if (valid_curve(NID_brainpoolP160r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP160r1"); + add_curve_if_supported(NID_brainpoolP160r1, env, fips, "brainpoolP160r1"); #else #endif #ifdef NID_brainpoolP160t1 - if (valid_curve(NID_brainpoolP160t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP160t1"); + add_curve_if_supported(NID_brainpoolP160t1, env, fips, "brainpoolP160t1"); #else #endif #ifdef NID_brainpoolP192r1 - if (valid_curve(NID_brainpoolP192r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP192r1"); + add_curve_if_supported(NID_brainpoolP192r1, env, fips, "brainpoolP192r1"); #else #endif #ifdef NID_brainpoolP192t1 - if (valid_curve(NID_brainpoolP192t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP192t1"); + add_curve_if_supported(NID_brainpoolP192t1, env, fips, "brainpoolP192t1"); #else #endif #ifdef NID_brainpoolP224r1 - if (valid_curve(NID_brainpoolP224r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP224r1"); + add_curve_if_supported(NID_brainpoolP224r1, env, fips, "brainpoolP224r1"); #else #endif #ifdef NID_brainpoolP224t1 - if (valid_curve(NID_brainpoolP224t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP224t1"); + add_curve_if_supported(NID_brainpoolP224t1, env, fips, "brainpoolP224t1"); #else #endif #ifdef NID_brainpoolP256r1 - if (valid_curve(NID_brainpoolP256r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP256r1"); + add_curve_if_supported(NID_brainpoolP256r1, env, fips, "brainpoolP256r1"); #else #endif #ifdef NID_brainpoolP256t1 - if (valid_curve(NID_brainpoolP256t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP256t1"); + add_curve_if_supported(NID_brainpoolP256t1, env, fips, "brainpoolP256t1"); #else #endif #ifdef NID_brainpoolP320r1 - if (valid_curve(NID_brainpoolP320r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP320r1"); + add_curve_if_supported(NID_brainpoolP320r1, env, fips, "brainpoolP320r1"); #else #endif #ifdef NID_brainpoolP320t1 - if (valid_curve(NID_brainpoolP320t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP320t1"); + add_curve_if_supported(NID_brainpoolP320t1, env, fips, "brainpoolP320t1"); #else #endif #ifdef NID_brainpoolP384r1 - if (valid_curve(NID_brainpoolP384r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP384r1"); + add_curve_if_supported(NID_brainpoolP384r1, env, fips, "brainpoolP384r1"); #else #endif #ifdef NID_brainpoolP384t1 - if (valid_curve(NID_brainpoolP384t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP384t1"); + add_curve_if_supported(NID_brainpoolP384t1, env, fips, "brainpoolP384t1"); #else #endif #ifdef NID_brainpoolP512r1 - if (valid_curve(NID_brainpoolP512r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP512r1"); + add_curve_if_supported(NID_brainpoolP512r1, env, fips, "brainpoolP512r1"); #else #endif #ifdef NID_brainpoolP512t1 - if (valid_curve(NID_brainpoolP512t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP512t1"); + add_curve_if_supported(NID_brainpoolP512t1, env, fips, "brainpoolP512t1"); #else #endif - //#if !defined(OPENSSL_NO_EC2M) + //#if !defined(OPENSSL_NO_EC2M) #ifdef NID_sect163k1 - if (valid_curve(NID_sect163k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163k1"); + add_curve_if_supported(NID_sect163k1, env, fips, "sect163k1"); #else #endif #ifdef NID_sect163r1 - if (valid_curve(NID_sect163r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163r1"); + add_curve_if_supported(NID_sect163r1, env, fips, "sect163r1"); #else #endif #ifdef NID_sect163r2 - if (valid_curve(NID_sect163r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163r2"); + add_curve_if_supported(NID_sect163r2, env, fips, "sect163r2"); #else #endif #ifdef NID_sect193r1 - if (valid_curve(NID_sect193r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect193r1"); + add_curve_if_supported(NID_sect193r1, env, fips, "sect193r1"); #else #endif #ifdef NID_sect193r2 - if (valid_curve(NID_sect193r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect193r2"); + add_curve_if_supported(NID_sect193r2, env, fips, "sect193r2"); #else #endif #ifdef NID_sect233k1 - if (valid_curve(NID_sect233k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect233k1"); + add_curve_if_supported(NID_sect233k1, env, fips, "sect233k1"); #else #endif #ifdef NID_sect233r1 - if (valid_curve(NID_sect233r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect233r1"); + add_curve_if_supported(NID_sect233r1, env, fips, "sect233r1"); #else #endif #ifdef NID_sect239k1 - if (valid_curve(NID_sect239k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect239k1"); + add_curve_if_supported(NID_sect239k1, env, fips, "sect239k1"); #else #endif #ifdef NID_sect283k1 - if (valid_curve(NID_sect283k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect283k1"); + add_curve_if_supported(NID_sect283k1, env, fips, "sect283k1"); #else #endif #ifdef NID_sect283r1 - if (valid_curve(NID_sect283r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect283r1"); + add_curve_if_supported(NID_sect283r1, env, fips, "sect283r1"); #else #endif #ifdef NID_sect409k1 - if (valid_curve(NID_sect409k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect409k1"); + add_curve_if_supported(NID_sect409k1, env, fips, "sect409k1"); #else #endif #ifdef NID_sect409r1 - if (valid_curve(NID_sect409r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect409r1"); + add_curve_if_supported(NID_sect409r1, env, fips, "sect409r1"); #else #endif #ifdef NID_sect571k1 - if (valid_curve(NID_sect571k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect571k1"); + add_curve_if_supported(NID_sect571k1, env, fips, "sect571k1"); #else #endif #ifdef NID_sect571r1 - if (valid_curve(NID_sect571r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect571r1"); + add_curve_if_supported(NID_sect571r1, env, fips, "sect571r1"); #else #endif #ifdef NID_X9_62_c2pnb163v1 - if (valid_curve(NID_X9_62_c2pnb163v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v1"); + add_curve_if_supported(NID_X9_62_c2pnb163v1, env, fips, "c2pnb163v1"); #else #endif #ifdef NID_X9_62_c2pnb163v2 - if (valid_curve(NID_X9_62_c2pnb163v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v2"); + add_curve_if_supported(NID_X9_62_c2pnb163v2, env, fips, "c2pnb163v2"); #else #endif #ifdef NID_X9_62_c2pnb163v3 - if (valid_curve(NID_X9_62_c2pnb163v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v3"); + add_curve_if_supported(NID_X9_62_c2pnb163v3, env, fips, "c2pnb163v3"); #else #endif #ifdef NID_X9_62_c2pnb176v1 - if (valid_curve(NID_X9_62_c2pnb176v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb176v1"); + add_curve_if_supported(NID_X9_62_c2pnb176v1, env, fips, "c2pnb176v1"); #else #endif #ifdef NID_X9_62_c2tnb191v1 - if (valid_curve(NID_X9_62_c2tnb191v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v1"); + add_curve_if_supported(NID_X9_62_c2tnb191v1, env, fips, "c2tnb191v1"); #else #endif #ifdef NID_X9_62_c2tnb191v2 - if (valid_curve(NID_X9_62_c2tnb191v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v2"); + add_curve_if_supported(NID_X9_62_c2tnb191v2, env, fips, "c2tnb191v2"); #else #endif #ifdef NID_X9_62_c2tnb191v3 - if (valid_curve(NID_X9_62_c2tnb191v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v3"); + add_curve_if_supported(NID_X9_62_c2tnb191v3, env, fips, "c2tnb191v3"); #else #endif #ifdef NID_X9_62_c2pnb208w1 - if (valid_curve(NID_X9_62_c2pnb208w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb208w1"); + add_curve_if_supported(NID_X9_62_c2pnb208w1, env, fips, "c2pnb208w1"); #else #endif #ifdef NID_X9_62_c2tnb239v1 - if (valid_curve(NID_X9_62_c2tnb239v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v1"); + add_curve_if_supported(NID_X9_62_c2tnb239v1, env, fips, "c2tnb239v1"); #else #endif #ifdef NID_X9_62_c2tnb239v2 - if (valid_curve(NID_X9_62_c2tnb239v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v2"); + add_curve_if_supported(NID_X9_62_c2tnb239v2, env, fips, "c2tnb239v2"); #else #endif #ifdef NID_X9_62_c2tnb239v3 - if (valid_curve(NID_X9_62_c2tnb239v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v3"); + add_curve_if_supported(NID_X9_62_c2tnb239v3, env, fips, "c2tnb239v3"); #else #endif #ifdef NID_X9_62_c2pnb272w1 - if (valid_curve(NID_X9_62_c2pnb272w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb272w1"); + add_curve_if_supported(NID_X9_62_c2pnb272w1, env, fips, "c2pnb272w1"); #else #endif #ifdef NID_X9_62_c2pnb304w1 - if (valid_curve(NID_X9_62_c2pnb304w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb304w1"); + add_curve_if_supported(NID_X9_62_c2pnb304w1, env, fips, "c2pnb304w1"); #else #endif #ifdef NID_X9_62_c2tnb359v1 - if (valid_curve(NID_X9_62_c2tnb359v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb359v1"); + add_curve_if_supported(NID_X9_62_c2tnb359v1, env, fips, "c2tnb359v1"); #else #endif #ifdef NID_X9_62_c2pnb368w1 - if (valid_curve(NID_X9_62_c2pnb368w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb368w1"); + add_curve_if_supported(NID_X9_62_c2pnb368w1, env, fips, "c2pnb368w1"); #else #endif #ifdef NID_X9_62_c2tnb431r1 - if (valid_curve(NID_X9_62_c2tnb431r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb431r1"); + add_curve_if_supported(NID_X9_62_c2tnb431r1, env, fips, "c2tnb431r1"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls3 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls3)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls3"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls3, env, fips, "wtls3"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls5 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls5)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls5"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls5, env, fips, "wtls5"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls10 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls10)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls10"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls10, env, fips, "wtls10"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls11 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls11)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls11"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls11, env, fips, "wtls11"); #else #endif // Non-validated algorithms follow #ifdef NID_secp112r1 - if (valid_curve(NID_secp112r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp112r1"); + add_curve_if_supported(NID_secp112r1, env, fips, "secp112r1"); #else #endif #ifdef NID_secp112r2 - if (valid_curve(NID_secp112r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp112r2"); + add_curve_if_supported(NID_secp112r2, env, fips, "secp112r2"); #else #endif #ifdef NID_secp128r1 - if (valid_curve(NID_secp128r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp128r1"); + add_curve_if_supported(NID_secp128r1, env, fips, "secp128r1"); #else #endif #ifdef NID_secp128r2 - if (valid_curve(NID_secp128r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp128r2"); + add_curve_if_supported(NID_secp128r2, env, fips, "secp128r2"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls6 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls6)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls6"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls6, env, fips, "wtls6"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls8 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls8)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls8"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls8, env, fips, "wtls8"); #else #endif //#if !defined(OPENSSL_NO_EC2M) #ifdef NID_sect113r1 - if (valid_curve(NID_sect113r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect113r1"); + add_curve_if_supported(NID_sect113r1, env, fips, "sect113r1"); #else #endif #ifdef NID_sect113r2 - if (valid_curve(NID_sect113r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect113r2"); + add_curve_if_supported(NID_sect113r2, env, fips, "sect113r2"); #else #endif #ifdef NID_sect131r1 - if (valid_curve(NID_sect131r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect131r1"); + add_curve_if_supported(NID_sect131r1, env, fips, "sect131r1"); #else #endif #ifdef NID_sect131r2 - if (valid_curve(NID_sect131r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect131r2"); + add_curve_if_supported(NID_sect131r2, env, fips, "sect131r2"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls1 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls1)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls1"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls1, env, fips, "wtls1"); #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls4 - if (valid_curve(NID_wap_wsg_idm_ecid_wtls4)) algo_curve[fips][cnt++] = enif_make_atom(env,"wtls4"); + add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls4, env, fips, "wtls4"); #else #endif #ifdef NID_ipsec3 - if (valid_curve(NID_ipsec3)) algo_curve[fips][cnt++] = enif_make_atom(env,"ipsec3"); + add_curve_if_supported(NID_ipsec3, env, fips, "ipsec3"); #else #endif #ifdef NID_ipsec4 - if (valid_curve(NID_ipsec4)) algo_curve[fips][cnt++] = enif_make_atom(env,"ipsec4"); + add_curve_if_supported(NID_ipsec4, env, fips, "ipsec4"); #else #endif if (!fips) { #ifdef HAVE_ED25519 - algo_curve[fips][cnt++] = enif_make_atom(env,"ed25519"); + add_curve_if_supported(0, env, fips, "ed25519"); #endif #ifdef HAVE_ED448 - algo_curve[fips][cnt++] = enif_make_atom(env,"ed448"); + add_curve_if_supported(0, env, fips, "ed448"); #endif #ifdef HAVE_X25519 - algo_curve[fips][cnt++] = enif_make_atom(env,"x25519"); + add_curve_if_supported(0, env, fips, "x25519"); #endif #ifdef HAVE_X448 - algo_curve[fips][cnt++] = enif_make_atom(env,"x448"); + add_curve_if_supported(0, env, fips, "x448"); #endif } - ASSERT(cnt <= sizeof(algo_curve[0])/sizeof(ERL_NIF_TERM)); - - return cnt; -#else /* if not HAVE_EC */ - return 0; + /* Check buffer overrun just in case */ + ASSERT(algo_curve.count <= sizeof(algo_curve.algorithms)/sizeof(algo_curve.algorithms[0])); #endif } @@ -650,8 +906,8 @@ int init_curves(ErlNifEnv* env, int fips) { current cryptolib and current FIPS state. */ -int valid_curve(int nid) { - int ret = 0; +bool is_curve_valid_by_nid(const int nid) { + bool ret = false; #if defined(HAVE_DH) # if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH) @@ -677,7 +933,7 @@ int valid_curve(int nid) { goto out; if (1 != EVP_PKEY_keygen(kctx, &pkey)) goto out; - ret = 1; + ret = true; out: if (pkey) EVP_PKEY_free(pkey); if (kctx) EVP_PKEY_CTX_free(kctx); @@ -693,7 +949,7 @@ int valid_curve(int nid) { if(1 != EC_KEY_generate_key(key)) goto out; - ret = 1; + ret = true; out: if (key) EC_KEY_free(key); # endif @@ -709,9 +965,7 @@ int valid_curve(int nid) { ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - unsigned int cnt = - FIPS_MODE() ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; - + const size_t cnt = FIPS_MODE() ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; return enif_make_list_from_array(env, algo_rsa_opts, cnt); } @@ -743,6 +997,52 @@ void init_rsa_opts_types(ErlNifEnv* env) { algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; - ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); + ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(algo_rsa_opts[0])); +} + +ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#ifdef FIPS_SUPPORT + return digest_types_as_list(env, true); +#else + return enif_make_list(env, 0); /* nothing is forbidden */ +#endif +} + +ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#ifdef FIPS_SUPPORT + /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == true */ + return pubkey_algorithms_as_list(env, true); +#else + return enif_make_list(env, 0); /* nothing is forbidden */ +#endif +} + +ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#ifdef FIPS_SUPPORT + /* Filter the results by IS_CIPHER_FORBIDDEN_IN_FIPS() == true */ + return cipher_types_as_list(env, true); +#else + return enif_make_list(env, 0); /* nothing is forbidden */ +#endif +} + +ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#ifdef FIPS_SUPPORT + return kem_algorithms_as_list(env, true); +#else + return enif_make_list(env, 0); /* nothing is forbidden */ +#endif +} + +ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#ifdef FIPS_SUPPORT + return mac_types_as_list(env, true); +#else + return enif_make_list(env, 0); /* not forbidden */ +#endif } +ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + curves_lazy_init(env, FIPS_MODE()); + return curve_algorithms_as_list(env, true); +} diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h index 7a2c75106b5d..1293fbc9579d 100644 --- a/lib/crypto/c_src/algorithms.h +++ b/lib/crypto/c_src/algorithms.h @@ -30,11 +30,26 @@ void destroy_curve_mutex(void); void init_algorithms_types(ErlNifEnv* env); ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#ifdef FIPS_SUPPORT +ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif + ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); #endif /* E_ALGORITHMS_H__ */ diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index 1f86451563ba..250a0b8d84b5 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -254,7 +254,7 @@ static int get_init_args(ErlNifEnv* env, } - if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(*cipherp)) { *return_term = EXCP_NOTSUP_N(env, cipher_arg_num, "Forbidden in FIPS"); goto err; diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index b57563b3a712..6cd813b6fb9d 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -30,21 +30,21 @@ static struct cipher_type_t cipher_types[] = { #ifdef HAVE_RC2 - {{"rc2_cbc"}, "rc2-cbc", {&EVP_rc2_cbc}, 0, NO_FIPS_CIPHER, NOT_AEAD}, + {{"rc2_cbc"}, "rc2-cbc", {&EVP_rc2_cbc}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #else - {{"rc2_cbc"}, "rc2-cbc", {NULL}, 0, NO_FIPS_CIPHER, NOT_AEAD}, + {{"rc2_cbc"}, "rc2-cbc", {NULL}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #endif #ifdef HAVE_RC4 - {{"rc4"}, "rc4", {&EVP_rc4}, 0, NO_FIPS_CIPHER, NOT_AEAD}, + {{"rc4"}, "rc4", {&EVP_rc4}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #else - {{"rc4"}, "rc4", {NULL}, 0, NO_FIPS_CIPHER, NOT_AEAD}, + {{"rc4"}, "rc4", {NULL}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #endif #ifdef HAVE_DES - {{"des_cbc"}, "des-cbc", {&EVP_des_cbc}, 0, NO_FIPS_CIPHER}, - {{"des_cfb"}, "des-cfb", {&EVP_des_cfb8}, 0, NO_FIPS_CIPHER}, - {{"des_ecb"}, "des-ecb", {&EVP_des_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L}, + {{"des_cbc"}, "des-cbc", {&EVP_des_cbc}, 0, FIPS_FORBIDDEN_CIPHER}, + {{"des_cfb"}, "des-cfb", {&EVP_des_cfb8}, 0, FIPS_FORBIDDEN_CIPHER}, + {{"des_ecb"}, "des-ecb", {&EVP_des_ecb}, 0, FIPS_FORBIDDEN_CIPHER | ECB_BUG_0_9_8L}, #else {{"des_cbc"}, "des-cbc", {NULL}, 0, 0}, {{"des_cfb"}, "des-cfb", {NULL}, 0, 0}, @@ -64,10 +64,10 @@ static struct cipher_type_t cipher_types[] = #endif #ifdef HAVE_BF - {{"blowfish_cbc"}, "BF-CBC", {&EVP_bf_cbc}, 0, NO_FIPS_CIPHER, NOT_AEAD}, - {{"blowfish_cfb64"}, "BF-CFB", {&EVP_bf_cfb64}, 0, NO_FIPS_CIPHER, NOT_AEAD}, - {{"blowfish_ofb64"}, "BF-OFB", {&EVP_bf_ofb}, 0, NO_FIPS_CIPHER, NOT_AEAD}, - {{"blowfish_ecb"}, "BF-ECB", {&EVP_bf_ecb}, 0, NO_FIPS_CIPHER | ECB_BUG_0_9_8L, NOT_AEAD}, + {{"blowfish_cbc"}, "BF-CBC", {&EVP_bf_cbc}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"blowfish_cfb64"}, "BF-CFB", {&EVP_bf_cfb64}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"blowfish_ofb64"}, "BF-OFB", {&EVP_bf_ofb}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"blowfish_ecb"}, "BF-ECB", {&EVP_bf_ecb}, 0, FIPS_FORBIDDEN_CIPHER | ECB_BUG_0_9_8L, NOT_AEAD}, #else {{"blowfish_cbc"}, "BF-CBC", {NULL}, 0, 0, NOT_AEAD}, {{"blowfish_cfb64"}, "BF-CFB", {NULL}, 0, 0, NOT_AEAD}, @@ -76,17 +76,17 @@ static struct cipher_type_t cipher_types[] = #endif #ifdef HAVE_SM4 - {{"sm4_cbc"}, "sm4-cbc", {&EVP_sm4_cbc}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ecb"}, "sm4-ecb", {&EVP_sm4_ecb}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_cfb"}, "sm4-cfb", {&EVP_sm4_cfb}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ofb"}, "sm4-ofb", {&EVP_sm4_ofb}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ctr"}, "sm4-ctr", {&EVP_sm4_ctr}, 16, NO_FIPS_CIPHER, NOT_AEAD}, + {{"sm4_cbc"}, "sm4-cbc", {&EVP_sm4_cbc}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ecb"}, "sm4-ecb", {&EVP_sm4_ecb}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_cfb"}, "sm4-cfb", {&EVP_sm4_cfb}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ofb"}, "sm4-ofb", {&EVP_sm4_ofb}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ctr"}, "sm4-ctr", {&EVP_sm4_ctr}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #else - {{"sm4_cbc"}, "sm4-cbc", {NULL}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ecb"}, "sm4-ecb", {NULL}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_cfb"}, "sm4-cfb", {NULL}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ofb"}, "sm4-ofb", {NULL}, 16, NO_FIPS_CIPHER, NOT_AEAD}, - {{"sm4_ctr"}, "sm4-ctr", {NULL}, 16, NO_FIPS_CIPHER, NOT_AEAD}, + {{"sm4_cbc"}, "sm4-cbc", {NULL}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ecb"}, "sm4-ecb", {NULL}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_cfb"}, "sm4-cfb", {NULL}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ofb"}, "sm4-ofb", {NULL}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, + {{"sm4_ctr"}, "sm4-ctr", {NULL}, 16, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #endif {{"aes_128_cbc"}, "aes-128-cbc", {&EVP_aes_128_cbc}, 16, 0, NOT_AEAD}, @@ -120,23 +120,23 @@ static struct cipher_type_t cipher_types[] = #endif #if defined(HAVE_CHACHA20) - {{"chacha20"}, "chacha20", {&EVP_chacha20}, 32, NO_FIPS_CIPHER, NOT_AEAD}, + {{"chacha20"}, "chacha20", {&EVP_chacha20}, 32, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #else - {{"chacha20"}, "chacha20", {NULL}, 0, NO_FIPS_CIPHER, NOT_AEAD}, + {{"chacha20"}, "chacha20", {NULL}, 0, FIPS_FORBIDDEN_CIPHER, NOT_AEAD}, #endif /*==== AEAD ciphers ====*/ #if defined(HAVE_CHACHA20_POLY1305) - {{"chacha20_poly1305"}, "chacha20-poly1305", {&EVP_chacha20_poly1305}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, AEAD_CTRL}, + {{"chacha20_poly1305"}, "chacha20-poly1305", {&EVP_chacha20_poly1305}, 0, FIPS_FORBIDDEN_CIPHER | AEAD_CIPHER, AEAD_CTRL}, #else - {{"chacha20_poly1305"}, "chacha20-poly1305", {NULL}, 0, NO_FIPS_CIPHER | AEAD_CIPHER, {{0,0,0}}}, + {{"chacha20_poly1305"}, "chacha20-poly1305", {NULL}, 0, FIPS_FORBIDDEN_CIPHER | AEAD_CIPHER, {{0,0,0}}}, #endif #if defined(HAVE_SM4_GCM) - {{"sm4_gcm"}, "sm4-gcm", {NULL}, 16, NO_FIPS_CIPHER | AEAD_CIPHER | GCM_MODE, AEAD_CTRL}, + {{"sm4_gcm"}, "sm4-gcm", {NULL}, 16, FIPS_FORBIDDEN_CIPHER | AEAD_CIPHER | GCM_MODE, AEAD_CTRL}, #endif #if defined(HAVE_SM4_CCM) - {{"sm4_ccm"}, "sm4-ccm", {NULL}, 16, NO_FIPS_CIPHER | AEAD_CIPHER | CCM_MODE, AEAD_CTRL}, + {{"sm4_ccm"}, "sm4-ccm", {NULL}, 16, FIPS_FORBIDDEN_CIPHER | AEAD_CIPHER | CCM_MODE, AEAD_CTRL}, #endif #if defined(HAVE_GCM) && defined(HAS_3_0_API) @@ -205,6 +205,29 @@ int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) { return 0; } +#ifdef HAS_3_0_API +#ifdef FIPS_SUPPORT +/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ +static int is_valid_in_fips(const EVP_CIPHER *cipher) +{ + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + int usable = 0; + + if (cipher) { + unsigned char key[64] = {0}; + unsigned char iv[32] = {0}; + /* Try to initialize the cipher in encryption mode */ + if (EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, 1) == 1) { + usable = 1; + } + } + + EVP_CIPHER_CTX_free(ctx); + return usable; +} +#endif /* FIPS_SUPPORT */ +#endif /* HAS_3_0_API */ + void init_cipher_types(ErlNifEnv* env) { struct cipher_type_t* p = cipher_types; @@ -212,21 +235,21 @@ void init_cipher_types(ErlNifEnv* env) num_cipher_types = 0; for (p = cipher_types; p->type.str; p++) { num_cipher_types++; - p->type.atom = enif_make_atom(env, p->type.str); + p->type.atom = enif_make_atom(env, p->type.str); #ifdef HAS_3_0_API if (p->str_v3) { - p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); # ifdef FIPS_SUPPORT - /* Try if valid in FIPS */ - { - EVP_CIPHER *tmp = EVP_CIPHER_fetch(NULL, p->str_v3, "fips=yes"); - - if (tmp) { - EVP_CIPHER_free(tmp); - p->flags &= ~NO_FIPS_CIPHER; - } else - p->flags |= NO_FIPS_CIPHER; + EVP_CIPHER* fetched_cipher = EVP_CIPHER_fetch(NULL, p->str_v3, "fips=yes"); + /* Deeper check for validity in FIPS, also checks for NULL */ + if (is_valid_in_fips(fetched_cipher)) { + p->flags &= ~FIPS_FORBIDDEN_CIPHER; + p->cipher.p = fetched_cipher; + } else { + p->flags |= FIPS_FORBIDDEN_CIPHER; + EVP_CIPHER_free(fetched_cipher); /* NULL is allowed */ } +#else + p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); # endif /* FIPS_SUPPORT and >=3.0.0 */ } #else @@ -277,7 +300,7 @@ ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if ((cipherp = get_cipher_type_no_key(argv[0])) == NULL) return enif_make_badarg(env); - if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(cipherp)) return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); @@ -391,26 +414,19 @@ int cmp_cipher_types_no_key(const void *keyp, const void *elemp) { return ret; } - -ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) +/* Argument fips_forbidden partitions the results by invoking IS_CIPHER_FORBIDDEN_IN_FIPS(p) */ +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env, const bool fips_forbidden) { - struct cipher_type_t* p; - ERL_NIF_TERM prev, hd; + ERL_NIF_TERM hd = enif_make_list(env, 0); + ERL_NIF_TERM prev = atom_undefined; - hd = enif_make_list(env, 0); - prev = atom_undefined; - - for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { - if ((prev == p->type.atom) || - CIPHER_FORBIDDEN_IN_FIPS(p) ) + for (struct cipher_type_t *p = cipher_types; p->type.atom && (p->type.atom != atom_false); p++) { + if (prev == p->type.atom || IS_CIPHER_FORBIDDEN_IN_FIPS(p) != fips_forbidden) { continue; - - if ((p->cipher.p != NULL) || - (p->flags & AES_CTR_COMPAT)) - { - hd = enif_make_list_cell(env, p->type.atom, hd); - } + } + if (p->cipher.p != NULL || p->flags & AES_CTR_COMPAT) { + hd = enif_make_list_cell(env, p->type.atom, hd); + } } - return hd; } diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h index 36a6d015f286..787c1aea75f0 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -27,35 +27,37 @@ struct cipher_type_t { union { - const char* str; /* before init */ - ERL_NIF_TERM atom; /* after init */ - }type; - const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ + const char* str; /* before init */ + ERL_NIF_TERM atom; /* after init */ + } type; + const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ union { - const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_CIPHER* p; /* after init, NULL if notsup */ - }cipher; - size_t key_len; /* != 0 to also match on key_len */ + const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_CIPHER* p; /* after init, NULL if notsup */ + } cipher; + size_t key_len; /* != 0 to also match on key_len */ unsigned flags; union { - struct aead_ctrl {int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag;} aead; + struct aead_ctrl { int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag; } aead; } extra; }; /* masks in the flags field if cipher_type_t */ -#define NO_FIPS_CIPHER 1 -#define AES_CFBx 2 -#define ECB_BUG_0_9_8L 4 -#define AEAD_CIPHER 8 -#define NON_EVP_CIPHER 16 -#define AES_CTR_COMPAT 32 -#define CCM_MODE 64 -#define GCM_MODE 128 +enum CIPHER_TYPE_FLAGS { + FIPS_FORBIDDEN_CIPHER = 1, + AES_CFBx = 2, + ECB_BUG_0_9_8L = 4, + AEAD_CIPHER = 8, + NON_EVP_CIPHER = 16, + AES_CTR_COMPAT = 32, + CCM_MODE = 64, + GCM_MODE = 128, +}; #ifdef FIPS_SUPPORT -# define CIPHER_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_MODE()) +# define IS_CIPHER_FORBIDDEN_IN_FIPS(p) (((p)->flags & FIPS_FORBIDDEN_CIPHER) && FIPS_MODE()) #else -# define CIPHER_FORBIDDEN_IN_FIPS(P) 0 +# define IS_CIPHER_FORBIDDEN_IN_FIPS(P) false #endif extern ErlNifResourceType* evp_cipher_ctx_rtype; @@ -84,6 +86,6 @@ const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); int cmp_cipher_types(const void *keyp, const void *elemp); int cmp_cipher_types_no_key(const void *keyp, const void *elemp); -ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env); +ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env, bool fips_forbidden); #endif /* E_CIPHER_H__ */ diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h index 2c5c306c9d67..a999345d459a 100644 --- a/lib/crypto/c_src/common.h +++ b/lib/crypto/c_src/common.h @@ -38,7 +38,6 @@ #include "openssl_config.h" #include "atoms.h" - /* All nif functions return a valid value or throws an exception */ ERL_NIF_TERM raise_exception(ErlNifEnv* env, ERL_NIF_TERM id, int arg_num, char* explanation, char* file, int Line); diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index aad48c45eb9f..77ed4aa3f4d4 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -70,12 +70,27 @@ static ErlNifFunc nif_funcs[] = { {"info_lib", 0, info_lib, 0}, {"info_fips", 0, info_fips, 0}, {"enable_fips_mode_nif", 1, enable_fips_mode_nif, 0}, + {"hash_algorithms", 0, hash_algorithms, 0}, + {"fips_forbidden_hash_algorithms", 0, fips_forbidden_hash_algorithms, 0}, + {"pubkey_algorithms", 0, pubkey_algorithms, 0}, + {"fips_forbidden_pubkey_algorithms", 0, fips_forbidden_pubkey_algorithms, 0}, + {"cipher_algorithms", 0, cipher_algorithms, 0}, + {"fips_forbidden_cipher_algorithms", 0, fips_forbidden_cipher_algorithms, 0}, + + {"kem_algorithms_nif", 0, kem_algorithms_nif, 0}, + {"fips_forbidden_kem_algorithms", 0, fips_forbidden_kem_algorithms, 0}, + {"mac_algorithms", 0, mac_algorithms, 0}, + {"fips_forbidden_mac_algorithms", 0, fips_forbidden_mac_algorithms, 0}, + {"curve_algorithms", 0, curve_algorithms, 0}, + {"fips_forbidden_curve_algorithms", 0, fips_forbidden_curve_algorithms, 0}, + {"rsa_opts_algorithms", 0, rsa_opts_algorithms, 0}, + {"hash_info", 1, hash_info_nif, 0}, {"hash_nif", 2, hash_nif, 0}, {"hash_init_nif", 1, hash_init_nif, 0}, @@ -99,14 +114,13 @@ static ErlNifFunc nif_funcs[] = { {"do_exor", 2, do_exor, 0}, {"hash_equals_nif", 2, hash_equals_nif, 0}, - + {"pbkdf2_hmac_nif", 5, pbkdf2_hmac_nif, 0}, {"pkey_sign_nif", 5, pkey_sign_nif, 0}, {"pkey_verify_nif", 6, pkey_verify_nif, 0}, {"pkey_crypt_nif", 6, pkey_crypt_nif, 0}, {"encapsulate_key_nif", 2, encapsulate_key_nif, 0}, {"decapsulate_key_nif", 3, decapsulate_key_nif, 0}, - {"kem_algorithms_nif", 0, kem_algorithms_nif, 0}, {"rsa_generate_key_nif", 2, rsa_generate_key_nif, 0}, {"dh_generate_key_nif", 4, dh_generate_key_nif, 0}, {"dh_compute_key_nif", 3, dh_compute_key_nif, 0}, @@ -411,4 +425,3 @@ static void unload(ErlNifEnv* env, void* priv_data) */ } } - diff --git a/lib/crypto/c_src/dh.c b/lib/crypto/c_src/dh.c index fb7ec9d83b79..74a3e5061fac 100644 --- a/lib/crypto/c_src/dh.c +++ b/lib/crypto/c_src/dh.c @@ -91,8 +91,11 @@ ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar ret = EXCP_BADARG_N(env, 3, "Bad value of length element"); goto done; } - else if (len) - params[i++] = OSSL_PARAM_construct_uint64("priv_len", &len); + if (len) { + /* ErlNifUint64 is defined as unsigned long while uint64_t is defined as unsigned long long */ + uint64_t len_u64 = len; + params[i++] = OSSL_PARAM_construct_uint64("priv_len", &len_u64); + } /* End of parameter fetching */ params[i++] = OSSL_PARAM_construct_end(); diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c index 863490cf2b15..6d464ff6c921 100644 --- a/lib/crypto/c_src/digest.c +++ b/lib/crypto/c_src/digest.c @@ -24,7 +24,7 @@ static struct digest_type_t digest_types[] = { - {"md4", "MD4", 0, NO_FIPS_DIGEST, + {"md4", "MD4", 0, FIPS_FORBIDDEN_DIGEST, #ifdef HAVE_MD4 {&EVP_md4,NULL} #else @@ -32,7 +32,7 @@ static struct digest_type_t digest_types[] = #endif }, - {"md5", "MD5", 0, NO_FIPS_DIGEST, + {"md5", "MD5", 0, FIPS_FORBIDDEN_DIGEST, #ifdef HAVE_MD5 {&EVP_md5,NULL} #else @@ -40,7 +40,7 @@ static struct digest_type_t digest_types[] = #endif }, - {"ripemd160", "RIPEMD160", 0, NO_FIPS_DIGEST, + {"ripemd160", "RIPEMD160", 0, FIPS_FORBIDDEN_DIGEST, #ifdef HAVE_RIPEMD160 {&EVP_ripemd160,NULL} #else @@ -51,7 +51,7 @@ static struct digest_type_t digest_types[] = {"sha", "SHA1", 0, PBKDF2_ELIGIBLE_DIGEST, {&EVP_sha1,NULL} }, - + {"sha224", "SHA2-224", 0, PBKDF2_ELIGIBLE_DIGEST, #ifdef HAVE_SHA224 {&EVP_sha224,NULL} @@ -178,30 +178,50 @@ static struct digest_type_t digest_types[] = {NULL, NULL, 0, 0, {NULL,NULL}} }; +#ifdef HAS_3_0_API +#ifdef FIPS_SUPPORT +/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ +static int is_valid_in_fips(const EVP_MD* md) +{ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + int usable = 0; + + if (md) { + /* Try to initialize the digest algorithm for use, this will check the dependencies */ + if (EVP_DigestInit_ex(ctx, md, NULL) == 1) { + usable = 1; + } + } + + EVP_MD_CTX_free(ctx); + return usable; +} +#endif /* FIPS_SUPPORT */ +#endif /* HAS_3_0_API */ + void init_digest_types(ErlNifEnv* env) { struct digest_type_t* p = digest_types; - - for (p = digest_types; p->str; p++) { + for (/* p = digest_types */; p->str; p++) { #ifdef HAS_3_0_API if (p->str_v3) { - p->md.p = EVP_MD_fetch(NULL, p->str_v3, ""); # ifdef FIPS_SUPPORT - /* Try if valid in FIPS */ - { - EVP_MD *tmp = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); - - if (tmp) { - EVP_MD_free(tmp); - p->flags &= ~NO_FIPS_DIGEST; - } else - p->flags |= NO_FIPS_DIGEST; + EVP_MD* fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); + /* Deeper check for validity in FIPS, also checks for NULL */ + if (is_valid_in_fips(fetched_md)) { + p->flags &= ~FIPS_FORBIDDEN_DIGEST; + p->md.p = fetched_md; + } else { + p->flags |= FIPS_FORBIDDEN_DIGEST; + EVP_MD_free(fetched_md); /* NULL is allowed */ } +# else + p->md.p = EVP_MD_fetch(NULL, p->str_v3, ""); # endif /* FIPS_SUPPORT and >=3.0.0 */ } #else if (p->md.funcp) - p->md.p = p->md.funcp(); + p->md.p = p->md.funcp(); #endif p->atom = enif_make_atom(env, p->str); } @@ -213,31 +233,26 @@ struct digest_type_t* get_digest_type(ERL_NIF_TERM type) { struct digest_type_t* p = NULL; for (p = digest_types; p->atom != atom_false; p++) { - if (type == p->atom) { - return p; - } + if (type == p->atom) { + return p; + } } return NULL; } -#ifdef HAS_3_0_API -ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env) +/* If FIPS mode enabled: Filters away disallowed digest types */ +ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, const bool fips_forbidden) { - struct digest_type_t* p; - ERL_NIF_TERM hd; - - hd = enif_make_list(env, 0); - - for (p = digest_types; (p->atom & (p->atom != atom_false)); p++) { - if (DIGEST_FORBIDDEN_IN_FIPS(p)) - continue; + struct digest_type_t* p = digest_types; + ERL_NIF_TERM hd = enif_make_list(env, 0); - if (p->md.p != NULL) + for (/* p = digest_types */; p->atom & (p->atom != atom_false); p++) { + if (p->md.p && !IS_DIGEST_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { hd = enif_make_list_cell(env, p->atom, hd); + } } return hd; } -#endif diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h index c211a8ca96ea..9d469a0c88d9 100644 --- a/lib/crypto/c_src/digest.h +++ b/lib/crypto/c_src/digest.h @@ -26,33 +26,32 @@ #include "common.h" struct digest_type_t { - const char* str; /* before init, NULL for end-of-table */ + const char* str; /* before init, NULL for end-of-table */ const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - unsigned flags; + unsigned flags; /* combination of DIGEST_TYPE_FLAGS */ struct { - const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ const EVP_MD* p; /* after init, NULL if notsup */ - }md; + } md; unsigned int xof_default_length; /* 0 or default digest length for XOF digests */ }; -/* masks in the flags field if digest_type_t */ -#define NO_FIPS_DIGEST 1 -#define PBKDF2_ELIGIBLE_DIGEST 2 +/* masks in the `flags` field of digest_type_t */ +enum DIGEST_TYPE_FLAGS { + FIPS_FORBIDDEN_DIGEST = 1, /* no support in FIPS for digest */ + PBKDF2_ELIGIBLE_DIGEST = 2 +}; #ifdef FIPS_SUPPORT -# define DIGEST_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_DIGEST) && FIPS_MODE()) +# define IS_DIGEST_FORBIDDEN_IN_FIPS(p) (((p)->flags & FIPS_FORBIDDEN_DIGEST) && FIPS_MODE()) #else -# define DIGEST_FORBIDDEN_IN_FIPS(P) 0 +# define IS_DIGEST_FORBIDDEN_IN_FIPS(P) false #endif - void init_digest_types(ErlNifEnv* env); struct digest_type_t* get_digest_type(ERL_NIF_TERM type); -#ifdef HAS_3_0_API -ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env); -#endif +ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, bool fips_forbidden); #endif /* E_DIGEST_H__ */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index 88271ebc9a6e..882a2f8cff5b 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -44,17 +44,15 @@ ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) { if (fips_mode_to_set == atom_true) { if (FIPS_mode_set(1)) return atom_true; - else return atom_false; - - } else if (fips_mode_to_set == atom_false) { + return atom_false; + } + if (fips_mode_to_set == atom_false) { if (!FIPS_mode_set(0)) return atom_false; - else return atom_true; - - } else - return enif_make_badarg(env); + return atom_true; + } + return enif_make_badarg(env); } #else - { if (fips_mode_to_set == atom_true) return atom_false; else if (fips_mode_to_set == atom_false) return atom_true; diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index 5757c30d24e5..c7c3f5eda7ce 100644 --- a/lib/crypto/c_src/hash.c +++ b/lib/crypto/c_src/hash.c @@ -84,7 +84,7 @@ ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[0])) == NULL) return enif_make_badarg(env); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return RAISE_NOTSUP(env); if ((md = digp->md.p) == NULL) @@ -109,7 +109,7 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return EXCP_NOTSUP_N(env, 0, "Bad digest type in FIPS"); if ((md = digp->md.p) == NULL) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in this cryptolib"); @@ -174,7 +174,7 @@ ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in FIPS"); if (digp->md.p == NULL) return EXCP_NOTSUP_N(env, 0, "Unsupported digest type"); @@ -288,7 +288,7 @@ ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((digp = get_digest_type(argv[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in FIPS"); if (digp->md.p == NULL) return EXCP_NOTSUP_N(env, 0, "Unsupported digest type"); @@ -374,7 +374,7 @@ ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return EXCP_BADARG_N(env, 0, "Bad state"); if ((digp = get_digest_type(tuple[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad state"); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return EXCP_BADARG_N(env, 0, "Bad state"); if (digp->md.p == NULL) return EXCP_BADARG_N(env, 0, "Bad state"); @@ -473,7 +473,7 @@ ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return EXCP_BADARG_N(env, 0, "Bad state"); if ((digp = get_digest_type(tuple[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad state"); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) return EXCP_BADARG_N(env, 0, "Bad state"); if ((md = digp->md.p) == NULL) return EXCP_BADARG_N(env, 0, "Bad state"); diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 3db57da49133..f67088e4d665 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -34,15 +34,15 @@ struct mac_type_t { union { - const char* str; /* before init, NULL for end-of-table */ - ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - }name; - unsigned flags; + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + } name; + unsigned unavail_flags; /* MAC_UNAVAIL_FLAGS: reasons why not available */ union { const int pkey_type; - }alg; - int type; - size_t key_len; /* != 0 to also match on key_len */ + } alg; + int type; /* MAC_TYPE */ + size_t key_len; /* != 0 to also match on key_len */ #if defined(HAS_3_0_API) const char* fetch_name; EVP_MAC *evp_mac; @@ -50,16 +50,21 @@ struct mac_type_t { }; /* masks in the flags field if mac_type_t */ -#define NO_FIPS_MAC 1 +enum MAC_UNAVAIL_FLAGS { + FIPS_MAC_NOT_AVAIL = 1, + FIPS_FORBIDDEN_MAC = 2, +}; -#define NO_mac 0 -#define HMAC_mac 1 -#define CMAC_mac 2 -#define POLY1305_mac 3 +enum MAC_TYPE { + NO_mac = 0, + HMAC_mac = 1, + CMAC_mac = 2, + POLY1305_mac = 3, +}; static struct mac_type_t mac_types[] = { - {{"poly1305"}, NO_FIPS_MAC, + {{"poly1305"}, FIPS_FORBIDDEN_MAC, #ifdef HAVE_POLY1305 /* If we have POLY then we have EVP_PKEY */ {EVP_PKEY_POLY1305}, POLY1305_mac, 32 @@ -102,9 +107,9 @@ static struct mac_type_t mac_types[] = }; #ifdef FIPS_SUPPORT -# define MAC_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_MAC) && FIPS_MODE()) +# define IS_MAC_FORBIDDEN_IN_FIPS(p) (((p)->unavail_flags & FIPS_FORBIDDEN_MAC) && FIPS_MODE()) #else -# define MAC_FORBIDDEN_IN_FIPS(P) 0 +# define IS_MAC_FORBIDDEN_IN_FIPS(P) 0 #endif /*************************** @@ -123,35 +128,75 @@ ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); Support functions for type array *********************************/ +#ifdef HAS_3_0_API +#ifdef FIPS_SUPPORT +/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ +static int is_valid_in_fips(EVP_MAC* mac) +{ + int usable = 0; + if (mac) { + EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac); + + /* Dummy key and parameters. */ + unsigned char key[64] = {0}; + OSSL_PARAM params[2]; + params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA256", 0); + params[1] = OSSL_PARAM_construct_end(); + + /* Try to initialize the digest algorithm for use, this will check the dependencies */ + if (EVP_MAC_init(ctx, key, sizeof(key), params) == 1) { + usable |= FIPS_FORBIDDEN_MAC; + } + + EVP_MAC_CTX_free(ctx); + } else { + return FIPS_MAC_NOT_AVAIL; + } + return usable; +} +#endif /* FIPS_SUPPORT */ +#endif /* HAS_3_0_API */ + void init_mac_types(ErlNifEnv* env) { struct mac_type_t* p = mac_types; - for (p = mac_types; p->name.str; p++) { - p->name.atom = enif_make_atom(env, p->name.str); + for (/* p = mac_types */; p->name.str; p++) { + p->name.atom = enif_make_atom(env, p->name.str); #if defined(HAS_3_0_API) +# ifdef FIPS_SUPPORT + { + EVP_MAC* fetched_mac = EVP_MAC_fetch(NULL, p->fetch_name, "fips=yes"); + const int unavail_flags = is_valid_in_fips(fetched_mac); /* Also tests for NULL */ + if (unavail_flags == 0) { + p->unavail_flags = 0; /* mark available */ + p->evp_mac = fetched_mac; + } else { + p->unavail_flags |= unavail_flags; + EVP_MAC_free(fetched_mac); + } + } +# else p->evp_mac = EVP_MAC_fetch(NULL, p->fetch_name, NULL); +# endif #endif } p->name.atom = atom_false; /* end marker */ } -ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env) +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env, const bool fips_forbidden) { - struct mac_type_t* p; - ERL_NIF_TERM prev, hd; - - hd = enif_make_list(env, 0); - prev = atom_undefined; + ERL_NIF_TERM prev = atom_undefined; + ERL_NIF_TERM hd = enif_make_list(env, 0); - for (p = mac_types; p->name.atom != atom_false; p++) { - if (prev == p->name.atom) + for (struct mac_type_t *p = mac_types; p->name.atom != atom_false; p++) + { + if (prev == p->name.atom || IS_MAC_FORBIDDEN_IN_FIPS(p) != fips_forbidden) { continue; - - if (p->type != NO_mac) - { - hd = enif_make_list_cell(env, p->name.atom, hd); - } + } + if (p->type != NO_mac) { + hd = enif_make_list_cell(env, p->name.atom, hd); + } } return hd; @@ -161,10 +206,10 @@ struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len) { struct mac_type_t* p = NULL; for (p = mac_types; p->name.atom != atom_false; p++) { - if (type == p->name.atom) { - if ((p->key_len == 0) || (p->key_len == key_len)) + if (type == p->name.atom) { + if (p->key_len == 0 || p->key_len == key_len) return p; - } + } } return NULL; } @@ -173,9 +218,9 @@ struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type) { struct mac_type_t* p = NULL; for (p = mac_types; p->name.atom != atom_false; p++) { - if (type == p->name.atom) { - return p; - } + if (type == p->name.atom) { + return p; + } } return NULL; } @@ -245,7 +290,8 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (!(macp = get_mac_type(argv[0], key_bin.size))) + macp = get_mac_type(argv[0], key_bin.size); + if (!macp) { if (!get_mac_type_no_key(argv[0])) return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); @@ -254,7 +300,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (MAC_FORBIDDEN_IN_FIPS(macp)) + if (IS_MAC_FORBIDDEN_IN_FIPS(macp)) { return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); goto err; @@ -281,7 +327,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) { return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; @@ -335,7 +381,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(cipherp)) { return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS"); goto err; @@ -606,7 +652,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (MAC_FORBIDDEN_IN_FIPS(macp)) + if (IS_MAC_FORBIDDEN_IN_FIPS(macp)) { return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); goto err; @@ -633,7 +679,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) { return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; @@ -677,7 +723,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + if (IS_CIPHER_FORBIDDEN_IN_FIPS(cipherp)) { return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS"); goto err; diff --git a/lib/crypto/c_src/mac.h b/lib/crypto/c_src/mac.h index 60cb4ff1d925..91c6ff129332 100644 --- a/lib/crypto/c_src/mac.h +++ b/lib/crypto/c_src/mac.h @@ -29,7 +29,7 @@ int init_mac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf); void init_mac_types(ErlNifEnv* env); -ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env); +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env, bool fips_forbidden); ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index f10998bafff4..3831f8e26210 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -185,7 +185,7 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, if ((digp = get_digest_type(type)) == NULL) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Bad digest type")); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Digest type forbidden in FIPS")); if (digp->md.p == NULL) diff --git a/lib/crypto/doc/guides/fips.md b/lib/crypto/doc/guides/fips.md index fc7e10dd4d16..c37e1721ed73 100644 --- a/lib/crypto/doc/guides/fips.md +++ b/lib/crypto/doc/guides/fips.md @@ -19,7 +19,7 @@ limitations under the License. %CopyrightEnd% --> -# FIPS mode +# FIPS Mode [](){: #fips } This chapter describes FIPS mode support in the crypto application. @@ -36,41 +36,71 @@ only the validated algorithms provided by the Object Module are accessible, other algorithms usually available in OpenSSL (like md5) or implemented in the Erlang code (like SRP) are disabled. -## Enabling FIPS mode - -1. Build or install the FIPS Object Module and a FIPS enabled OpenSSL library. - -You should read and precisely follow the instructions of the -[Security Policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf) -and [User Guide](https://www.openssl.org/docs/fips/UserGuide-2.0.pdf). +## Enabling FIPS Mode > #### Warning {: .warning } > > It is very easy to build a working OpenSSL FIPS Object Module and library from -> the source. However it _does not_ qualify as FIPS 140-2 validated if the -> numerous restrictions in the Security Policy are not properly followed. +> the source, all OpenSSL versions support FIPS. However any version _does not_ +> qualify as FIPS 140-2 or FIPS 140-3 validated if the numerous restrictions in +> the Security Policy are not properly followed, the version also must be listed +> on the NIST website as validated. + +### Build FIPS-enabled OpenSSL + +For a proper FIPS-compliant build you should read and precisely follow the instructions +of the [Security Policy 140-2](https://csrc.nist.gov/pubs/fips/140-2/upd2/final) +superceded by [Security Policy 140-3](https://csrc.nist.gov/pubs/fips/140-3/final) +and [User Guide](https://docs.openssl.org/), +but to get quick results which allow you to continue development, read on... + +Build is performed in 2 steps (for Windows follow the steps in `NOTES-WINDOWS.md`): +1. Define, where you will put the compiled OpenSSL files, this will be your `SSLDIR`. + ```bash + export SSLDIR=... + ``` +2. Configure the library. Run once (requires Perl): + ```bash + export OPENSSL_CONF=${SSLDIR}/openssl.cnf + ./Configure shared enable-fips --prefix=${SSLDIR} --openssldir=${SSLDIR} + ``` +3. Build the library + ```bash + make -j && make install install_fips + ``` + +### Configuring OpenSSL for FIPS + +Now that the `make install` has finished and the `${SSLDIR}` contains your ready +to use copy of OpenSSL, it is time to configure it for FIPS mode. + +1. Edit the `openssl.cnf` + 1. Find `.include fipsmodule.cnf` and edit it to include the full path to the `.cnf` file. + 2. Find `fips = fips_sect` and uncomment +2. Inside `${SSLDIR}` copy or link two files found `lib/ossl-modules/` + (`fips.so` or `fips.dylib`, and `legacy.so` or `legacy.dylib`) to be found in `${SSLDIR}/lib`. +3. You can verify that OpenSSL was configured correctly by invoking it from `${SSLDIR}`: + ```bash + bin/openssl list -providers + ``` + You should expect to see both `default` and `fips` providers, both having + `status: active`. The `default` can be disabled in `openssl.cnf` in the + `[default]` section. + +### Building Erlang With FIPS 1. Configure and build Erlang/OTP with FIPS support: - -```text -$ cd $ERL_TOP -$ ./otp_build configure --enable-fips -... -checking for FIPS_mode_set... yes -... -$ make -``` - -If `FIPS_mode_set` returns `no` the OpenSSL library is not FIPS enabled and -crypto won't support FIPS mode either. - -1. Set the `fips_mode` configuration setting of the crypto application to `true` - _before loading the crypto module_. - -The best place is in the `sys.config` system configuration file of the release. - -1. Start and use the crypto application as usual. However take care to avoid the - non-FIPS validated algorithms, they will all throw exception `not_supported`. + ```bash + export ERL_TOP=`pwd` # where your Erlang source is located + ./otp_build setup -a --enable-fips --with-ssl=${SSLDIR} + ``` +2. Set the `fips_mode` configuration setting of the `crypto` application to `true` + _before starting the `crypto` application_. + The best place to do so is in the `sys.config` system configuration file of the release, + but for development you can create your own `fips.config` file and provide it to Erlang. +3. Start and use the crypto application as usual. However any attempt to use + non-FIPS validated algorithms will end with a `not_supported` exception. +4. Verify that FIPS was enabled by calling `crypto:info_fips()` and `crypto:supports()`. Entering and leaving FIPS mode on a node already running crypto is not supported. The reason is that OpenSSL is designed to prevent an application @@ -83,7 +113,7 @@ section protected from any concurrently running crypto operations. Furthermore in case of failure all crypto calls would have to be disabled from the Erlang or nif code. This would be too much effort put into this not too important feature. -## Incompatibilities with regular builds +## Incompatibilities With Regular Builds The Erlang API of the crypto application is identical regardless of building with or without FIPS support. However the nif code internally uses a different @@ -94,7 +124,7 @@ functions (`hash_(init|update|final)`, `hmac_(init|update|final)` and `stream_(init|encrypt|decrypt)`) is different and incompatible with regular builds when compiling crypto with FIPS support. -## Common caveats +## Common Caveats In FIPS mode non-validated algorithms are disabled. This may cause some unexpected problems in application relying on crypto. @@ -106,32 +136,30 @@ unexpected problems in application relying on crypto. > 140-2 validated cryptographic module if it uses it exclusively for every > cryptographic operation. -### Restrictions on key sizes +### Restrictions On Key Sizes Although public key algorithms are supported in FIPS mode they can only be used with secure key sizes. The Security Policy requires the following minimum values: - **RSA** - 1024 bit - - **DSS** - 1024 bit - - **EC algorithms** - 160 bit -### Restrictions on elliptic curves +### Restrictions On Elliptic Curves The Erlang API allows using arbitrary curve parameters, but in FIPS mode only those allowed by the Security Policy shall be used. -### Avoid md5 for hashing +### Avoid MD5 For Hashing -Md5 is a popular choice as a hash function, but it is not secure enough to be +MD5 is a popular choice as a hash function, but it is not secure enough to be validated. Try to use sha instead wherever possible. For exceptional, non-cryptographic use cases one may consider switching to `erlang:md5/1` as well. -### Certificates and encrypted keys +### Certificates And Encrypted Keys As md5 is not available in FIPS mode it is only possible to use certificates that were signed using sha hashing. When validating an entire certificate chain @@ -141,14 +169,14 @@ For similar dependency on the md5 and des algorithms most encrypted private keys in PEM format do not work either. However, the PBES2 encryption scheme allows the use of stronger FIPS verified algorithms which is a viable alternative. -### SNMP v3 limitations +### SNMP v3 Limitations It is only possible to use `usmHMACSHAAuthProtocol` and `usmAesCfb128Protocol` for authentication and privacy respectively in FIPS mode. The snmp application however won't restrict selecting disabled protocols in any way, and using them would result in run time crashes. -### TLS 1.2 is required +### TLS 1.2 Is Required All SSL and TLS versions prior to TLS 1.2 use a combination of md5 and sha1 hashes in the handshake for various purposes: diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index de1f20c0fb93..908074cc2eaa 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -106,7 +106,8 @@ The exception `error:notsup` signifies that the algorithm is known but is not supported by current underlying libcrypto or explicitly disabled when building that. -For a list of supported algorithms, see [supports(ciphers)](`supports/1`). +For a list of supported algorithms, see [supports(ciphers)](`supports/1`) +and [supports()](`supports/0`). [](){: #error_3tup } @@ -151,7 +152,7 @@ end """. --export([start/0, stop/0, info/0, info_lib/0, info_fips/0, supports/0, enable_fips_mode/1, +-export([start/0, stop/0, info/0, info_lib/0, info_fips/0, enable_fips_mode/1, version/0, bytes_to_integer/1]). -export([cipher_info/1, hash_info/1]). -export([hash/2, hash_xof/3, hash_init/1, hash_update/2, hash_final/1, hash_final_xof/2]). @@ -238,7 +239,7 @@ end hash_equals/2, - supports/1, + supports/0, supports/1, mac/3, mac/4, macN/4, macN/5, mac_init/2, mac_init/3, mac_update/2, mac_final/1, mac_finalN/2 ]). @@ -271,6 +272,9 @@ end hash_algorithms/0, pubkey_algorithms/0, cipher_algorithms/0, kem_algorithms_nif/0, mac_algorithms/0, curve_algorithms/0, rsa_opts_algorithms/0, + fips_forbidden_hash_algorithms/0, fips_forbidden_pubkey_algorithms/0, + fips_forbidden_cipher_algorithms/0, fips_forbidden_kem_algorithms/0, + fips_forbidden_mac_algorithms/0, fips_forbidden_curve_algorithms/0, hash_info/1, hash_nif/2, hash_init_nif/1, hash_update_nif/2, hash_final_nif/1, hash_final_xof_nif/2, mac_nif/4, mac_init_nif/3, mac_update_nif/2, mac_final_nif/1, cipher_info_nif/1, ng_crypto_init_nif/4, @@ -811,23 +815,70 @@ start() -> stop() -> application:stop(crypto). --doc false. --spec supports() -> [Support] - when Support :: {hashs, Hashs} - | {ciphers, Ciphers} - | {kems, KEMs} - | {public_keys, PKs} - | {macs, Macs} - | {curves, Curves} - | {rsa_opts, RSAopts}, - 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()], - Macs :: [hmac | cmac | poly1305], - Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()], - RSAopts :: [rsa_sign_verify_opt() | rsa_opt()] . +-type digest_algorithm() :: sha1() | sha2() | sha3() | sha3_xof() | blake2() | ripemd160 | compatibility_only_hash(). +-type public_key_algorithm() :: rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m. +-type mac_algorithm() :: hmac | cmac | poly1305. +-type curve_algorithm() :: ec_named_curve() | edwards_curve_dh() | edwards_curve_ed(). +-type rsa_option() :: rsa_sign_verify_opt() | rsa_opt(). +-type supported_algorithm_list() :: [digest_algorithm()] | [cipher()] | [kem()] | [public_key_algorithm()] + | [mac_algorithm()] | [curve_algorithm()] | [rsa_option()]. + +-type supported_result_item() :: + {hashs, [digest_algorithm()]} + | {ciphers, [cipher()]} + | {kems, [kem()]} + | {public_keys, [public_key_algorithm()]} + | {macs, [cmac_cipher_algorithm()]} + | {curves, [curve_algorithm()]} + | {rsa_opts, [rsa_option()]}. + +-doc """ +Get a collection of all supported crypto algorithms, grouped per type. + +If FIPS mode is enabled and supported, the return value will also include an additional key: +`fips_forbidden`, containing lists of algorithms which are not allowed to use under FIPS mode. +Each algorithm is tried once during the `crypto` application startup. + +The `rsa_opts` key in `fips_forbidden` is returned for completeness and is always an empty list, +because the validity of each `rsa_opts` option under FIPS can only be determined based on +multiple other `rsa_opts` passed together. + +Example response with FIPS enabled: +```erlang +[{hashs, [... + {ciphers, ... + {kems, []}, + {public_keys, ... + {macs, ... + {curves, ... + {rsa_opts, ... + {fips_forbidden,[{hashs,[blake2s,blake2b,sm3,ripemd160,md5,md4]}, + {ciphers,[chacha20,sm4_ctr,sm4_ofb,sm4_cfb,sm4_ecb,sm4_cbc,...]}, + {kems,[mlkem1024,mlkem768,mlkem512]}, + {public_keys,[srp,eddh,eddsa,ecdh,ecdsa,ec_gf2m,dss]}, + {macs,[hmac,poly1305]}, + {curves,[secp256r1]}, + {rsa_opts,[]}]}] +``` +""". +-doc(#{group => <<"Utility Functions">>}). +-spec supports() -> [supported_result_item() | {fips_forbidden, [supported_result_item()]}]. supports() -> + %% Add FIPS-disabled algorithms separately for the users to see + FIPSForbidden = case application:get_env(crypto, fips_mode, false) of + true -> [ + {fips_forbidden, [ + {hashs, fips_forbidden(hashs)}, + {ciphers, fips_forbidden(ciphers)}, + {kems, fips_forbidden(kems)}, + {public_keys, fips_forbidden(public_keys)}, + {macs, fips_forbidden(macs)}, + {curves, fips_forbidden(curves)}, + {rsa_opts, []} % Always empty, added for completeness + ]} + ]; + false -> [] + end, [{hashs, supports(hashs)}, {ciphers, supports(ciphers)}, {kems, supports(kems)} @@ -836,8 +887,9 @@ supports() -> curves, rsa_opts] ] - ]. + ] ++ FIPSForbidden. +-type supported_algorithm_type() :: hashs | ciphers | kems | public_keys | macs | curves | rsa_opts. -doc """ Get which crypto algorithms that are supported by the underlying libcrypto @@ -847,28 +899,7 @@ See `hash_info/1` and `cipher_info/1` for information about the hash and cipher algorithms. """. -doc(#{since => <<"OTP 22.0">>}). --spec supports(Type) -> Support - when Type :: hashs - | ciphers - | kems - | public_keys - | macs - | curves - | rsa_opts, - Support :: Hashs - | Ciphers - | KEMs - | PKs - | Macs - | Curves - | RSAopts, - Hashs :: [sha1() | sha2() | sha3() | sha3_xof() | blake2() | ripemd160 | compatibility_only_hash()], - Ciphers :: [cipher()], - KEMs :: [kem()], - PKs :: [rsa | dss | ecdsa | dh | ecdh | eddh | ec_gf2m], - Macs :: [hmac | cmac | poly1305], - Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()], - RSAopts :: [rsa_sign_verify_opt() | rsa_opt()] . +-spec supports(supported_algorithm_type()) -> supported_algorithm_list(). -define(CURVES, '$curves$'). @@ -881,6 +912,24 @@ supports(macs) -> mac_algorithms(); supports(curves) -> curve_algorithms(); supports(rsa_opts) -> rsa_opts_algorithms(). +-doc """ +Only when FIPS mode is enabled, will return crypto algorithms that are forbidden in the FIPS mode. +When FIPS mode is disabled, always returns empty list. +""". +-doc(#{since => <<"OTP 28.2">>}). +-spec fips_forbidden(supported_algorithm_type()) -> supported_algorithm_list(). + +-doc(#{group => <<"Utility Functions">>}). +fips_forbidden(hashs) -> fips_forbidden_hash_algorithms(); +fips_forbidden(public_keys) -> fips_forbidden_pubkey_algorithms(); +fips_forbidden(ciphers) -> add_cipher_aliases(fips_forbidden_cipher_algorithms()); +fips_forbidden(kems) -> fips_forbidden_kem_algorithms(); +fips_forbidden(macs) -> fips_forbidden_mac_algorithms(); +fips_forbidden(curves) -> fips_forbidden_curve_algorithms(). +%% Missing: fips_forbidden(rsa_opts) because RSA options can only be forbidden +%% or valid together with multiple other settings, not feasible to test all +%% combinations of those early. + -doc(#{group => <<"Utility Functions">>}). -doc """ Get the name and version of the libraries used by crypto. @@ -3906,14 +3955,44 @@ hash_equals(A, B) -> hash_equals_nif(_A, _B) -> ?nif_stub. +-spec hash_algorithms() -> [digest_algorithm()]. hash_algorithms() -> ?nif_stub. + +-spec fips_forbidden_hash_algorithms() -> [digest_algorithm()]. +fips_forbidden_hash_algorithms() -> ?nif_stub. + +-spec pubkey_algorithms() -> [public_key_algorithm()]. pubkey_algorithms() -> ?nif_stub. + +-spec fips_forbidden_pubkey_algorithms() -> [public_key_algorithm()]. +fips_forbidden_pubkey_algorithms() -> ?nif_stub. + +-spec cipher_algorithms() -> [cipher()]. cipher_algorithms() -> ?nif_stub. + +-spec fips_forbidden_cipher_algorithms() -> [cipher()]. +fips_forbidden_cipher_algorithms() -> ?nif_stub. + +-spec kem_algorithms_nif() -> [kem()]. kem_algorithms_nif() -> ?nif_stub. + +-spec fips_forbidden_kem_algorithms() -> [kem()]. +fips_forbidden_kem_algorithms() -> ?nif_stub. + +-spec mac_algorithms() -> [mac_algorithm()]. mac_algorithms() -> ?nif_stub. + +-spec fips_forbidden_mac_algorithms() -> [mac_algorithm()]. +fips_forbidden_mac_algorithms() -> ?nif_stub. + +-spec curve_algorithms() -> [curve_algorithm()]. curve_algorithms() -> ?nif_stub. -rsa_opts_algorithms() -> ?nif_stub. +-spec fips_forbidden_curve_algorithms() -> [curve_algorithm()]. +fips_forbidden_curve_algorithms() -> ?nif_stub. + +-spec rsa_opts_algorithms() -> [rsa_opt()]. +rsa_opts_algorithms() -> ?nif_stub. int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); int_to_bin(X) -> int_to_bin_pos(X, []). From fa19b83cb2ee540affd73e55b3e8792bfff58f80 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 5 Nov 2025 02:39:15 +0100 Subject: [PATCH 02/15] Documentation for enable_fips_mode where we discourage its manual invocation --- lib/crypto/src/crypto.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 908074cc2eaa..a28067d6a0d4 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -1018,7 +1018,18 @@ about how to enable FIPS mode. info_fips() -> ?nif_stub. -doc """ -Enable or disable FIPs mode. +Enable or disable FIPS mode of the OpenSSL library. + +--- +Do not use this function in your code, it is designed to only be used by the crypto +library or by Erlang self-tests. This function is called automatically on first load +of the crypto NIF with the value `fips_mode :: true | false` from crypto app environment. + +This operation is not thread-safe, it should only be called once (by the Erlang crypto +library) and user code calling it, while there are SSL operations running, might get +undesired consequences, because the attached OpenSSL library structures will switch +on the fly. Unintended non-FIPS algorithms might become enabled in your FIPS-only code. +--- Argument `Enable` should be `true` to enable and `false` to disable FIPS mode. Returns `true` if the operation was successful or `false` otherwise. From 44fe090b9ab9a389d5f9a945e56543881110d224 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 5 Nov 2025 13:02:24 +0100 Subject: [PATCH 03/15] Lazy pubkey algorithms init --- lib/crypto/c_src/algorithms.c | 91 +++++++++++++++++++++++------------ lib/crypto/c_src/algorithms.h | 5 +- lib/crypto/c_src/crypto.c | 2 +- lib/crypto/c_src/fips.c | 18 ++++--- 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index cc341fa54665..0ae81463f3b0 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -63,9 +63,12 @@ enum PKEY_AVAIL_FLAGS { /* Stores all known public key algorithms with their FIPS unavailability flag if FIPS is enabled */ static struct pkey_availability_array_t { - size_t count; + ssize_t count; struct pkey_availability_t algorithm[12]; /* increase when extending the list */ -} algo_pubkey; + ErlNifMutex* mutex; +} algo_pubkey = {.count = -1, .algorithm = {{0}}, .mutex = NULL}; + +static ssize_t pubkey_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); struct kem_availability_t { const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ @@ -90,7 +93,6 @@ static struct kem_availability_array_t { struct kem_availability_t algorithm[3]; /* increase when extending the list */ } algo_kem; -void init_pubkey_types(ErlNifEnv* env); void init_kem_types(void); struct curve_availability_t { @@ -114,8 +116,8 @@ static struct curve_availability_array_t { /* [0] contains non-FIPS, and [1] contains FIPS curve details */ struct curve_availability_t algorithms[89]; /* increase when extending the list */ - ErlNifMutex* mtx_init_curve_types; -} algo_curve = {-1, {0}, NULL}; + ErlNifMutex* mutex; +} algo_curve = {.count = -1, .algorithms = {{0}}, .mutex = NULL}; static size_t curves_lazy_init(ErlNifEnv* env, bool fips_enabled); @@ -130,26 +132,33 @@ void init_algorithms_types(ErlNifEnv* env) #else init_hash_types(env); #endif - init_pubkey_types(env); init_kem_types(); init_rsa_opts_types(env); /* ciphers and macs are initiated statically */ } -int create_curve_mutex(void) +int create_algorithm_mutexes(void) { - if (!algo_curve.mtx_init_curve_types) { - algo_curve.mtx_init_curve_types = enif_mutex_create("init_curve_types"); + if (!algo_curve.mutex) { + algo_curve.mutex = enif_mutex_create("init_curve_types"); } - return !!algo_curve.mtx_init_curve_types; + if (!algo_pubkey.mutex) { + algo_pubkey.mutex = enif_mutex_create("init_pkey_algorithms"); + } + return (algo_curve.mutex != NULL) + && (algo_pubkey.mutex != NULL); } void destroy_curve_mutex(void) { - if (algo_curve.mtx_init_curve_types) { - enif_mutex_destroy(algo_curve.mtx_init_curve_types); - algo_curve.mtx_init_curve_types = NULL; + if (algo_curve.mutex) { + enif_mutex_destroy(algo_curve.mutex); + algo_curve.mutex = NULL; + } + if (algo_pubkey.mutex) { + enif_mutex_destroy(algo_pubkey.mutex); + algo_pubkey.mutex = NULL; } } @@ -249,6 +258,7 @@ static ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv* env, const bool fips_fo ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + pubkey_algorithms_lazy_init(env, FIPS_MODE()); /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false */ return pubkey_algorithms_as_list(env, false); } @@ -271,8 +281,12 @@ static void add_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, * algorithm is allowed, for non-FIPS the old behavior - always allow. * Pass 0 for atom to create one right here. */ -static void probe_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, ERL_NIF_TERM atom) { +static void probe_pubkey_algorithm(ErlNifEnv *env, const char *str_v3, + ERL_NIF_TERM atom, const bool fips_enabled) { unsigned unavailable = 0; + if (!fips_enabled) { /* No check for non-fips, all algorithms are welcome */ + return add_pubkey_algorithm(env, str_v3, unavailable, atom); + } #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); /* failed: algorithm not available, do not add */ @@ -312,44 +326,60 @@ static void probe_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, ERL_NIF_T add_pubkey_algorithm(env, str_v3, unavailable, atom); } -void init_pubkey_types(ErlNifEnv* env) { +/* Must be invoked under algo_pubkey.mutex protection */ +static void init_pubkey_types(ErlNifEnv* env, const bool fips_enabled) { // Validated algorithms first algo_pubkey.count = 0; - probe_pubkey_algorithm(env, "rsa", 0); + probe_pubkey_algorithm(env, "rsa", 0, fips_enabled); #ifdef HAVE_DSA - probe_pubkey_algorithm(env, "dss", 0); + probe_pubkey_algorithm(env, "dss", 0, fips_enabled); #endif #ifdef HAVE_DH - probe_pubkey_algorithm(env, "dh", 0); + probe_pubkey_algorithm(env, "dh", 0, fips_enabled); #endif #if defined(HAVE_EC) #if !defined(OPENSSL_NO_EC2M) - probe_pubkey_algorithm(env, "ec_gf2m", 0); + probe_pubkey_algorithm(env, "ec_gf2m", 0, fips_enabled); #endif - probe_pubkey_algorithm(env, "ecdsa", 0); - probe_pubkey_algorithm(env, "ecdh", 0); + probe_pubkey_algorithm(env, "ecdsa", 0, fips_enabled); + probe_pubkey_algorithm(env, "ecdh", 0, fips_enabled); #endif // Non-validated algorithms follow // Don't know if Edward curves are fips validated #if defined(HAVE_EDDSA) - probe_pubkey_algorithm(env, "eddsa", 0); + probe_pubkey_algorithm(env, "eddsa", 0, fips_enabled); #endif #if defined(HAVE_EDDH) - probe_pubkey_algorithm(env, "eddh", 0); + probe_pubkey_algorithm(env, "eddh", 0, fips_enabled); #endif - probe_pubkey_algorithm(env, "srp", 0); + probe_pubkey_algorithm(env, "srp", 0, fips_enabled); #ifdef HAVE_ML_DSA - probe_pubkey_algorithm(env, "mldsa44", atom_mldsa44); - probe_pubkey_algorithm(env, "mldsa65", atom_mldsa65); - probe_pubkey_algorithm(env, "mldsa87", atom_mldsa87); + probe_pubkey_algorithm(env, "mldsa44", atom_mldsa44, fips_enabled); + probe_pubkey_algorithm(env, "mldsa65", atom_mldsa65, fips_enabled); + probe_pubkey_algorithm(env, "mldsa87", atom_mldsa87, fips_enabled); #endif /* When adding a new algorithm, update the size of algo_pubkey.algorithm array */ ASSERT(algo_pubkey.count <= sizeof(algo_pubkey.algorithm)/sizeof(algo_pubkey.algorithm[0])); } +/* Perform late lazy init of pubkey algorithms, hence the need for mutex */ +static ssize_t pubkey_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + size_t result = 0; + if (algo_pubkey.count >= 0) return algo_pubkey.count; + + enif_mutex_lock(algo_pubkey.mutex); + if (algo_pubkey.count < 0) { + init_pubkey_types(env, fips_enabled); /* also updates algo_curve.count[0] or [1] */ + result = algo_curve.count; + } + enif_mutex_unlock(algo_curve.mutex); + + return result; +} + #ifdef HAVE_ML_KEM static void add_kem_algorithm(const char* str_v3, const unsigned unavail_flags, ERL_NIF_TERM atom) { struct kem_availability_t* algo = &algo_kem.algorithm[algo_kem.count]; @@ -481,12 +511,12 @@ static size_t curves_lazy_init(ErlNifEnv* env, const bool fips_enabled) { size_t result = 0; if (algo_curve.count >= 0) return algo_curve.count; - enif_mutex_lock(algo_curve.mtx_init_curve_types); + enif_mutex_lock(algo_curve.mutex); if (algo_curve.count < 0) { init_curves(env, fips_enabled); /* also updates algo_curve.count[0] or [1] */ result = algo_curve.count; } - enif_mutex_unlock(algo_curve.mtx_init_curve_types); + enif_mutex_unlock(algo_curve.mutex); return result; } @@ -542,7 +572,7 @@ static void add_curve_if_supported(const int nid, ErlNifEnv* env, bool fips_enab EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ } #else - add_curve(env, fips_enabled, str_v3, 0); + add_curve(env, str_v3, 0); #endif } @@ -1010,6 +1040,7 @@ ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_ ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef FIPS_SUPPORT + pubkey_algorithms_lazy_init(env, 1); /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == true */ return pubkey_algorithms_as_list(env, true); #else diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h index 1293fbc9579d..da406d74af62 100644 --- a/lib/crypto/c_src/algorithms.h +++ b/lib/crypto/c_src/algorithms.h @@ -25,15 +25,12 @@ #include "common.h" -int create_curve_mutex(void); +int create_algorithm_mutexes(void); void destroy_curve_mutex(void); void init_algorithms_types(ErlNifEnv* env); ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -#ifdef FIPS_SUPPORT ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -#endif ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 77ed4aa3f4d4..bbe3153ece0f 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -258,7 +258,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) if (!create_engine_mutex(env)) { ret = __LINE__; goto done; } - if (!create_curve_mutex()) { + if (!create_algorithm_mutexes()) { ret = __LINE__; goto done; } diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index 882a2f8cff5b..d96c548a473d 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -42,15 +42,19 @@ ERL_NIF_TERM enable_fips_mode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) #ifdef FIPS_SUPPORT { - if (fips_mode_to_set == atom_true) { - if (FIPS_mode_set(1)) return atom_true; - return atom_false; + bool previous_setting = FIPS_MODE(); + bool fips_mode = fips_mode_to_set == atom_true + + /* Badarg if not atom 'true' and the false value is not coming from atom 'false' */ + if (!fips_mode && fips_mode_to_set != atom_false) { + return enif_make_badarg(env); } - if (fips_mode_to_set == atom_false) { - if (!FIPS_mode_set(0)) return atom_false; - return atom_true; + + bool result = FIPS_mode_set(fips_mode) ? atom_true : atom_false; + if (result && previous_setting != fips_mode) { + /* Reinitialize the algorithms which may disappear or reappear when FIPS mode changes */ } - return enif_make_badarg(env); + return result; } #else { From 69805e9869c7910a01d3c40906b6275b6089d10a Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Thu, 6 Nov 2025 10:54:02 +0100 Subject: [PATCH 04/15] Cipher and MAC algorithms init edits --- lib/crypto/c_src/algorithms.c | 2 +- lib/crypto/c_src/cipher.c | 48 ++++++++++++++++++--------------- lib/crypto/c_src/digest.c | 51 +++++++++++++++++++++-------------- lib/crypto/c_src/mac.c | 38 ++++++++++++++------------ 4 files changed, 80 insertions(+), 59 deletions(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 0ae81463f3b0..42f8b60377e9 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -367,7 +367,7 @@ static void init_pubkey_types(ErlNifEnv* env, const bool fips_enabled) { /* Perform late lazy init of pubkey algorithms, hence the need for mutex */ static ssize_t pubkey_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { - size_t result = 0; + ssize_t result = 0; if (algo_pubkey.count >= 0) return algo_pubkey.count; enif_mutex_lock(algo_pubkey.mutex); diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 6cd813b6fb9d..19d1393920d3 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -228,34 +228,40 @@ static int is_valid_in_fips(const EVP_CIPHER *cipher) #endif /* FIPS_SUPPORT */ #endif /* HAS_3_0_API */ +static void update_cipher_type_availability(struct cipher_type_t* p) +{ +#ifdef HAS_3_0_API + if (p->str_v3) { +# ifdef FIPS_SUPPORT + EVP_CIPHER* fetched_cipher = EVP_CIPHER_fetch(NULL, p->str_v3, "fips=yes"); + /* Deeper check for validity in FIPS, also checks for NULL */ + if (is_valid_in_fips(fetched_cipher)) { + p->flags &= ~FIPS_FORBIDDEN_CIPHER; + p->cipher.p = fetched_cipher; + } else { + p->flags |= FIPS_FORBIDDEN_CIPHER; + EVP_CIPHER_free(fetched_cipher); /* NULL is allowed */ + } +# else + p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); +# endif /* FIPS_SUPPORT and >=3.0.0 */ + } +#else + if (p->cipher.funcp) { + p->cipher.p = p->cipher.funcp(); + } +#endif +} + void init_cipher_types(ErlNifEnv* env) { struct cipher_type_t* p = cipher_types; num_cipher_types = 0; - for (p = cipher_types; p->type.str; p++) { + for (/* p = cipher_types */; p->type.str; p++) { num_cipher_types++; p->type.atom = enif_make_atom(env, p->type.str); -#ifdef HAS_3_0_API - if (p->str_v3) { -# ifdef FIPS_SUPPORT - EVP_CIPHER* fetched_cipher = EVP_CIPHER_fetch(NULL, p->str_v3, "fips=yes"); - /* Deeper check for validity in FIPS, also checks for NULL */ - if (is_valid_in_fips(fetched_cipher)) { - p->flags &= ~FIPS_FORBIDDEN_CIPHER; - p->cipher.p = fetched_cipher; - } else { - p->flags |= FIPS_FORBIDDEN_CIPHER; - EVP_CIPHER_free(fetched_cipher); /* NULL is allowed */ - } -#else - p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); -# endif /* FIPS_SUPPORT and >=3.0.0 */ - } -#else - if (p->cipher.funcp) - p->cipher.p = p->cipher.funcp(); -#endif + update_cipher_type_availability(p); } p->type.atom = atom_false; /* end marker */ diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c index 6d464ff6c921..9c2ef0dc02d8 100644 --- a/lib/crypto/c_src/digest.c +++ b/lib/crypto/c_src/digest.c @@ -199,30 +199,41 @@ static int is_valid_in_fips(const EVP_MD* md) #endif /* FIPS_SUPPORT */ #endif /* HAS_3_0_API */ -void init_digest_types(ErlNifEnv* env) +static void update_digest_type_fips_flags(struct digest_type_t* p) +{ +#ifdef FIPS_SUPPORT + EVP_MD *fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); + /* Deeper check for validity in FIPS, also checks for NULL */ + if (is_valid_in_fips(fetched_md)) { + p->flags &= ~FIPS_FORBIDDEN_DIGEST; + p->md.p = fetched_md; + } else { + p->flags |= FIPS_FORBIDDEN_DIGEST; + EVP_MD_free(fetched_md); /* NULL is allowed */ + } +#else + p->md.p = EVP_MD_fetch(NULL, p->str_v3, ""); +#endif /* FIPS_SUPPORT and >=3.0.0 */ +} + +static void update_digest_type_availability(struct digest_type_t* p) { - struct digest_type_t* p = digest_types; - for (/* p = digest_types */; p->str; p++) { #ifdef HAS_3_0_API - if (p->str_v3) { -# ifdef FIPS_SUPPORT - EVP_MD* fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); - /* Deeper check for validity in FIPS, also checks for NULL */ - if (is_valid_in_fips(fetched_md)) { - p->flags &= ~FIPS_FORBIDDEN_DIGEST; - p->md.p = fetched_md; - } else { - p->flags |= FIPS_FORBIDDEN_DIGEST; - EVP_MD_free(fetched_md); /* NULL is allowed */ - } -# else - p->md.p = EVP_MD_fetch(NULL, p->str_v3, ""); -# endif /* FIPS_SUPPORT and >=3.0.0 */ - } + if (p->str_v3) { + update_digest_type_fips_flags(p); + } #else - if (p->md.funcp) - p->md.p = p->md.funcp(); + if (p->md.funcp) { + p->md.p = p->md.funcp(); + } #endif +} + +void init_digest_types(ErlNifEnv* env) +{ + struct digest_type_t* p = digest_types; + for (/* p = digest_types */; p->str; p++) { + update_digest_type_availability(p); p->atom = enif_make_atom(env, p->str); } diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index f67088e4d665..3334e90e38af 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -157,29 +157,33 @@ static int is_valid_in_fips(EVP_MAC* mac) #endif /* FIPS_SUPPORT */ #endif /* HAS_3_0_API */ -void init_mac_types(ErlNifEnv* env) -{ - struct mac_type_t* p = mac_types; - - for (/* p = mac_types */; p->name.str; p++) { - p->name.atom = enif_make_atom(env, p->name.str); +static void update_mac_type_fips_flags(struct mac_type_t* p) { #if defined(HAS_3_0_API) # ifdef FIPS_SUPPORT - { - EVP_MAC* fetched_mac = EVP_MAC_fetch(NULL, p->fetch_name, "fips=yes"); - const int unavail_flags = is_valid_in_fips(fetched_mac); /* Also tests for NULL */ - if (unavail_flags == 0) { - p->unavail_flags = 0; /* mark available */ - p->evp_mac = fetched_mac; - } else { - p->unavail_flags |= unavail_flags; - EVP_MAC_free(fetched_mac); - } + { + EVP_MAC* fetched_mac = EVP_MAC_fetch(NULL, p->fetch_name, "fips=yes"); + const int unavail_flags = is_valid_in_fips(fetched_mac); /* Also tests for NULL */ + if (unavail_flags == 0) { + p->unavail_flags = 0; /* mark available */ + p->evp_mac = fetched_mac; + } else { + p->unavail_flags |= unavail_flags; + EVP_MAC_free(fetched_mac); } + } # else - p->evp_mac = EVP_MAC_fetch(NULL, p->fetch_name, NULL); + p->evp_mac = EVP_MAC_fetch(NULL, p->fetch_name, NULL); # endif #endif +} + +void init_mac_types(ErlNifEnv* env) +{ + struct mac_type_t* p = mac_types; + + for (/* p = mac_types */; p->name.str; p++) { + p->name.atom = enif_make_atom(env, p->name.str); + update_mac_type_fips_flags(p); } p->name.atom = atom_false; /* end marker */ } From d5d08e37f0280834ed5bff9dca9ed077c6017afe Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Thu, 6 Nov 2025 16:02:01 +0100 Subject: [PATCH 05/15] New store module for algorithms --- lib/crypto/c_src/Makefile.in | 11 +- lib/crypto/c_src/algorithms.c | 140 ++++------------------- lib/crypto/c_src/algorithms.h | 2 - lib/crypto/c_src/algorithms_store.cpp | 157 ++++++++++++++++++++++++++ lib/crypto/c_src/algorithms_store.h | 50 ++++++++ lib/crypto/c_src/crypto.c | 7 +- lib/crypto/c_src/fips.c | 2 +- 7 files changed, 241 insertions(+), 128 deletions(-) create mode 100644 lib/crypto/c_src/algorithms_store.cpp create mode 100644 lib/crypto/c_src/algorithms_store.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index d36e76dd7e11..7fd68bb8b5f8 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -36,7 +36,7 @@ CC = @DED_CC@ LD = @DED_LD@ SHELL = /bin/sh LIBS = @DED_LIBS@ -LDFLAGS += @DED_LDFLAGS@ +LDFLAGS += @DED_LDFLAGS@ -lstdc++ CFLAGS = @DED_CFLAGS@ @SSL_FLAGS@ @DEFS@ # From configure @@ -91,6 +91,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/aead$(TYPEMARKER).o \ $(OBJDIR)/aes$(TYPEMARKER).o \ $(OBJDIR)/algorithms$(TYPEMARKER).o \ + $(OBJDIR)/algorithms_store$(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ @@ -119,7 +120,8 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/pbkdf2_hmac$(TYPEMARKER).o CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o -CRYPTO_STATIC_OBJS = $(patsubst $(OBJDIR)/%$(TYPEMARKER).o,$(OBJDIR)/%_static$(TYPEMARKER).o,$(CRYPTO_OBJS) $(CALLBACK_OBJS)) +CRYPTO_STATIC_OBJS = $(patsubst $(OBJDIR)/%$(TYPEMARKER).o,$(OBJDIR)/%_static$(TYPEMARKER).o,$(CRYPTO_OBJS) $(CALLBACK_OBJS)) \ + $(patsubst $(OBJDIR)/%$(TYPEMARKER).cpp.o,$(OBJDIR)/%_static$(TYPEMARKER).cpp.o,$(CRYPTO_OBJS) $(CALLBACK_OBJS)) NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a @@ -180,6 +182,7 @@ endif CONFIGURE_ARGS = -DDISABLE_EVP_DH=@DISABLE_EVP_DH@ -DDISABLE_EVP_HMAC=@DISABLE_EVP_HMAC@ ALL_CFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) +ALL_CXXFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) ALL_STATIC_CFLAGS = @DED_STATIC_CFLAGS@ $(TYPE_EXTRA_CFLAGS) $(CONFIGURE_ARGS) $(INCLUDES) # ---------------------------------------------------- @@ -222,6 +225,10 @@ $(OBJDIR)/pkey$(TYPEMARKER).o: pkey.c # ---- End of Hard-coded removal of deprecated warning for ENGINE function calls +$(OBJDIR)/%$(TYPEMARKER).cpp.o: %.cpp + $(V_at)$(INSTALL_DIR) $(OBJDIR) + $(V_CXX) -MMD -c -o $@ $(ALL_CXXFLAGS) $< + $(OBJDIR)/%$(TYPEMARKER).o: %.c $(V_at)$(INSTALL_DIR) $(OBJDIR) $(V_CC) -MMD -c -o $@ $(ALL_CFLAGS) $< diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 42f8b60377e9..769c029d09c2 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -25,6 +25,7 @@ #include "common.h" #include "mac.h" #include "pkey.h" +#include "algorithms_store.h" #include #ifdef HAS_3_0_API @@ -38,38 +39,6 @@ static ERL_NIF_TERM algo_hash[17]; /* increase when extending the list */ void init_hash_types(ErlNifEnv* env); #endif -struct pkey_availability_t { - const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ - unsigned flags; /* combination of PKEY_AVAIL_FLAGS */ - ERL_NIF_TERM atom; /* added to results when the user is querying */ -}; - -enum PKEY_AVAIL_FLAGS { - FIPS_PKEY_NOT_AVAIL = 1, - FIPS_FORBIDDEN_PKEY_KEYGEN = 2, /* not available by name */ - FIPS_FORBIDDEN_PKEY_SIGN = 4, /* not available for signing */ - FIPS_FORBIDDEN_PKEY_VERIFY = 8, /* not available for verification */ - FIPS_FORBIDDEN_PKEY_ENCRYPT = 16, /* not available for encryption */ - FIPS_FORBIDDEN_PKEY_DERIVE = 32, /* not available for key derivation */ - FIPS_FORBIDDEN_PKEY_ALL = FIPS_FORBIDDEN_PKEY_KEYGEN | FIPS_FORBIDDEN_PKEY_SIGN | - FIPS_FORBIDDEN_PKEY_VERIFY | FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE -}; - -#ifdef FIPS_SUPPORT -# define IS_PUBKEY_FORBIDDEN_IN_FIPS(p) (((p)->flags == FIPS_FORBIDDEN_PKEY_ALL || (p)->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE()) -#else -# define IS_PUBKEY_FORBIDDEN_IN_FIPS(P) false -#endif - -/* Stores all known public key algorithms with their FIPS unavailability flag if FIPS is enabled */ -static struct pkey_availability_array_t { - ssize_t count; - struct pkey_availability_t algorithm[12]; /* increase when extending the list */ - ErlNifMutex* mutex; -} algo_pubkey = {.count = -1, .algorithm = {{0}}, .mutex = NULL}; - -static ssize_t pubkey_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); - struct kem_availability_t { const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ unsigned flags; /* combination of KEM_AVAIL_FLAGS */ @@ -128,6 +97,9 @@ void init_rsa_opts_types(ErlNifEnv* env); void init_algorithms_types(ErlNifEnv* env) { + init_digest_types(env); + init_mac_types(env); + init_cipher_types(env); #ifdef HAS_3_0_API #else init_hash_types(env); @@ -137,31 +109,6 @@ void init_algorithms_types(ErlNifEnv* env) /* ciphers and macs are initiated statically */ } - -int create_algorithm_mutexes(void) -{ - if (!algo_curve.mutex) { - algo_curve.mutex = enif_mutex_create("init_curve_types"); - } - if (!algo_pubkey.mutex) { - algo_pubkey.mutex = enif_mutex_create("init_pkey_algorithms"); - } - return (algo_curve.mutex != NULL) - && (algo_pubkey.mutex != NULL); -} - -void destroy_curve_mutex(void) -{ - if (algo_curve.mutex) { - enif_mutex_destroy(algo_curve.mutex); - algo_curve.mutex = NULL; - } - if (algo_pubkey.mutex) { - enif_mutex_destroy(algo_pubkey.mutex); - algo_pubkey.mutex = NULL; - } -} - /*================================================================ Hash algorithms */ @@ -242,40 +189,6 @@ void init_hash_types(ErlNifEnv* env) { Public key algorithms */ -static ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) { - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (size_t i = 0; i < algo_pubkey.count; i++) { - struct pkey_availability_t* algo = &algo_pubkey.algorithm[i]; - - /* Any of the forbidden flags is not set, then something is available */ - if (IS_PUBKEY_FORBIDDEN_IN_FIPS(algo) == fips_forbidden) { - hd = enif_make_list_cell(env, algo->atom, hd); - } - } - return hd; -} - -ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - pubkey_algorithms_lazy_init(env, FIPS_MODE()); - /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false */ - return pubkey_algorithms_as_list(env, false); -} - -static void add_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, - const unsigned unavailable, ERL_NIF_TERM atom) -{ - struct pkey_availability_t* algo = &algo_pubkey.algorithm[algo_pubkey.count]; - algo->str_v3 = str_v3; - algo->flags = unavailable; - - if (!atom) atom = enif_make_atom(env, str_v3); - algo->atom = atom; - - algo_pubkey.count++; -} - /* * for FIPS will attempt to initialize the pubkey context to verify whether the * algorithm is allowed, for non-FIPS the old behavior - always allow. @@ -283,53 +196,52 @@ static void add_pubkey_algorithm(ErlNifEnv* env, const char* str_v3, */ static void probe_pubkey_algorithm(ErlNifEnv *env, const char *str_v3, ERL_NIF_TERM atom, const bool fips_enabled) { - unsigned unavailable = 0; + unsigned flags = 0; if (!fips_enabled) { /* No check for non-fips, all algorithms are welcome */ - return add_pubkey_algorithm(env, str_v3, unavailable, atom); + return pubkey_add_algorithm(env, str_v3, flags, atom); } #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); /* failed: algorithm not available, do not add */ if (ctx) { if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ - unavailable |= FIPS_FORBIDDEN_PKEY_KEYGEN; + flags |= FIPS_FORBIDDEN_PKEY_KEYGEN; } EVP_PKEY_CTX_free(ctx); ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); if (EVP_PKEY_sign_init(ctx) <= 0) { /* can't sign */ - unavailable |= FIPS_FORBIDDEN_PKEY_SIGN; + flags |= FIPS_FORBIDDEN_PKEY_SIGN; } EVP_PKEY_CTX_free(ctx); ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); if (EVP_PKEY_verify_init(ctx) <= 0) { /* can't verify */ - unavailable |= FIPS_FORBIDDEN_PKEY_VERIFY; + flags |= FIPS_FORBIDDEN_PKEY_VERIFY; } EVP_PKEY_CTX_free(ctx); ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); if (EVP_PKEY_encrypt_init(ctx) <= 0) { /* can't encrypt/decrypt */ - unavailable |= FIPS_FORBIDDEN_PKEY_ENCRYPT; + flags |= FIPS_FORBIDDEN_PKEY_ENCRYPT; } EVP_PKEY_CTX_free(ctx); ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); if (EVP_PKEY_derive_init(ctx) <= 0) { /* can't derive */ - unavailable |= FIPS_FORBIDDEN_PKEY_DERIVE; + flags |= FIPS_FORBIDDEN_PKEY_DERIVE; } EVP_PKEY_CTX_free(ctx); } else { - unavailable |= FIPS_PKEY_NOT_AVAIL; + flags |= FIPS_PKEY_NOT_AVAIL; } #endif /* FIPS_SUPPORT && HAS_3_0_API */ - add_pubkey_algorithm(env, str_v3, unavailable, atom); + pubkey_add_algorithm(env, str_v3, flags, atom); } -/* Must be invoked under algo_pubkey.mutex protection */ -static void init_pubkey_types(ErlNifEnv* env, const bool fips_enabled) { +/* Invoked via pubkey_algorithms_lazy_init */ +static void pubkey_algorithms_delayed_init(ErlNifEnv* env, const bool fips_enabled) { // Validated algorithms first - algo_pubkey.count = 0; probe_pubkey_algorithm(env, "rsa", 0, fips_enabled); #ifdef HAVE_DSA probe_pubkey_algorithm(env, "dss", 0, fips_enabled); @@ -361,23 +273,13 @@ static void init_pubkey_types(ErlNifEnv* env, const bool fips_enabled) { probe_pubkey_algorithm(env, "mldsa65", atom_mldsa65, fips_enabled); probe_pubkey_algorithm(env, "mldsa87", atom_mldsa87, fips_enabled); #endif - /* When adding a new algorithm, update the size of algo_pubkey.algorithm array */ - ASSERT(algo_pubkey.count <= sizeof(algo_pubkey.algorithm)/sizeof(algo_pubkey.algorithm[0])); } -/* Perform late lazy init of pubkey algorithms, hence the need for mutex */ -static ssize_t pubkey_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { - ssize_t result = 0; - if (algo_pubkey.count >= 0) return algo_pubkey.count; - - enif_mutex_lock(algo_pubkey.mutex); - if (algo_pubkey.count < 0) { - init_pubkey_types(env, fips_enabled); /* also updates algo_curve.count[0] or [1] */ - result = algo_curve.count; - } - enif_mutex_unlock(algo_curve.mutex); - - return result; +ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + pubkey_algorithms_lazy_init(env, FIPS_MODE(), &pubkey_algorithms_delayed_init); + /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false */ + return pubkey_algorithms_as_list(env, false); } #ifdef HAVE_ML_KEM @@ -1040,7 +942,7 @@ ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_ ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef FIPS_SUPPORT - pubkey_algorithms_lazy_init(env, 1); + pubkey_algorithms_lazy_init(env, 1, &pubkey_algorithms_delayed_init); /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == true */ return pubkey_algorithms_as_list(env, true); #else diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h index da406d74af62..b47f98bfbea0 100644 --- a/lib/crypto/c_src/algorithms.h +++ b/lib/crypto/c_src/algorithms.h @@ -25,8 +25,6 @@ #include "common.h" -int create_algorithm_mutexes(void); -void destroy_curve_mutex(void); void init_algorithms_types(ErlNifEnv* env); ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/algorithms_store.cpp b/lib/crypto/c_src/algorithms_store.cpp new file mode 100644 index 000000000000..af16dd4d7481 --- /dev/null +++ b/lib/crypto/c_src/algorithms_store.cpp @@ -0,0 +1,157 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +extern "C" { +#include "algorithms_store.h" +#include "common.h" +} + +// extern "C" { +// #include "algorithms.h" +// #include "cipher.h" +// #include "common.h" +// #include "mac.h" +// #include "pkey.h" +// +// #include +// #ifdef HAS_3_0_API +// #include "digest.h" +// #endif +// } + +#include + +struct mutex_lock_and_auto_release { + ErlNifMutex *mutex; + + explicit mutex_lock_and_auto_release(ErlNifMutex *m) : mutex(m) { enif_mutex_lock(m); } + ~mutex_lock_and_auto_release() { enif_mutex_unlock(mutex); } +}; + +// Stores array of algorithms for T (T can be a struct for describing a +// pubkey, mac, cipher, kem, curve ...etc) +// Statically created before the crypto library is initialized, the mutex +// must be additionally constructed (call only once) +template +struct algorithm_collection_t { + bool lazy_init_done; + std::vector algorithms; + ErlNifMutex *mutex; + const char *array_name; + + explicit algorithm_collection_t(const char *array_name) : + lazy_init_done(false), mutex(nullptr), array_name(array_name) {} + + ~algorithm_collection_t() { destroy_mutex(); } + + bool create_mutex() { + this->mutex = enif_mutex_create(const_cast(array_name)); + return this->mutex != nullptr; + } + + void destroy_mutex() { + if (this->mutex) { + enif_mutex_destroy(this->mutex); + this->mutex = nullptr; + } + } + + + void reset() { + mutex_lock_and_auto_release critical_section(this->mutex); + this->lazy_init_done = false; + this->algorithms.clear(); + } + + // Checks whether the init has already be done for the array, otherwise will invoke init_fn + size_t lazy_init(ErlNifEnv *env, const bool fips_enabled, const init_algorithms_fn init_fn) { + size_t result = 0; + if (this->lazy_init_done) { + return this->algorithms.size(); + } + + mutex_lock_and_auto_release critical_section(this->mutex); + + this->algorithms.clear(); + init_fn(env, fips_enabled); + result = this->algorithms.size(); + this->lazy_init_done = true; + + return result; + } + + ERL_NIF_TERM to_list(ErlNifEnv *env, const bool fips_forbidden) const { + ERL_NIF_TERM hd = enif_make_list(env, 0); + + for (const auto &algo: this->algorithms) { + // Any of the forbidden flags is not set, then something is available + if (algo.is_forbidden_in_fips() == fips_forbidden) { + hd = enif_make_list_cell(env, algo.get_atom(), hd); + } + } + return hd; + } +}; + +struct pkey_availability_t { + const char *str_v3; /* the algorithm name as in OpenSSL 3.x */ + unsigned flags; /* combination of PKEY_AVAIL_FLAGS */ + ERL_NIF_TERM atom; /* added to results when the user is querying */ + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return (this->flags == FIPS_FORBIDDEN_PKEY_ALL || this->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE(); +#else + return false; +#endif + } + ERL_NIF_TERM get_atom() const { return this->atom; } +}; + +static algorithm_collection_t pkey_collection("crypto.pkey_collection"); + +extern "C" bool create_algorithm_mutexes() { return pkey_collection.create_mutex(); } +extern "C" void free_algorithm_mutexes(void) { pkey_collection.destroy_mutex(); } + +extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled, + const init_algorithms_fn init_algorithms) { + return pkey_collection.lazy_init(env, fips_enabled, init_algorithms); +} +extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { + return pkey_collection.to_list(env, fips_enabled); +} +extern "C" void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavailable, + ERL_NIF_TERM atom) { + // Ensure atoms are not created repeatedly + if (!atom) { + enif_make_existing_atom(env, str_v3, &atom, ERL_NIF_UTF8); + if (!atom) { + atom = enif_make_atom(env, str_v3); + } + } + const pkey_availability_t algo = { + .str_v3 = str_v3, + .flags = unavailable, + .atom = atom, + }; + pkey_collection.algorithms.push_back(algo); +} diff --git a/lib/crypto/c_src/algorithms_store.h b/lib/crypto/c_src/algorithms_store.h new file mode 100644 index 000000000000..2005241ab765 --- /dev/null +++ b/lib/crypto/c_src/algorithms_store.h @@ -0,0 +1,50 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#ifndef E_ALGORITHMS_STORE_H +#define E_ALGORITHMS_STORE_H 1 + +#include "common.h" + +enum PKEY_AVAIL_FLAGS { + FIPS_PKEY_NOT_AVAIL = 1, + FIPS_FORBIDDEN_PKEY_KEYGEN = 2, /* not available by name */ + FIPS_FORBIDDEN_PKEY_SIGN = 4, /* not available for signing */ + FIPS_FORBIDDEN_PKEY_VERIFY = 8, /* not available for verification */ + FIPS_FORBIDDEN_PKEY_ENCRYPT = 16, /* not available for encryption */ + FIPS_FORBIDDEN_PKEY_DERIVE = 32, /* not available for key derivation */ + FIPS_FORBIDDEN_PKEY_ALL = FIPS_FORBIDDEN_PKEY_KEYGEN | FIPS_FORBIDDEN_PKEY_SIGN | FIPS_FORBIDDEN_PKEY_VERIFY | + FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE +}; + +/* C bool is int */ +bool create_algorithm_mutexes(void); +void free_algorithm_mutexes(void); + +typedef void (*init_algorithms_fn)(ErlNifEnv *env, bool fips_enabled); + +void pubkey_algorithms_reset_cache(void); +size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn init_algorithms); +ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); +void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailable, ERL_NIF_TERM atom); + +#endif /* E_ALGORITHMS_STORE_H */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index bbe3153ece0f..4ed69f81d0e6 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -54,6 +54,8 @@ #include "rsa.h" #include "srp.h" +#include "algorithms_store.h" + /* NIF interface declarations */ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); @@ -351,9 +353,6 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) #endif /* OPENSSL_THREADS */ #endif - init_digest_types(env); - init_mac_types(env); - init_cipher_types(env); init_algorithms_types(env); library_initialized = 1; @@ -410,7 +409,7 @@ static void unload_thread(void* priv_data) static void unload(ErlNifEnv* env, void* priv_data) { if (--library_refc == 0) { - destroy_curve_mutex(); + free_algorithm_mutexes(); destroy_engine_mutex(env); /* diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index d96c548a473d..d60468f70e94 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -43,7 +43,7 @@ ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) #ifdef FIPS_SUPPORT { bool previous_setting = FIPS_MODE(); - bool fips_mode = fips_mode_to_set == atom_true + bool fips_mode = fips_mode_to_set == atom_true; /* Badarg if not atom 'true' and the false value is not coming from atom 'false' */ if (!fips_mode && fips_mode_to_set != atom_false) { From 06b59d4955f40976a0e72b07ec0a6d545419a56c Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Thu, 6 Nov 2025 16:32:33 +0100 Subject: [PATCH 06/15] Functioning storage for curves and pkeys --- lib/crypto/c_src/algorithms.c | 95 ++++----------------------- lib/crypto/c_src/algorithms_store.cpp | 94 +++++++++++++++++++------- lib/crypto/c_src/algorithms_store.h | 14 +++- lib/crypto/c_src/fips.c | 3 +- 4 files changed, 96 insertions(+), 110 deletions(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 769c029d09c2..ec65a88bcfb1 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -64,32 +64,6 @@ static struct kem_availability_array_t { void init_kem_types(void); -struct curve_availability_t { - const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ - unsigned flags; /* combination of CURVE_AVAIL_FLAGS */ - ERL_NIF_TERM atom; /* as returned to the library user on a query */ -}; - -enum CURVE_AVAIL_FLAGS { - FIPS_CURVE_INIT_FAILED = 1, /* could not find by name or initialize */ -}; - -#ifdef FIPS_SUPPORT -# define IS_CURVE_FORBIDDEN_IN_FIPS(p) ((p)->flags != 0 && FIPS_MODE()) -#else -# define IS_CURVE_FORBIDDEN_IN_FIPS(P) false -#endif - -static struct curve_availability_array_t { - ssize_t count; /* Negative -1 serves as a flag for lazy initiazlilization */ - - /* [0] contains non-FIPS, and [1] contains FIPS curve details */ - struct curve_availability_t algorithms[89]; /* increase when extending the list */ - ErlNifMutex* mutex; -} algo_curve = {.count = -1, .algorithms = {{0}}, .mutex = NULL}; - -static size_t curves_lazy_init(ErlNifEnv* env, bool fips_enabled); - static size_t algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ void init_rsa_opts_types(ErlNifEnv* env); @@ -382,57 +356,11 @@ ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) Curves */ -static ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) -{ - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (size_t i = 0; i < algo_curve.count; i++) { - struct curve_availability_t *p = &algo_curve.algorithms[i]; - - if (IS_CURVE_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { - hd = enif_make_list_cell(env, p->atom, hd); - } - } - - return hd; -} - -ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - curves_lazy_init(env, FIPS_MODE()); - return curve_algorithms_as_list(env, false); -} - -static void init_curves(ErlNifEnv* env, bool fips); +void curve_algorithms_delayed_init(ErlNifEnv* env, bool fips); #if defined(HAVE_EC) static bool is_curve_valid_by_nid(int nid); #endif -/* Perform late lazy init of curve algorithms, hence the need for mutex */ -static size_t curves_lazy_init(ErlNifEnv* env, const bool fips_enabled) { - size_t result = 0; - if (algo_curve.count >= 0) return algo_curve.count; - - enif_mutex_lock(algo_curve.mutex); - if (algo_curve.count < 0) { - init_curves(env, fips_enabled); /* also updates algo_curve.count[0] or [1] */ - result = algo_curve.count; - } - enif_mutex_unlock(algo_curve.mutex); - - return result; -} - -static void add_curve(ErlNifEnv* env, const char* str_v3, - const unsigned unavail_flags) -{ - struct curve_availability_t* curve = &algo_curve.algorithms[algo_curve.count]; - curve->str_v3 = str_v3; - curve->atom = enif_make_atom(env, str_v3); - curve->flags = unavail_flags; - algo_curve.count++; -} - static void add_curve_if_supported(const int nid, ErlNifEnv* env, bool fips_enabled, const char* str_v3) { /* Some curves can be pre-checked by their NID */ if (nid && !is_curve_valid_by_nid(nid)) { /* passing NID=0 will skip this check */ @@ -468,20 +396,18 @@ static void add_curve_if_supported(const int nid, ErlNifEnv* env, bool fips_enab unavail_flags |= FIPS_CURVE_INIT_FAILED; } } - add_anyway: - add_curve(env, str_v3, unavail_flags); +add_anyway: + curve_add_algorithm(env, str_v3, unavail_flags); EVP_PKEY_free(pkey); /* NULL is allowed */ EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ } #else - add_curve(env, str_v3, 0); + curve_add_algorithm(env, str_v3, 0); #endif } -void init_curves(ErlNifEnv* env, const bool fips) { +void curve_algorithms_delayed_init(ErlNifEnv* env, const bool fips) { #if defined(HAVE_EC) - algo_curve.count = 0; - #ifdef NID_secp160k1 add_curve_if_supported(NID_secp160k1, env, fips, "secp160k1"); #else @@ -826,9 +752,6 @@ void init_curves(ErlNifEnv* env, const bool fips) { add_curve_if_supported(0, env, fips, "x448"); #endif } - - /* Check buffer overrun just in case */ - ASSERT(algo_curve.count <= sizeof(algo_curve.algorithms)/sizeof(algo_curve.algorithms[0])); #endif } @@ -891,6 +814,12 @@ bool is_curve_valid_by_nid(const int nid) { } #endif /* HAVE_EC */ +ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + curve_algorithms_lazy_init(env, FIPS_MODE(), &curve_algorithms_delayed_init); + return curve_algorithms_as_list(env, false); +} + /*================================================================ RSA Options */ @@ -976,6 +905,6 @@ ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_N } ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - curves_lazy_init(env, FIPS_MODE()); + curve_algorithms_lazy_init(env, FIPS_MODE(), &curve_algorithms_delayed_init); return curve_algorithms_as_list(env, true); } diff --git a/lib/crypto/c_src/algorithms_store.cpp b/lib/crypto/c_src/algorithms_store.cpp index af16dd4d7481..0ed630ef181b 100644 --- a/lib/crypto/c_src/algorithms_store.cpp +++ b/lib/crypto/c_src/algorithms_store.cpp @@ -25,21 +25,13 @@ extern "C" { #include "common.h" } -// extern "C" { -// #include "algorithms.h" -// #include "cipher.h" -// #include "common.h" -// #include "mac.h" -// #include "pkey.h" -// -// #include -// #ifdef HAS_3_0_API -// #include "digest.h" -// #endif -// } - #include +// RAII enif_mutex wrapper, auto releases the execution has left the scope +// { +// mutex_lock_and_auto_release x(mutex_ptr); +// ... protected code +// } <- auto released here struct mutex_lock_and_auto_release { ErlNifMutex *mutex; @@ -75,7 +67,6 @@ struct algorithm_collection_t { } } - void reset() { mutex_lock_and_auto_release critical_section(this->mutex); this->lazy_init_done = false; @@ -129,29 +120,84 @@ struct pkey_availability_t { static algorithm_collection_t pkey_collection("crypto.pkey_collection"); -extern "C" bool create_algorithm_mutexes() { return pkey_collection.create_mutex(); } -extern "C" void free_algorithm_mutexes(void) { pkey_collection.destroy_mutex(); } +struct curve_availability_t { + const char *str_v3; // the algorithm name as in OpenSSL 3.x + unsigned flags; // combination of CURVE_AVAIL_FLAGS + ERL_NIF_TERM atom; // as returned to the library user on a query + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags != 0 && FIPS_MODE(); +#else + return false; +#endif + } + ERL_NIF_TERM get_atom() const { return this->atom; } +}; + +static algorithm_collection_t curve_collection("crypto.curve_collection"); + +extern "C" bool create_algorithm_mutexes() { return pkey_collection.create_mutex() && curve_collection.create_mutex(); } + +extern "C" void free_algorithm_mutexes(void) { + pkey_collection.destroy_mutex(); + curve_collection.destroy_mutex(); +} + +extern "C" void algorithms_reset_cache() { + pkey_collection.reset(); + curve_collection.reset(); +} + +// +// Implementation of Pubkey Algorithm storage API +// extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled, - const init_algorithms_fn init_algorithms) { - return pkey_collection.lazy_init(env, fips_enabled, init_algorithms); + const init_algorithms_fn delayed_init_fn) { + return pkey_collection.lazy_init(env, fips_enabled, delayed_init_fn); } + extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { return pkey_collection.to_list(env, fips_enabled); } -extern "C" void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavailable, - ERL_NIF_TERM atom) { - // Ensure atoms are not created repeatedly + +// Ensure atoms are not created repeatedly +static ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom = 0) { if (!atom) { - enif_make_existing_atom(env, str_v3, &atom, ERL_NIF_UTF8); + enif_make_existing_atom(env, atom_name, &atom, ERL_NIF_UTF8); if (!atom) { - atom = enif_make_atom(env, str_v3); + atom = enif_make_atom(env, atom_name); } } + return atom; +} + +extern "C" void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavailable, + ERL_NIF_TERM atom) { const pkey_availability_t algo = { .str_v3 = str_v3, .flags = unavailable, - .atom = atom, + .atom = create_or_existing_atom(env, str_v3, atom), }; pkey_collection.algorithms.push_back(algo); } + +// +// Implementation of Curve Algorithm storage API +// + +extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled, + const init_algorithms_fn delayed_init_fn) { + return curve_collection.lazy_init(env, fips_enabled, delayed_init_fn); +} + +extern "C" void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavail_flags) { + const curve_availability_t curve = { + .str_v3 = str_v3, .flags = unavail_flags, .atom = create_or_existing_atom(env, str_v3)}; + curve_collection.algorithms.push_back(curve); +} + +extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { + return curve_collection.to_list(env, fips_enabled); +} diff --git a/lib/crypto/c_src/algorithms_store.h b/lib/crypto/c_src/algorithms_store.h index 2005241ab765..29a20e14c0d6 100644 --- a/lib/crypto/c_src/algorithms_store.h +++ b/lib/crypto/c_src/algorithms_store.h @@ -36,15 +36,25 @@ enum PKEY_AVAIL_FLAGS { FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE }; +enum CURVE_AVAIL_FLAGS { + FIPS_CURVE_INIT_FAILED = 1 /* could not find by name or initialize */ +}; + /* C bool is int */ bool create_algorithm_mutexes(void); void free_algorithm_mutexes(void); +void algorithms_reset_cache(void); // Called on fips mode change to reinitialize the algorithm lists typedef void (*init_algorithms_fn)(ErlNifEnv *env, bool fips_enabled); -void pubkey_algorithms_reset_cache(void); -size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn init_algorithms); +/* Pubkey Algorithms storage API */ +size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn delayed_init_fn); ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailable, ERL_NIF_TERM atom); +/* Curve Algorithms storage API */ +size_t curve_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn delayed_init_fn); +void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavail_flags); +ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); + #endif /* E_ALGORITHMS_STORE_H */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index d60468f70e94..f6c7beda8322 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -22,7 +22,7 @@ #include "fips.h" #include "digest.h" - +#include "algorithms_store.h" ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -53,6 +53,7 @@ ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) bool result = FIPS_mode_set(fips_mode) ? atom_true : atom_false; if (result && previous_setting != fips_mode) { /* Reinitialize the algorithms which may disappear or reappear when FIPS mode changes */ + algorithms_reset_cache(); } return result; } From d3196a050b724c9957c31d43df8a54b9d6036a59 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Fri, 7 Nov 2025 22:36:26 +0100 Subject: [PATCH 07/15] Minor changes --- lib/crypto/c_src/algorithms.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index ec65a88bcfb1..65ed2fb4e280 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -24,7 +24,6 @@ #include "cipher.h" #include "common.h" #include "mac.h" -#include "pkey.h" #include "algorithms_store.h" #include From dc92bacc15ea25a828a255988da5968e15a83e89 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Sat, 8 Nov 2025 19:12:11 +0100 Subject: [PATCH 08/15] Pubkey collections implementation --- lib/crypto/c_src/Makefile.in | 8 +- lib/crypto/c_src/algorithms.c | 126 +------- lib/crypto/c_src/algorithms_collection.cpp | 53 ++++ lib/crypto/c_src/algorithms_collection.h | 142 +++++++++ lib/crypto/c_src/algorithms_curve.cpp | 45 +++ lib/crypto/c_src/algorithms_curve.h | 73 +++++ lib/crypto/c_src/algorithms_digest.cpp | 163 +++++++++++ lib/crypto/c_src/algorithms_digest.h | 108 +++++++ lib/crypto/c_src/algorithms_pubkey.cpp | 124 ++++++++ ...algorithms_store.h => algorithms_pubkey.h} | 66 +++-- lib/crypto/c_src/algorithms_store.cpp | 203 ------------- lib/crypto/c_src/crypto.c | 6 +- lib/crypto/c_src/digest.c | 269 ------------------ lib/crypto/c_src/digest.h | 57 ---- lib/crypto/c_src/fips.c | 4 +- lib/crypto/c_src/hash.c | 14 +- lib/crypto/c_src/hmac.c | 4 +- lib/crypto/c_src/mac.c | 10 +- lib/crypto/c_src/pbkdf2_hmac.c | 6 +- lib/crypto/c_src/pkey.c | 4 +- 20 files changed, 795 insertions(+), 690 deletions(-) create mode 100644 lib/crypto/c_src/algorithms_collection.cpp create mode 100644 lib/crypto/c_src/algorithms_collection.h create mode 100644 lib/crypto/c_src/algorithms_curve.cpp create mode 100644 lib/crypto/c_src/algorithms_curve.h create mode 100644 lib/crypto/c_src/algorithms_digest.cpp create mode 100644 lib/crypto/c_src/algorithms_digest.h create mode 100644 lib/crypto/c_src/algorithms_pubkey.cpp rename lib/crypto/c_src/{algorithms_store.h => algorithms_pubkey.h} (53%) delete mode 100644 lib/crypto/c_src/algorithms_store.cpp delete mode 100644 lib/crypto/c_src/digest.c delete mode 100644 lib/crypto/c_src/digest.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 7fd68bb8b5f8..557159f4dc86 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -91,7 +91,10 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/aead$(TYPEMARKER).o \ $(OBJDIR)/aes$(TYPEMARKER).o \ $(OBJDIR)/algorithms$(TYPEMARKER).o \ - $(OBJDIR)/algorithms_store$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_collection$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_digest$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_pubkey$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_curve$(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ @@ -99,7 +102,6 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/cmac$(TYPEMARKER).o \ $(OBJDIR)/common$(TYPEMARKER).o \ $(OBJDIR)/dh$(TYPEMARKER).o \ - $(OBJDIR)/digest$(TYPEMARKER).o \ $(OBJDIR)/dss$(TYPEMARKER).o \ $(OBJDIR)/ec$(TYPEMARKER).o \ $(OBJDIR)/ecdh$(TYPEMARKER).o \ @@ -182,7 +184,7 @@ endif CONFIGURE_ARGS = -DDISABLE_EVP_DH=@DISABLE_EVP_DH@ -DDISABLE_EVP_HMAC=@DISABLE_EVP_HMAC@ ALL_CFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) -ALL_CXXFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) +ALL_CXXFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) -std=c++14 ALL_STATIC_CFLAGS = @DED_STATIC_CFLAGS@ $(TYPE_EXTRA_CFLAGS) $(CONFIGURE_ARGS) $(INCLUDES) # ---------------------------------------------------- diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 65ed2fb4e280..f0aa981cf947 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -21,22 +21,20 @@ */ #include "algorithms.h" +#include "algorithms_collection.h" #include "cipher.h" #include "common.h" #include "mac.h" -#include "algorithms_store.h" #include -#ifdef HAS_3_0_API -#include "digest.h" -#endif +#include "algorithms_digest.h" -#ifdef HAS_3_0_API -#else -static size_t algo_hash_cnt, algo_hash_fips_cnt; -static ERL_NIF_TERM algo_hash[17]; /* increase when extending the list */ -void init_hash_types(ErlNifEnv* env); -#endif +// #ifdef HAS_3_0_API +// #else +// static size_t algo_hash_cnt, algo_hash_fips_cnt; +// static ERL_NIF_TERM algo_hash[17]; /* increase when extending the list */ +// void init_hash_types(ErlNifEnv* env); +// #endif struct kem_availability_t { const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ @@ -70,13 +68,8 @@ void init_rsa_opts_types(ErlNifEnv* env); void init_algorithms_types(ErlNifEnv* env) { - init_digest_types(env); init_mac_types(env); init_cipher_types(env); -#ifdef HAS_3_0_API -#else - init_hash_types(env); -#endif init_kem_types(); init_rsa_opts_types(env); /* ciphers and macs are initiated statically */ @@ -88,14 +81,8 @@ void init_algorithms_types(ErlNifEnv* env) ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef HAS_3_0_API + digest_types_lazy_init(env, FIPS_MODE()); return digest_types_as_list(env, false); -#else - unsigned int cnt = - FIPS_MODE() ? algo_hash_fips_cnt : algo_hash_cnt; - - return enif_make_list_from_array(env, algo_hash, cnt); -#endif } #ifdef HAS_3_0_API @@ -158,96 +145,6 @@ void init_hash_types(ErlNifEnv* env) { } #endif -/*================================================================ - Public key algorithms -*/ - -/* - * for FIPS will attempt to initialize the pubkey context to verify whether the - * algorithm is allowed, for non-FIPS the old behavior - always allow. - * Pass 0 for atom to create one right here. - */ -static void probe_pubkey_algorithm(ErlNifEnv *env, const char *str_v3, - ERL_NIF_TERM atom, const bool fips_enabled) { - unsigned flags = 0; - if (!fips_enabled) { /* No check for non-fips, all algorithms are welcome */ - return pubkey_add_algorithm(env, str_v3, flags, atom); - } -#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); - /* failed: algorithm not available, do not add */ - if (ctx) { - if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ - flags |= FIPS_FORBIDDEN_PKEY_KEYGEN; - } - EVP_PKEY_CTX_free(ctx); - - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_sign_init(ctx) <= 0) { /* can't sign */ - flags |= FIPS_FORBIDDEN_PKEY_SIGN; - } - EVP_PKEY_CTX_free(ctx); - - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_verify_init(ctx) <= 0) { /* can't verify */ - flags |= FIPS_FORBIDDEN_PKEY_VERIFY; - } - EVP_PKEY_CTX_free(ctx); - - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_encrypt_init(ctx) <= 0) { /* can't encrypt/decrypt */ - flags |= FIPS_FORBIDDEN_PKEY_ENCRYPT; - } - EVP_PKEY_CTX_free(ctx); - - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_derive_init(ctx) <= 0) { /* can't derive */ - flags |= FIPS_FORBIDDEN_PKEY_DERIVE; - } - EVP_PKEY_CTX_free(ctx); - } else { - flags |= FIPS_PKEY_NOT_AVAIL; - } -#endif /* FIPS_SUPPORT && HAS_3_0_API */ - pubkey_add_algorithm(env, str_v3, flags, atom); -} - -/* Invoked via pubkey_algorithms_lazy_init */ -static void pubkey_algorithms_delayed_init(ErlNifEnv* env, const bool fips_enabled) { - // Validated algorithms first - probe_pubkey_algorithm(env, "rsa", 0, fips_enabled); -#ifdef HAVE_DSA - probe_pubkey_algorithm(env, "dss", 0, fips_enabled); -#endif - -#ifdef HAVE_DH - probe_pubkey_algorithm(env, "dh", 0, fips_enabled); -#endif - -#if defined(HAVE_EC) -#if !defined(OPENSSL_NO_EC2M) - probe_pubkey_algorithm(env, "ec_gf2m", 0, fips_enabled); -#endif - probe_pubkey_algorithm(env, "ecdsa", 0, fips_enabled); - probe_pubkey_algorithm(env, "ecdh", 0, fips_enabled); -#endif - - // Non-validated algorithms follow - // Don't know if Edward curves are fips validated -#if defined(HAVE_EDDSA) - probe_pubkey_algorithm(env, "eddsa", 0, fips_enabled); -#endif -#if defined(HAVE_EDDH) - probe_pubkey_algorithm(env, "eddh", 0, fips_enabled); -#endif - probe_pubkey_algorithm(env, "srp", 0, fips_enabled); -#ifdef HAVE_ML_DSA - probe_pubkey_algorithm(env, "mldsa44", atom_mldsa44, fips_enabled); - probe_pubkey_algorithm(env, "mldsa65", atom_mldsa65, fips_enabled); - probe_pubkey_algorithm(env, "mldsa87", atom_mldsa87, fips_enabled); -#endif -} - ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { pubkey_algorithms_lazy_init(env, FIPS_MODE(), &pubkey_algorithms_delayed_init); @@ -861,11 +758,8 @@ void init_rsa_opts_types(ErlNifEnv* env) { } ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef FIPS_SUPPORT + digest_types_lazy_init(env, FIPS_MODE()); return digest_types_as_list(env, true); -#else - return enif_make_list(env, 0); /* nothing is forbidden */ -#endif } ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/lib/crypto/c_src/algorithms_collection.cpp b/lib/crypto/c_src/algorithms_collection.cpp new file mode 100644 index 000000000000..83bf8a4e17d6 --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.cpp @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_collection.h" +#include "algorithms_curve.h" +#include "algorithms_digest.h" +#include "algorithms_pubkey.h" + +extern "C" bool create_algorithm_mutexes() { + return pubkey_collection.create_mutex() && curve_collection.create_mutex() && digest_collection.create_mutex(); +} + +extern "C" void free_algorithm_mutexes(void) { + pubkey_collection.destroy_mutex(); + curve_collection.destroy_mutex(); + digest_collection.destroy_mutex(); +} + +extern "C" void algorithms_reset_cache() { + pubkey_collection.reset(); + curve_collection.reset(); + digest_collection.reset(); +} + +// Ensure atoms are not created repeatedly. Pass atom=0 to attempt creating an existing atom (then a new atom). +ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom) { + if (!atom) { + enif_make_existing_atom(env, atom_name, &atom, ERL_NIF_UTF8); + if (!atom) { + atom = enif_make_atom(env, atom_name); + } + } + return atom; +} diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h new file mode 100644 index 000000000000..2e7d131a21b4 --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.h @@ -0,0 +1,142 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif +#include "common.h" + +// +// C API which affect all collections at once +// + +// Creates protective mutex for each collection to allow for safe lazy init and reinit +bool create_algorithm_mutexes(void); +// Deletes (and zeroes) algorithm mutexes +void free_algorithm_mutexes(void); +// Called on fips mode change to reset the algorithm lists. Next lazy_init call to each collection will do the work +// again. +void algorithms_reset_cache(void); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +#include + +// RAII enif_mutex wrapper, auto releases the execution has left the scope +// { +// mutex_lock_and_auto_release x(mutex_ptr); +// ... protected code +// } <- auto released here +struct mutex_lock_and_auto_release { + ErlNifMutex *mutex; + + explicit mutex_lock_and_auto_release(ErlNifMutex *m) : mutex(m) { enif_mutex_lock(m); } + ~mutex_lock_and_auto_release() { enif_mutex_unlock(mutex); } +}; + +// Stores array of algorithms for detected algorithms of type AlgorithmT and to +// populate the array, a second type is provided: ProbeT, this is a struct containing +// logic to detect algorithm availability and create AlgorithmT. +// +// The collections for all types of algorithms are statically created before the crypto +// library is initialized, but the mutex must be additionally constructed (call only once). +template +struct algorithm_collection_t { +private: + bool lazy_init_done; + // each probe is executed every time we reset and repopulate algorithms list. Probes are not const, because + // their implementations might want to cache something like found existing atoms by string + ProbeT *probes; + const size_t probe_count; + // contains detected and supported algorithms + std::vector algorithms; + ErlNifMutex *mutex; + const char *debug_name; + +public: + explicit algorithm_collection_t(const char *debug_name, ProbeT *probes_, const size_t probe_count_) : + lazy_init_done(false), probes(probes_), probe_count(probe_count_), mutex(nullptr), debug_name(debug_name) {} + + ~algorithm_collection_t() { destroy_mutex(); } + + bool create_mutex() { + this->mutex = enif_mutex_create(const_cast(debug_name)); + return this->mutex != nullptr; + } + + void destroy_mutex() { + if (this->mutex) { + enif_mutex_destroy(this->mutex); + this->mutex = nullptr; + } + } + + // Resets the found algorithms list and the flag for lazy init, so lazy init will + void reset() { + mutex_lock_and_auto_release critical_section(this->mutex); + this->lazy_init_done = false; + this->algorithms.clear(); + } + + // Checks whether the init has already been done for the array, otherwise will invoke init_fn + size_t lazy_init(ErlNifEnv *env, const bool fips_enabled) { + size_t result = 0; + if (this->lazy_init_done) { + return this->algorithms.size(); + } + + mutex_lock_and_auto_release critical_section(this->mutex); + + this->algorithms.clear(); + for (auto i = 0; i < probe_count; i++) { + // For each probe, call probe() member function, in case of success the probe code + // will use the passed 'this->algorithms' reference to add an algorithm to the collection. + probes[i].probe(env, fips_enabled, this->algorithms); + } + result = this->algorithms.size(); + this->lazy_init_done = true; + + return result; + } + + ERL_NIF_TERM to_list(ErlNifEnv *env, const bool fips_forbidden) const { + ERL_NIF_TERM hd = enif_make_list(env, 0); + + for (const auto &algo: this->algorithms) { + // Any of the forbidden flags is not set, then something is available + if (algo.is_forbidden_in_fips() == fips_forbidden) { + hd = enif_make_list_cell(env, algo.get_atom(), hd); + } + } + return hd; + } +}; + +// Ensure atoms are not created repeatedly +ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom = 0); + +#endif diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp new file mode 100644 index 000000000000..97d23dda76a6 --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -0,0 +1,45 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_curve.h" + +curve_probe_t curve_probes[] = {}; + +curve_collection_t curve_collection("crypto.curve_collection", curve_probes, sizeof(curve_probes) / sizeof(curve_probes[0])); + +// +// Implementation of Curve Algorithm storage API +// + +extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled) { + return curve_collection.lazy_init(env, fips_enabled); +} + +extern "C" void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavail_flags) { + const curve_availability_t curve = { + .str_v3 = str_v3, .flags = unavail_flags, .atom = create_or_existing_atom(env, str_v3)}; + curve_collection.algorithms.push_back(curve); +} + +extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { + return curve_collection.to_list(env, fips_enabled); +} diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h new file mode 100644 index 000000000000..54cd8865f070 --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.h @@ -0,0 +1,73 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once +#include "algorithms_collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// +// Curve Algorithms storage API +// +size_t curve_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled); +void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavail_flags); +ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +struct curve_availability_t { + const char *str_v3; // the algorithm name as in OpenSSL 3.x + unsigned flags; // combination of CURVE_AVAIL_FLAGS + ERL_NIF_TERM atom; // as returned to the library user on a query + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags != 0 && FIPS_MODE(); +#else + return false; +#endif + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const { return this->atom; } +}; + +enum CURVE_AVAIL_FLAGS { + FIPS_CURVE_INIT_FAILED = 1 // could not find by name or initialize +}; + +struct curve_probe_t { + // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); +}; + +// Forward declaration, find +using curve_collection_t = algorithm_collection_t; +extern curve_collection_t curve_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp new file mode 100644 index 000000000000..a4caac93bac0 --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -0,0 +1,163 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_digest.h" +#include "algorithms_collection.h" + +static digest_probe_t digest_probes[] = { +#ifdef HAVE_MD4 + {.str = "md4", .str_v3 = "MD4", .constructor_fn = &EVP_md4}, +#endif +#ifdef HAVE_MD5 + {.str = "md5", .str_v3 = "MD5", .constructor_fn = &EVP_md5}, +#endif +#ifdef HAVE_RIPEMD160 + {.str = "ripemd160", .str_v3 = "RIPEMD160", .constructor_fn = &EVP_ripemd160}, +#endif + {.str = "sha", .str_v3 = "SHA1", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha1}, +#ifdef HAVE_SHA224 + {.str = "sha224", .str_v3 = "SHA2-224", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha224}, +#endif +#ifdef HAVE_SHA256 + {.str = "sha256", .str_v3 = "SHA2-256", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha256}, +#endif +#ifdef HAVE_SHA384 + {.str = "sha384", .str_v3 = "SHA2-384", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha384}, +#endif +#ifdef HAVE_SHA512 + {.str = "sha512", .str_v3 = "SHA2-512", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha512}, +#endif +#ifdef HAVE_SHA512_224 + {.str = "sha512_224", + .str_v3 = "SHA2-512/224", + .flags_hint = PBKDF2_ELIGIBLE_DIGEST, + .constructor_fn = &EVP_sha512_224}, +#endif +#ifdef HAVE_SHA512_256 + {.str = "sha512_256", + .str_v3 = "SHA2-512/256", + .flags_hint = PBKDF2_ELIGIBLE_DIGEST, + .constructor_fn = &EVP_sha512_256}, +#endif +#ifdef HAVE_SHA3_224 + {.str = "sha3_224", .str_v3 = "SHA3-224", .constructor_fn = &EVP_sha3_224}, +#endif +#ifdef HAVE_SHA3_256 + {.str = "sha3_256", .str_v3 = "SHA3-256", .constructor_fn = &EVP_sha3_256}, +#endif +#ifdef HAVE_SHA3_384 + {.str = "sha3_384", .str_v3 = "SHA3-384", .constructor_fn = &EVP_sha3_384}, +#endif +#ifdef HAVE_SHA3_512 + {.str = "sha3_512", .str_v3 = "SHA3-512", .constructor_fn = &EVP_sha3_512}, +#endif +#ifdef HAVE_SHAKE128 + {.str = "shake128", .str_v3 = "SHAKE-128", .constructor_fn = &EVP_shake128, .xof_default_length = 6}, +#endif +#ifdef HAVE_SHAKE256 + {.str = "shake256", .str_v3 = "SHAKE-256", .constructor_fn = &EVP_shake256, .xof_default_length = 32}, +#endif +#ifdef HAVE_SM3 + {.str = "sm3", .str_v3 = "SM3", .constructor_fn = &EVP_sm3}, +#endif +#ifdef HAVE_BLAKE2 + {.str = "blake2b", .str_v3 = "BLAKE2b512", .constructor_fn = &EVP_blake2b512}, +#endif +#ifdef HAVE_BLAKE2 + {.str = "blake2s", + .str_v3 = "BLAKE2s256", + .constructor_fn = &EVP_blake2s256}, +#endif +}; + +digest_collection_t digest_collection("crypto.digest.digest_collection", digest_probes, + sizeof(digest_probes) / sizeof(digest_probes[0])); + +ERL_NIF_TERM digest_availability_t::get_atom() const { return this->init->atom; } + +#ifdef HAS_3_0_API +#ifdef FIPS_SUPPORT +/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ +static int is_valid_in_fips(const EVP_MD *md) { + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + int usable = 0; + + if (md) { + /* Try to initialize the digest algorithm for use, this will check the dependencies */ + if (EVP_DigestInit_ex(ctx, md, NULL) == 1) { + usable = 1; + } + } + + EVP_MD_CTX_free(ctx); + return usable; +} +#endif /* FIPS_SUPPORT */ +#endif /* HAS_3_0_API */ + +static void update_digest_type_fips_flags(struct digest_availability_t *p) { +#ifdef FIPS_SUPPORT + EVP_MD *fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); + /* Deeper check for validity in FIPS, also checks for NULL */ + if (is_valid_in_fips(fetched_md)) { + p->flags &= ~FIPS_FORBIDDEN_DIGEST; + p->md.p = fetched_md; + } else { + p->flags |= FIPS_FORBIDDEN_DIGEST; + EVP_MD_free(fetched_md); /* NULL is allowed */ + } +#else + p->md = EVP_MD_fetch(NULL, p->str_v3, ""); +#endif /* FIPS_SUPPORT and >=3.0.0 */ +} + +static void update_digest_type_availability(struct digest_availability_t *p) { +#ifdef HAS_3_0_API + if (p->str_v3) { + update_digest_type_fips_flags(p); + } +#else + if (p->md.funcp) { + p->md.p = p->md.funcp(); + } +#endif +} + +void digest_types_delayed_init(ErlNifEnv *env) { + for (struct digest_probe_t *p = digest_types; p->str; p++) { + update_digest_type_availability(p); + p->atom = enif_make_atom(env, p->init->str); + } + + p->atom = atom_false; /* end marker */ +} + +struct digest_availability_t *get_digest_type(ERL_NIF_TERM type) { + struct digest_availability_t *p = NULL; + for (p = digest_types; p->atom != atom_false; p++) { + if (type == p->atom) { + return p; + } + } + + return NULL; +} diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h new file mode 100644 index 000000000000..722a8c27b17a --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.h @@ -0,0 +1,108 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once + +#include "algorithms_collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// +// C Digest storage API +// +void digest_types_lazy_init(ErlNifEnv *env, bool fips_mode); +void digest_types_delayed_init(ErlNifEnv *env); +struct digest_availability_t *get_digest_type(ERL_NIF_TERM type); +ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + + +// Describes a digest method added by the init function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. +struct digest_availability_t { + // The definition used to create this record + const struct digest_probe_t *init; + // combination of DIGEST_TYPE_FLAGS from init + detected in runtime + size_t flags; + // after init will contain the algorithm pointer, NULL if not supported + EVP_MD *md; + // 0 or default digest length for XOF digests + size_t xof_default_length; + + digest_availability_t() = default; + ~digest_availability_t() { + if (md) { + EVP_MD_meth_free(md); + } + } + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return (this->flags & FIPS_FORBIDDEN_DIGEST) && FIPS_MODE(); +#else + return false; +#endif + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; +}; + +// masks in the `flags` field of digest_type_t +enum DIGEST_TYPE_FLAGS { + FIPS_FORBIDDEN_DIGEST = 1, /* no support in FIPS for digest */ + PBKDF2_ELIGIBLE_DIGEST = 2 +}; + +// This runs for each algorithm at library start and every time FIPS mode changes. +// Describes data required by digest availability probing algorithm (separate branches for +// OpenSSL API < 3.0, 3.0, and for FIPS enabled/disabled). +struct digest_probe_t { + // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); + + // the algorithm name as in OpenSSL < 3, also atom used by Erlang API + const char *str = nullptr; + // the algorithm name as in OpenSSL 3.x + const char *str_v3 = nullptr; + // This will be updated to created atomfound exi + ERL_NIF_TERM atom = 0; + // initial DIGEST_TYPE_FLAGS value for digest_type_t::flags + const unsigned flags_hint = 0; + // OpenSSL API to create this digest + const EVP_MD *(*constructor_fn)() = nullptr; + size_t xof_default_length = 0; +}; + +using digest_collection_t = algorithm_collection_t; +extern digest_collection_t digest_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp new file mode 100644 index 000000000000..507a0a69ecf0 --- /dev/null +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -0,0 +1,124 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_pubkey.h" +#include "algorithms_collection.h" + +pubkey_probe_t pubkey_probes[] = { + {.str = "rsa"}, +#ifdef HAVE_DSA + {.str = "dss"}, +#endif +#ifdef HAVE_DH + {.str = "dh"}, +#endif +#if defined(HAVE_EC) +#if !defined(OPENSSL_NO_EC2M) + {.str = "ec_gf2m"}, +#endif + {.str = "ecdsa"}, {.str = "ecdh"}, +#endif +// Non-validated algorithms follow +// Don't know if Edward curves are fips validated +#if defined(HAVE_EDDSA) + {.str = "eddsa"}, +#endif +#if defined(HAVE_EDDH) + {.str = "eddh"}, +#endif + {.str = "srp"}, +#ifdef HAVE_ML_DSA + {.str = "mldsa44"}, {.str = "mldsa65"}, {.str = "mldsa87"}, +#endif +}; + +pubkey_collection_t pubkey_collection("crypto.pkey_collection", pubkey_probes, + sizeof(pubkey_probes) / sizeof(pubkey_probes[0])); + +// +// Implementation of Pubkey Algorithm storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled) { + return pubkey_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { + return pubkey_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM pubkey_availability_t::get_atom() const { return this->init->atom; } + +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) +// FIPS is supported AND enabled here +void pubkey_probe_t::probe_algorithm_against_fips(size_t flags) const { + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); + /* failed: algorithm not available, do not add */ + if (ctx) { + if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ + flags |= FIPS_FORBIDDEN_PKEY_KEYGEN; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_sign_init(ctx) <= 0) { /* can't sign */ + flags |= FIPS_FORBIDDEN_PKEY_SIGN; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_verify_init(ctx) <= 0) { /* can't verify */ + flags |= FIPS_FORBIDDEN_PKEY_VERIFY; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_encrypt_init(ctx) <= 0) { /* can't encrypt/decrypt */ + flags |= FIPS_FORBIDDEN_PKEY_ENCRYPT; + } + EVP_PKEY_CTX_free(ctx); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + if (EVP_PKEY_derive_init(ctx) <= 0) { /* can't derive */ + flags |= FIPS_FORBIDDEN_PKEY_DERIVE; + } + EVP_PKEY_CTX_free(ctx); + } else { + flags |= FIPS_PKEY_NOT_AVAIL; + } +} +#endif // FIPS_SUPPORT && HAS_3_0_API + +// for FIPS we will attempt to initialize the pubkey context to verify whether the +// algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. +void pubkey_probe_t::probe(ErlNifEnv *env, bool fips_enabled, std::vector &output) { + size_t flags = 0; + if (!fips_enabled) { // No check for non-fips, all algorithms are welcome + return output.push_back({.init = this, .flags = flags}); + } +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + flags = this->probe_algorithm_against_fips(flags); +#endif // FIPS_SUPPORT && HAS_3_0_API + output.push_back({.init = this, .flags = flags}); +} diff --git a/lib/crypto/c_src/algorithms_store.h b/lib/crypto/c_src/algorithms_pubkey.h similarity index 53% rename from lib/crypto/c_src/algorithms_store.h rename to lib/crypto/c_src/algorithms_pubkey.h index 29a20e14c0d6..119f23c306f3 100644 --- a/lib/crypto/c_src/algorithms_store.h +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -20,11 +20,26 @@ * %CopyrightEnd% */ -#ifndef E_ALGORITHMS_STORE_H -#define E_ALGORITHMS_STORE_H 1 +#pragma once +#include "algorithms_collection.h" + +#ifdef __cplusplus +extern "C" { +#endif #include "common.h" +// +// Pubkey Algorithms storage C API +// +size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled); +ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); +void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailable, ERL_NIF_TERM atom); + +#ifdef __cplusplus +} +#endif + enum PKEY_AVAIL_FLAGS { FIPS_PKEY_NOT_AVAIL = 1, FIPS_FORBIDDEN_PKEY_KEYGEN = 2, /* not available by name */ @@ -36,25 +51,40 @@ enum PKEY_AVAIL_FLAGS { FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE }; -enum CURVE_AVAIL_FLAGS { - FIPS_CURVE_INIT_FAILED = 1 /* could not find by name or initialize */ +#ifdef __cplusplus + +struct pubkey_probe_t; + +struct pubkey_availability_t { + const pubkey_probe_t *init; // the pubkey_probe_t used to create this record + size_t flags; // combination of PKEY_AVAIL_FLAGS + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return (this->flags == FIPS_FORBIDDEN_PKEY_ALL || this->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE(); +#else + return false; +#endif + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; }; -/* C bool is int */ -bool create_algorithm_mutexes(void); -void free_algorithm_mutexes(void); -void algorithms_reset_cache(void); // Called on fips mode change to reinitialize the algorithm lists +struct pubkey_probe_t { + const char *str = nullptr; + const char *str_v3 = nullptr; // keep this nullptr to avoid duplcaticatiuonon, .str will be used instead + ERL_NIF_TERM atom = 0; -typedef void (*init_algorithms_fn)(ErlNifEnv *env, bool fips_enabled); + // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv *env, bool fips_enabled, std::vector &output); -/* Pubkey Algorithms storage API */ -size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn delayed_init_fn); -ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); -void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailable, ERL_NIF_TERM atom); +private: +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + void probe_algorithm_against_fips(size_t flags) const; // fips is supported AND enabled here +#endif // FIPS_SUPPORT && HAS_3_0_API +}; -/* Curve Algorithms storage API */ -size_t curve_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled, init_algorithms_fn delayed_init_fn); -void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavail_flags); -ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); +using pubkey_collection_t = algorithm_collection_t; +extern pubkey_collection_t pubkey_collection; -#endif /* E_ALGORITHMS_STORE_H */ +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_store.cpp b/lib/crypto/c_src/algorithms_store.cpp deleted file mode 100644 index 0ed630ef181b..000000000000 --- a/lib/crypto/c_src/algorithms_store.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * %CopyrightBegin% - * - * SPDX-License-Identifier: Apache-2.0 - * - * Copyright Ericsson AB 2010-2025. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -extern "C" { -#include "algorithms_store.h" -#include "common.h" -} - -#include - -// RAII enif_mutex wrapper, auto releases the execution has left the scope -// { -// mutex_lock_and_auto_release x(mutex_ptr); -// ... protected code -// } <- auto released here -struct mutex_lock_and_auto_release { - ErlNifMutex *mutex; - - explicit mutex_lock_and_auto_release(ErlNifMutex *m) : mutex(m) { enif_mutex_lock(m); } - ~mutex_lock_and_auto_release() { enif_mutex_unlock(mutex); } -}; - -// Stores array of algorithms for T (T can be a struct for describing a -// pubkey, mac, cipher, kem, curve ...etc) -// Statically created before the crypto library is initialized, the mutex -// must be additionally constructed (call only once) -template -struct algorithm_collection_t { - bool lazy_init_done; - std::vector algorithms; - ErlNifMutex *mutex; - const char *array_name; - - explicit algorithm_collection_t(const char *array_name) : - lazy_init_done(false), mutex(nullptr), array_name(array_name) {} - - ~algorithm_collection_t() { destroy_mutex(); } - - bool create_mutex() { - this->mutex = enif_mutex_create(const_cast(array_name)); - return this->mutex != nullptr; - } - - void destroy_mutex() { - if (this->mutex) { - enif_mutex_destroy(this->mutex); - this->mutex = nullptr; - } - } - - void reset() { - mutex_lock_and_auto_release critical_section(this->mutex); - this->lazy_init_done = false; - this->algorithms.clear(); - } - - // Checks whether the init has already be done for the array, otherwise will invoke init_fn - size_t lazy_init(ErlNifEnv *env, const bool fips_enabled, const init_algorithms_fn init_fn) { - size_t result = 0; - if (this->lazy_init_done) { - return this->algorithms.size(); - } - - mutex_lock_and_auto_release critical_section(this->mutex); - - this->algorithms.clear(); - init_fn(env, fips_enabled); - result = this->algorithms.size(); - this->lazy_init_done = true; - - return result; - } - - ERL_NIF_TERM to_list(ErlNifEnv *env, const bool fips_forbidden) const { - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (const auto &algo: this->algorithms) { - // Any of the forbidden flags is not set, then something is available - if (algo.is_forbidden_in_fips() == fips_forbidden) { - hd = enif_make_list_cell(env, algo.get_atom(), hd); - } - } - return hd; - } -}; - -struct pkey_availability_t { - const char *str_v3; /* the algorithm name as in OpenSSL 3.x */ - unsigned flags; /* combination of PKEY_AVAIL_FLAGS */ - ERL_NIF_TERM atom; /* added to results when the user is querying */ - - bool is_forbidden_in_fips() const { -#ifdef FIPS_SUPPORT - return (this->flags == FIPS_FORBIDDEN_PKEY_ALL || this->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE(); -#else - return false; -#endif - } - ERL_NIF_TERM get_atom() const { return this->atom; } -}; - -static algorithm_collection_t pkey_collection("crypto.pkey_collection"); - -struct curve_availability_t { - const char *str_v3; // the algorithm name as in OpenSSL 3.x - unsigned flags; // combination of CURVE_AVAIL_FLAGS - ERL_NIF_TERM atom; // as returned to the library user on a query - - bool is_forbidden_in_fips() const { -#ifdef FIPS_SUPPORT - return this->flags != 0 && FIPS_MODE(); -#else - return false; -#endif - } - ERL_NIF_TERM get_atom() const { return this->atom; } -}; - -static algorithm_collection_t curve_collection("crypto.curve_collection"); - -extern "C" bool create_algorithm_mutexes() { return pkey_collection.create_mutex() && curve_collection.create_mutex(); } - -extern "C" void free_algorithm_mutexes(void) { - pkey_collection.destroy_mutex(); - curve_collection.destroy_mutex(); -} - -extern "C" void algorithms_reset_cache() { - pkey_collection.reset(); - curve_collection.reset(); -} - -// -// Implementation of Pubkey Algorithm storage API -// - -extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled, - const init_algorithms_fn delayed_init_fn) { - return pkey_collection.lazy_init(env, fips_enabled, delayed_init_fn); -} - -extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { - return pkey_collection.to_list(env, fips_enabled); -} - -// Ensure atoms are not created repeatedly -static ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom = 0) { - if (!atom) { - enif_make_existing_atom(env, atom_name, &atom, ERL_NIF_UTF8); - if (!atom) { - atom = enif_make_atom(env, atom_name); - } - } - return atom; -} - -extern "C" void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavailable, - ERL_NIF_TERM atom) { - const pkey_availability_t algo = { - .str_v3 = str_v3, - .flags = unavailable, - .atom = create_or_existing_atom(env, str_v3, atom), - }; - pkey_collection.algorithms.push_back(algo); -} - -// -// Implementation of Curve Algorithm storage API -// - -extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled, - const init_algorithms_fn delayed_init_fn) { - return curve_collection.lazy_init(env, fips_enabled, delayed_init_fn); -} - -extern "C" void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavail_flags) { - const curve_availability_t curve = { - .str_v3 = str_v3, .flags = unavail_flags, .atom = create_or_existing_atom(env, str_v3)}; - curve_collection.algorithms.push_back(curve); -} - -extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { - return curve_collection.to_list(env, fips_enabled); -} diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 4ed69f81d0e6..bfe5d74d657f 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -30,12 +30,11 @@ #include "aead.h" #include "aes.h" #include "algorithms.h" +#include "algorithms_digest.h" #include "api_ng.h" #include "bn.h" #include "cipher.h" -#include "mac.h" #include "dh.h" -#include "digest.h" #include "dss.h" #include "ec.h" #include "ecdh.h" @@ -47,6 +46,7 @@ #include "hash_equals.h" #include "hmac.h" #include "info.h" +#include "mac.h" #include "math.h" #include "pbkdf2_hmac.h" #include "pkey.h" @@ -54,7 +54,7 @@ #include "rsa.h" #include "srp.h" -#include "algorithms_store.h" +#include "algorithms_collection.h" /* NIF interface declarations */ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c deleted file mode 100644 index 9c2ef0dc02d8..000000000000 --- a/lib/crypto/c_src/digest.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * %CopyrightBegin% - * - * SPDX-License-Identifier: Apache-2.0 - * - * Copyright Ericsson AB 2010-2025. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#include "digest.h" - -static struct digest_type_t digest_types[] = -{ - {"md4", "MD4", 0, FIPS_FORBIDDEN_DIGEST, -#ifdef HAVE_MD4 - {&EVP_md4,NULL} -#else - {NULL,NULL} -#endif - }, - - {"md5", "MD5", 0, FIPS_FORBIDDEN_DIGEST, -#ifdef HAVE_MD5 - {&EVP_md5,NULL} -#else - {NULL,NULL} -#endif - }, - - {"ripemd160", "RIPEMD160", 0, FIPS_FORBIDDEN_DIGEST, -#ifdef HAVE_RIPEMD160 - {&EVP_ripemd160,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha", "SHA1", 0, PBKDF2_ELIGIBLE_DIGEST, - {&EVP_sha1,NULL} - }, - - {"sha224", "SHA2-224", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA224 - {&EVP_sha224,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha256", "SHA2-256", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA256 - {&EVP_sha256,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha384", "SHA2-384", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA384 - {&EVP_sha384,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha512", "SHA2-512", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA512 - {&EVP_sha512,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha512_224", "SHA2-512/224", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA512_224 - {&EVP_sha512_224,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha512_256", "SHA2-512/256", 0, PBKDF2_ELIGIBLE_DIGEST, -#ifdef HAVE_SHA512_256 - {&EVP_sha512_256,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha3_224", "SHA3-224", 0, 0, -#ifdef HAVE_SHA3_224 - {&EVP_sha3_224,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha3_256", "SHA3-256", 0, 0, -#ifdef HAVE_SHA3_256 - {&EVP_sha3_256,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha3_384", "SHA3-384", 0, 0, -#ifdef HAVE_SHA3_384 - {&EVP_sha3_384,NULL} -#else - {NULL,NULL} -#endif - }, - - {"sha3_512", "SHA3-512", 0, 0, -#ifdef HAVE_SHA3_512 - {&EVP_sha3_512,NULL} -#else - {NULL,NULL} -#endif - }, - - {"shake128", "SHAKE-128", 0, 0, -#ifdef HAVE_SHAKE128 - {&EVP_shake128, NULL}, - 16, /* xof_default_length */ -#else - {NULL,NULL} -#endif - }, - - {"shake256", "SHAKE-256", 0, 0, -#ifdef HAVE_SHAKE256 - {&EVP_shake256, NULL}, - 32, /* xof_default_length */ -#else - {NULL,NULL} -#endif - }, - - {"sm3", "SM3", 0, 0, -#ifdef HAVE_SM3 - {&EVP_sm3, NULL} -#else - {NULL,NULL} -#endif - }, - - {"blake2b", "BLAKE2b512", 0, 0, -#ifdef HAVE_BLAKE2 - {&EVP_blake2b512,NULL} -#else - {NULL,NULL} -#endif - }, - - {"blake2s", "BLAKE2s256", 0, 0, -#ifdef HAVE_BLAKE2 - {&EVP_blake2s256,NULL} -#else - {NULL,NULL} -#endif - }, - - /*==== End of list ==== */ - {NULL, NULL, 0, 0, {NULL,NULL}} -}; - -#ifdef HAS_3_0_API -#ifdef FIPS_SUPPORT -/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ -static int is_valid_in_fips(const EVP_MD* md) -{ - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - int usable = 0; - - if (md) { - /* Try to initialize the digest algorithm for use, this will check the dependencies */ - if (EVP_DigestInit_ex(ctx, md, NULL) == 1) { - usable = 1; - } - } - - EVP_MD_CTX_free(ctx); - return usable; -} -#endif /* FIPS_SUPPORT */ -#endif /* HAS_3_0_API */ - -static void update_digest_type_fips_flags(struct digest_type_t* p) -{ -#ifdef FIPS_SUPPORT - EVP_MD *fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); - /* Deeper check for validity in FIPS, also checks for NULL */ - if (is_valid_in_fips(fetched_md)) { - p->flags &= ~FIPS_FORBIDDEN_DIGEST; - p->md.p = fetched_md; - } else { - p->flags |= FIPS_FORBIDDEN_DIGEST; - EVP_MD_free(fetched_md); /* NULL is allowed */ - } -#else - p->md.p = EVP_MD_fetch(NULL, p->str_v3, ""); -#endif /* FIPS_SUPPORT and >=3.0.0 */ -} - -static void update_digest_type_availability(struct digest_type_t* p) -{ -#ifdef HAS_3_0_API - if (p->str_v3) { - update_digest_type_fips_flags(p); - } -#else - if (p->md.funcp) { - p->md.p = p->md.funcp(); - } -#endif -} - -void init_digest_types(ErlNifEnv* env) -{ - struct digest_type_t* p = digest_types; - for (/* p = digest_types */; p->str; p++) { - update_digest_type_availability(p); - p->atom = enif_make_atom(env, p->str); - } - - p->atom = atom_false; /* end marker */ -} - -struct digest_type_t* get_digest_type(ERL_NIF_TERM type) -{ - struct digest_type_t* p = NULL; - for (p = digest_types; p->atom != atom_false; p++) { - if (type == p->atom) { - return p; - } - } - - return NULL; -} - - -/* If FIPS mode enabled: Filters away disallowed digest types */ -ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, const bool fips_forbidden) -{ - struct digest_type_t* p = digest_types; - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (/* p = digest_types */; p->atom & (p->atom != atom_false); p++) { - if (p->md.p && !IS_DIGEST_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { - hd = enif_make_list_cell(env, p->atom, hd); - } - } - - return hd; -} diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h deleted file mode 100644 index 9d469a0c88d9..000000000000 --- a/lib/crypto/c_src/digest.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * %CopyrightBegin% - * - * SPDX-License-Identifier: Apache-2.0 - * - * Copyright Ericsson AB 2010-2025. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#ifndef E_DIGEST_H__ -#define E_DIGEST_H__ 1 - -#include "common.h" - -struct digest_type_t { - const char* str; /* before init, NULL for end-of-table */ - const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ - ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - unsigned flags; /* combination of DIGEST_TYPE_FLAGS */ - struct { - const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ - const EVP_MD* p; /* after init, NULL if notsup */ - } md; - unsigned int xof_default_length; /* 0 or default digest length for XOF digests */ -}; - -/* masks in the `flags` field of digest_type_t */ -enum DIGEST_TYPE_FLAGS { - FIPS_FORBIDDEN_DIGEST = 1, /* no support in FIPS for digest */ - PBKDF2_ELIGIBLE_DIGEST = 2 -}; - -#ifdef FIPS_SUPPORT -# define IS_DIGEST_FORBIDDEN_IN_FIPS(p) (((p)->flags & FIPS_FORBIDDEN_DIGEST) && FIPS_MODE()) -#else -# define IS_DIGEST_FORBIDDEN_IN_FIPS(P) false -#endif - -void init_digest_types(ErlNifEnv* env); -struct digest_type_t* get_digest_type(ERL_NIF_TERM type); - -ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, bool fips_forbidden); - -#endif /* E_DIGEST_H__ */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index f6c7beda8322..fc46c9b300f7 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -21,8 +21,8 @@ */ #include "fips.h" -#include "digest.h" -#include "algorithms_store.h" +#include "algorithms_collection.h" +#include "algorithms_digest.h" ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index c7c3f5eda7ce..d0e02bfa5da4 100644 --- a/lib/crypto/c_src/hash.c +++ b/lib/crypto/c_src/hash.c @@ -21,7 +21,7 @@ */ #include "hash.h" -#include "digest.h" +#include "algorithms_digest.h" #include "info.h" #ifdef HAVE_MD5 @@ -73,7 +73,7 @@ int init_hash_ctx(ErlNifEnv* env, ErlNifBinary* rt_buf) { ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type) */ - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; const EVP_MD *md; ERL_NIF_TERM keys[3] = { atom_type, atom_size, atom_block_size }; ERL_NIF_TERM values[3]; @@ -100,7 +100,7 @@ ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Data) */ - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; const EVP_MD *md; ErlNifBinary data; ERL_NIF_TERM ret; @@ -167,7 +167,7 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type) */ - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; struct evp_md_ctx *ctx = NULL; ERL_NIF_TERM ret; @@ -277,7 +277,7 @@ ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type) */ typedef int (*init_fun)(unsigned char*); - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; ERL_NIF_TERM ctx; size_t ctx_size = 0; init_fun ctx_init = 0; @@ -363,7 +363,7 @@ ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ErlNifBinary ctx, data; const ERL_NIF_TERM *tuple; int arity; - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; unsigned char *ctx_buff; size_t ctx_size = 0; update_fun ctx_update = 0; @@ -460,7 +460,7 @@ ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifBinary ctx; const ERL_NIF_TERM *tuple; int arity; - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; const EVP_MD *md; void *new_ctx = NULL; size_t ctx_size = 0; diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c index 1200cd4ad7a4..199e7e7db639 100644 --- a/lib/crypto/c_src/hmac.c +++ b/lib/crypto/c_src/hmac.c @@ -31,7 +31,7 @@ ****************************************************************/ #include "hmac.h" -#include "digest.h" +#include "algorithms_digest.h" #include "info.h" #if !defined(HAS_EVP_PKEY_CTX) || DISABLE_EVP_HMAC @@ -80,7 +80,7 @@ static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (hmac, Type, Key) */ - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; ErlNifBinary key; ERL_NIF_TERM ret; struct hmac_context *obj = NULL; diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 3334e90e38af..04af8bb7c3a1 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -20,12 +20,12 @@ * %CopyrightEnd% */ -#include "common.h" +#include "mac.h" +#include "algorithms_digest.h" #include "cipher.h" -#include "digest.h" #include "cmac.h" +#include "common.h" #include "hmac.h" -#include "mac.h" #include "info.h" /*************************** @@ -324,7 +324,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ********/ case HMAC_mac: { - struct digest_type_t *digp; + struct digest_availability_t *digp; if ((digp = get_digest_type(argv[1])) == NULL) { @@ -676,7 +676,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ********/ case HMAC_mac: { - struct digest_type_t *digp; + struct digest_availability_t *digp; if ((digp = get_digest_type(argv[1])) == NULL) { diff --git a/lib/crypto/c_src/pbkdf2_hmac.c b/lib/crypto/c_src/pbkdf2_hmac.c index 6d450cb4fc15..1b3ba5e1bd5b 100644 --- a/lib/crypto/c_src/pbkdf2_hmac.c +++ b/lib/crypto/c_src/pbkdf2_hmac.c @@ -20,9 +20,9 @@ * %CopyrightEnd% */ -#include "common.h" #include "pbkdf2_hmac.h" -#include "digest.h" +#include "algorithms_digest.h" +#include "common.h" #ifdef HAS_PKCS5_PBKDF2_HMAC static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, @@ -30,7 +30,7 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, { ErlNifBinary pass, salt, out; ErlNifUInt64 iter, keylen; - struct digest_type_t* digp = NULL; + struct digest_availability_t* digp = NULL; if ((digp = get_digest_type(argv[0])) == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 3831f8e26210..9832947da749 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -21,8 +21,8 @@ */ #include "pkey.h" +#include "algorithms_digest.h" #include "bn.h" -#include "digest.h" #include "dss.h" #include "ec.h" #include "eddsa.h" @@ -161,7 +161,7 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, const EVP_MD **md, ERL_NIF_TERM *err_return) { - struct digest_type_t *digp = NULL; + struct digest_availability_t *digp = NULL; *md = NULL; if (type == atom_none) { From 72a5b56e21ca55407d5cc46edeebfb38003a6bfb Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Sun, 9 Nov 2025 02:45:39 +0100 Subject: [PATCH 09/15] Digest collection looks good --- lib/crypto/c_src/algorithms.c | 74 +------------- lib/crypto/c_src/algorithms_collection.h | 10 ++ lib/crypto/c_src/algorithms_digest.cpp | 119 +++++++++++------------ lib/crypto/c_src/algorithms_digest.h | 35 +++---- 4 files changed, 89 insertions(+), 149 deletions(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index f0aa981cf947..cd637d57dba4 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -21,7 +21,8 @@ */ #include "algorithms.h" -#include "algorithms_collection.h" +#include "algorithms_pubkey.h" +#include "algorithms_digest.h" #include "cipher.h" #include "common.h" #include "mac.h" @@ -29,13 +30,6 @@ #include #include "algorithms_digest.h" -// #ifdef HAS_3_0_API -// #else -// static size_t algo_hash_cnt, algo_hash_fips_cnt; -// static ERL_NIF_TERM algo_hash[17]; /* increase when extending the list */ -// void init_hash_types(ErlNifEnv* env); -// #endif - struct kem_availability_t { const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ unsigned flags; /* combination of KEM_AVAIL_FLAGS */ @@ -85,70 +79,10 @@ ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return digest_types_as_list(env, false); } -#ifdef HAS_3_0_API -#else -void init_hash_types(ErlNifEnv* env) { - // Validated algorithms first - algo_hash_cnt = 0; - algo_hash[algo_hash_cnt++] = atom_sha; -#ifdef HAVE_SHA224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha224"); -#endif -#ifdef HAVE_SHA256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha256"); -#endif -#ifdef HAVE_SHA384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha384"); -#endif -#ifdef HAVE_SHA512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha512"); -#endif -#ifdef HAVE_SHA3_224 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_224"); -#endif -#ifdef HAVE_SHA3_256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_256"); -#endif -#ifdef HAVE_SHA3_384 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_384"); -#endif -#ifdef HAVE_SHA3_512 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sha3_512"); -#endif -#ifdef HAVE_SHAKE128 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "shake128"); -#endif -#ifdef HAVE_SHAKE256 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "shake256"); -#endif -#ifdef HAVE_SM3 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "sm3"); -#endif -#ifdef HAVE_BLAKE2 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2b"); - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "blake2s"); -#endif - - // Non-validated algorithms follow - algo_hash_fips_cnt = algo_hash_cnt; -#ifdef HAVE_MD4 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md4"); -#endif -#ifdef HAVE_MD5 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "md5"); -#endif -#ifdef HAVE_RIPEMD160 - algo_hash[algo_hash_cnt++] = enif_make_atom(env, "ripemd160"); -#endif - - ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(algo_hash[0])); -} -#endif - ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - pubkey_algorithms_lazy_init(env, FIPS_MODE(), &pubkey_algorithms_delayed_init); - /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false */ + pubkey_algorithms_lazy_init(env, FIPS_MODE()); + // Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false return pubkey_algorithms_as_list(env, false); } diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h index 2e7d131a21b4..14675914d0cf 100644 --- a/lib/crypto/c_src/algorithms_collection.h +++ b/lib/crypto/c_src/algorithms_collection.h @@ -83,6 +83,16 @@ struct algorithm_collection_t { ~algorithm_collection_t() { destroy_mutex(); } + // Const pointer to start of algorithms + auto cbegin() const { return this->algorithms.cbegin(); } + // Const pointer to one after last of the algorithms + auto cend() const { return this->algorithms.cend(); } + + // Mutable pointer to start of algorithms + auto begin() { return this->algorithms.begin(); } + // Mutable pointer to one after last of the algorithms + auto end() { return this->algorithms.end(); } + bool create_mutex() { this->mutex = enif_mutex_create(const_cast(debug_name)); return this->mutex != nullptr; diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index a4caac93bac0..5bd6e28c60dd 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -25,67 +25,65 @@ static digest_probe_t digest_probes[] = { #ifdef HAVE_MD4 - {.str = "md4", .str_v3 = "MD4", .constructor_fn = &EVP_md4}, + {.str = "md4", .str_v3 = "MD4", .v1_ctor = &EVP_md4}, #endif #ifdef HAVE_MD5 - {.str = "md5", .str_v3 = "MD5", .constructor_fn = &EVP_md5}, + {.str = "md5", .str_v3 = "MD5", .v1_ctor = &EVP_md5}, #endif #ifdef HAVE_RIPEMD160 - {.str = "ripemd160", .str_v3 = "RIPEMD160", .constructor_fn = &EVP_ripemd160}, + {.str = "ripemd160", .str_v3 = "RIPEMD160", .v1_ctor = &EVP_ripemd160}, #endif - {.str = "sha", .str_v3 = "SHA1", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha1}, + {.str = "sha", .str_v3 = "SHA1", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha1}, #ifdef HAVE_SHA224 - {.str = "sha224", .str_v3 = "SHA2-224", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha224}, + {.str = "sha224", .str_v3 = "SHA2-224", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha224}, #endif #ifdef HAVE_SHA256 - {.str = "sha256", .str_v3 = "SHA2-256", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha256}, + {.str = "sha256", .str_v3 = "SHA2-256", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha256}, #endif #ifdef HAVE_SHA384 - {.str = "sha384", .str_v3 = "SHA2-384", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha384}, + {.str = "sha384", .str_v3 = "SHA2-384", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha384}, #endif #ifdef HAVE_SHA512 - {.str = "sha512", .str_v3 = "SHA2-512", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .constructor_fn = &EVP_sha512}, + {.str = "sha512", .str_v3 = "SHA2-512", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha512}, #endif #ifdef HAVE_SHA512_224 {.str = "sha512_224", .str_v3 = "SHA2-512/224", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, - .constructor_fn = &EVP_sha512_224}, + .v1_ctor = &EVP_sha512_224}, #endif #ifdef HAVE_SHA512_256 {.str = "sha512_256", .str_v3 = "SHA2-512/256", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, - .constructor_fn = &EVP_sha512_256}, + .v1_ctor = &EVP_sha512_256}, #endif #ifdef HAVE_SHA3_224 - {.str = "sha3_224", .str_v3 = "SHA3-224", .constructor_fn = &EVP_sha3_224}, + {.str = "sha3_224", .str_v3 = "SHA3-224", .v1_ctor = &EVP_sha3_224}, #endif #ifdef HAVE_SHA3_256 - {.str = "sha3_256", .str_v3 = "SHA3-256", .constructor_fn = &EVP_sha3_256}, + {.str = "sha3_256", .str_v3 = "SHA3-256", .v1_ctor = &EVP_sha3_256}, #endif #ifdef HAVE_SHA3_384 - {.str = "sha3_384", .str_v3 = "SHA3-384", .constructor_fn = &EVP_sha3_384}, + {.str = "sha3_384", .str_v3 = "SHA3-384", .v1_ctor = &EVP_sha3_384}, #endif #ifdef HAVE_SHA3_512 - {.str = "sha3_512", .str_v3 = "SHA3-512", .constructor_fn = &EVP_sha3_512}, + {.str = "sha3_512", .str_v3 = "SHA3-512", .v1_ctor = &EVP_sha3_512}, #endif #ifdef HAVE_SHAKE128 - {.str = "shake128", .str_v3 = "SHAKE-128", .constructor_fn = &EVP_shake128, .xof_default_length = 6}, + {.str = "shake128", .str_v3 = "SHAKE-128", .v1_ctor = &EVP_shake128, .xof_default_length = 6}, #endif #ifdef HAVE_SHAKE256 - {.str = "shake256", .str_v3 = "SHAKE-256", .constructor_fn = &EVP_shake256, .xof_default_length = 32}, + {.str = "shake256", .str_v3 = "SHAKE-256", .v1_ctor = &EVP_shake256, .xof_default_length = 32}, #endif #ifdef HAVE_SM3 - {.str = "sm3", .str_v3 = "SM3", .constructor_fn = &EVP_sm3}, + {.str = "sm3", .str_v3 = "SM3", .v1_ctor = &EVP_sm3}, #endif #ifdef HAVE_BLAKE2 - {.str = "blake2b", .str_v3 = "BLAKE2b512", .constructor_fn = &EVP_blake2b512}, + {.str = "blake2b", .str_v3 = "BLAKE2b512", .v1_ctor = &EVP_blake2b512}, #endif #ifdef HAVE_BLAKE2 - {.str = "blake2s", - .str_v3 = "BLAKE2s256", - .constructor_fn = &EVP_blake2s256}, + {.str = "blake2s", .str_v3 = "BLAKE2s256", .v1_ctor = &EVP_blake2s256}, #endif }; @@ -94,15 +92,14 @@ digest_collection_t digest_collection("crypto.digest.digest_collection", digest_ ERL_NIF_TERM digest_availability_t::get_atom() const { return this->init->atom; } -#ifdef HAS_3_0_API -#ifdef FIPS_SUPPORT -/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ -static int is_valid_in_fips(const EVP_MD *md) { +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) +// Initialize an algorithm to check that all its dependencies are valid in FIPS +bool digest_availability_t::is_valid_in_fips(const EVP_MD *md) const { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); int usable = 0; if (md) { - /* Try to initialize the digest algorithm for use, this will check the dependencies */ + // Try to initialize the digest algorithm for use, this will check the dependencies if (EVP_DigestInit_ex(ctx, md, NULL) == 1) { usable = 1; } @@ -111,53 +108,51 @@ static int is_valid_in_fips(const EVP_MD *md) { EVP_MD_CTX_free(ctx); return usable; } -#endif /* FIPS_SUPPORT */ -#endif /* HAS_3_0_API */ +#endif // FIPS_SUPPORT && HAS_3_0_API + +void digest_availability_t::create_md_resource(bool fips_mode) { +#ifdef HAS_3_0_API + EVP_MD *fetched_md = EVP_MD_fetch(NULL, this->init->str_v3, NULL); -static void update_digest_type_fips_flags(struct digest_availability_t *p) { -#ifdef FIPS_SUPPORT - EVP_MD *fetched_md = EVP_MD_fetch(NULL, p->str_v3, "fips=yes"); - /* Deeper check for validity in FIPS, also checks for NULL */ - if (is_valid_in_fips(fetched_md)) { - p->flags &= ~FIPS_FORBIDDEN_DIGEST; - p->md.p = fetched_md; + // Record failed algorithm instantiation for FIPS enabled & OpenSSL API 3.0 only + if (fips_mode && !is_usable_algorithm(fetched_md)) { + flags |= FIPS_FORBIDDEN_DIGEST; + EVP_MD_free(fetched_md); // NULL is allowed } else { - p->flags |= FIPS_FORBIDDEN_DIGEST; - EVP_MD_free(fetched_md); /* NULL is allowed */ + this->flags &= ~FIPS_FORBIDDEN_DIGEST; + this->md = fetched_md; } #else - p->md = EVP_MD_fetch(NULL, p->str_v3, ""); -#endif /* FIPS_SUPPORT and >=3.0.0 */ + // construct from the old API, each probe has a constructor function + this->md = this->init->v1_ctor(); +#endif // HAS_3_0_API } -static void update_digest_type_availability(struct digest_availability_t *p) { -#ifdef HAS_3_0_API - if (p->str_v3) { - update_digest_type_fips_flags(p); - } -#else - if (p->md.funcp) { - p->md.p = p->md.funcp(); +void digest_probe_t::probe(ErlNifEnv *env, const bool fips_mode, std::vector &output) { + digest_availability_t algo = {.init = this, .flags = this->flags_hint, .xof_default_length = this->xof_default_length}; + algo.create_md_resource(fips_mode); + if (algo.md) { + output.push_back(algo); } -#endif } -void digest_types_delayed_init(ErlNifEnv *env) { - for (struct digest_probe_t *p = digest_types; p->str; p++) { - update_digest_type_availability(p); - p->atom = enif_make_atom(env, p->init->str); +extern "C" digest_availability_t *get_digest_type(ERL_NIF_TERM type) { + for (auto &p: digest_collection) { + if (type == p.get_atom()) { + return &p; + } } - - p->atom = atom_false; /* end marker */ + return nullptr; // sorry } -struct digest_availability_t *get_digest_type(ERL_NIF_TERM type) { - struct digest_availability_t *p = NULL; - for (p = digest_types; p->atom != atom_false; p++) { - if (type == p->atom) { - return p; - } +digest_availability_t::~digest_availability_t() { + if (this->md) { +#if defined(HAS_3_0_API) + EVP_MD_free(this->md); +#else + // Old API creates it as const and deletes as mutable, hence the const_cast + EVP_MD_meth_free(const_cast(this->md)); +#endif // HAS_3_0_API + this->md = nullptr; } - - return NULL; } diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index 722a8c27b17a..a5c7272c5458 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -44,7 +44,6 @@ ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); #ifdef __cplusplus - // Describes a digest method added by the init function, and checked for compatibility // with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and // created again. @@ -52,18 +51,13 @@ struct digest_availability_t { // The definition used to create this record const struct digest_probe_t *init; // combination of DIGEST_TYPE_FLAGS from init + detected in runtime - size_t flags; - // after init will contain the algorithm pointer, NULL if not supported - EVP_MD *md; + size_t flags = 0; + // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. + const EVP_MD *md = nullptr; // 0 or default digest length for XOF digests - size_t xof_default_length; + size_t xof_default_length = 0; - digest_availability_t() = default; - ~digest_availability_t() { - if (md) { - EVP_MD_meth_free(md); - } - } + ~digest_availability_t(); bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT @@ -74,6 +68,13 @@ struct digest_availability_t { } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; + + // Fetches the algorithm and sets the initial flags + void create_md_resource(bool fips_mode); + // Attempts to instantiate the algorithm (forbidden algorithms will fail) +#if defined(HAS_3_0_API) + bool is_usable_algorithm(const EVP_MD *md) const; +#endif // HAS_3_0_API }; // masks in the `flags` field of digest_type_t @@ -86,9 +87,6 @@ enum DIGEST_TYPE_FLAGS { // Describes data required by digest availability probing algorithm (separate branches for // OpenSSL API < 3.0, 3.0, and for FIPS enabled/disabled). struct digest_probe_t { - // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' - void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); - // the algorithm name as in OpenSSL < 3, also atom used by Erlang API const char *str = nullptr; // the algorithm name as in OpenSSL 3.x @@ -96,10 +94,13 @@ struct digest_probe_t { // This will be updated to created atomfound exi ERL_NIF_TERM atom = 0; // initial DIGEST_TYPE_FLAGS value for digest_type_t::flags - const unsigned flags_hint = 0; - // OpenSSL API to create this digest - const EVP_MD *(*constructor_fn)() = nullptr; + const size_t flags_hint = 0; + // OpenSSL 1.0 API to create resource for this digest algorithm (not used in 3.0 API) + const EVP_MD *(*v1_ctor)() = nullptr; size_t xof_default_length = 0; + + // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); }; using digest_collection_t = algorithm_collection_t; From 91b4f05860bb2ba7b530d242b98828853832ec71 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Sun, 9 Nov 2025 16:09:55 +0100 Subject: [PATCH 10/15] WIP curve algorithms; auto resource helpers --- lib/crypto/c_src/algorithms.c | 465 +------------------- lib/crypto/c_src/algorithms_curve.cpp | 468 ++++++++++++++++++++- lib/crypto/c_src/algorithms_curve.h | 14 +- lib/crypto/c_src/algorithms_digest.cpp | 8 +- lib/crypto/c_src/algorithms_digest.h | 2 +- lib/crypto/c_src/algorithms_pubkey.cpp | 18 +- lib/crypto/c_src/algorithms_pubkey.h | 40 +- lib/crypto/c_src/crypto_openssl_resource.h | 59 +++ 8 files changed, 564 insertions(+), 510 deletions(-) create mode 100644 lib/crypto/c_src/crypto_openssl_resource.h diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index cd637d57dba4..3b3dae08e834 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -181,472 +181,9 @@ ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return mac_types_as_list(env, false); } - -/*================================================================ - Curves -*/ - -void curve_algorithms_delayed_init(ErlNifEnv* env, bool fips); -#if defined(HAVE_EC) -static bool is_curve_valid_by_nid(int nid); -#endif - -static void add_curve_if_supported(const int nid, ErlNifEnv* env, bool fips_enabled, const char* str_v3) { - /* Some curves can be pre-checked by their NID */ - if (nid && !is_curve_valid_by_nid(nid)) { /* passing NID=0 will skip this check */ - return; - } - -#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - { - EVP_PKEY_CTX *pctx = NULL; - EVP_PKEY *pkey = NULL; - unsigned unavail_flags = 0; - - /* This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added */ - if (fips_enabled) { - OSSL_PARAM params[2]; - - pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "fips=yes"); - if (!pctx) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; /* EC keygen context not available */ - } - if (EVP_PKEY_keygen_init(pctx) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; - } - params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char *)str_v3, 0); - params[1] = OSSL_PARAM_construct_end(); - if (EVP_PKEY_CTX_set_params(pctx, params) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; - } - if (EVP_PKEY_generate(pctx, &pkey) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - } - } -add_anyway: - curve_add_algorithm(env, str_v3, unavail_flags); - EVP_PKEY_free(pkey); /* NULL is allowed */ - EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ - } -#else - curve_add_algorithm(env, str_v3, 0); -#endif -} - -void curve_algorithms_delayed_init(ErlNifEnv* env, const bool fips) { -#if defined(HAVE_EC) -#ifdef NID_secp160k1 - add_curve_if_supported(NID_secp160k1, env, fips, "secp160k1"); -#else -#endif -#ifdef NID_secp160r1 - add_curve_if_supported(NID_secp160r1, env, fips, "secp160r1"); -#else -#endif -#ifdef NID_secp160r2 - add_curve_if_supported(NID_secp160r2, env, fips, "secp160r2"); -#else -#endif -#ifdef NID_secp192k1 - add_curve_if_supported(NID_secp192k1, env, fips, "secp192k1"); -#else -#endif -#ifdef NID_secp224k1 - add_curve_if_supported(NID_secp224k1, env, fips, "secp224k1"); -#else -#endif -#ifdef NID_secp224r1 - add_curve_if_supported(NID_secp224r1, env, fips, "secp224r1"); -#else -#endif -#ifdef NID_secp256k1 - add_curve_if_supported(NID_secp256k1, env, fips, "secp256k1"); -#else -#endif -#ifdef NID_secp384r1 - add_curve_if_supported(NID_secp384r1, env, fips, "secp384r1"); -#else -#endif -#ifdef NID_secp521r1 - add_curve_if_supported(NID_secp521r1, env, fips, "secp521r1"); -#else -#endif -#ifdef NID_X9_62_prime192v1 - add_curve_if_supported(NID_X9_62_prime192v1, env, fips, "secp192r1"); - add_curve_if_supported(NID_X9_62_prime192v1, env, fips, "prime192v1"); -#else -#endif -#ifdef NID_X9_62_prime192v2 - add_curve_if_supported(NID_X9_62_prime192v2, env, fips, "prime192v2"); -#else -#endif -#ifdef NID_X9_62_prime192v3 - add_curve_if_supported(NID_X9_62_prime192v3, env, fips, "prime192v3"); -#else -#endif -#ifdef NID_X9_62_prime239v1 - add_curve_if_supported(NID_X9_62_prime239v1, env, fips, "prime239v1"); -#else -#endif -#ifdef NID_X9_62_prime239v2 - add_curve_if_supported(NID_X9_62_prime239v2, env, fips, "prime239v2"); -#else -#endif -#ifdef NID_X9_62_prime239v3 - add_curve_if_supported(NID_X9_62_prime239v3, env, fips, "prime239v3"); -#else -#endif -#ifdef NID_X9_62_prime256v1 - add_curve_if_supported(NID_X9_62_prime256v1, env, fips, "secp256r1"); - add_curve_if_supported(NID_X9_62_prime256v1, env, fips, "prime256v1"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls7 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls7, env, fips, "wtls7"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls9 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls9, env, fips, "wtls9"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls12 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls12, env, fips, "wtls12"); -#else -#endif -#ifdef NID_brainpoolP160r1 - add_curve_if_supported(NID_brainpoolP160r1, env, fips, "brainpoolP160r1"); -#else -#endif -#ifdef NID_brainpoolP160t1 - add_curve_if_supported(NID_brainpoolP160t1, env, fips, "brainpoolP160t1"); -#else -#endif -#ifdef NID_brainpoolP192r1 - add_curve_if_supported(NID_brainpoolP192r1, env, fips, "brainpoolP192r1"); -#else -#endif -#ifdef NID_brainpoolP192t1 - add_curve_if_supported(NID_brainpoolP192t1, env, fips, "brainpoolP192t1"); -#else -#endif -#ifdef NID_brainpoolP224r1 - add_curve_if_supported(NID_brainpoolP224r1, env, fips, "brainpoolP224r1"); -#else -#endif -#ifdef NID_brainpoolP224t1 - add_curve_if_supported(NID_brainpoolP224t1, env, fips, "brainpoolP224t1"); -#else -#endif -#ifdef NID_brainpoolP256r1 - add_curve_if_supported(NID_brainpoolP256r1, env, fips, "brainpoolP256r1"); -#else -#endif -#ifdef NID_brainpoolP256t1 - add_curve_if_supported(NID_brainpoolP256t1, env, fips, "brainpoolP256t1"); -#else -#endif -#ifdef NID_brainpoolP320r1 - add_curve_if_supported(NID_brainpoolP320r1, env, fips, "brainpoolP320r1"); -#else -#endif -#ifdef NID_brainpoolP320t1 - add_curve_if_supported(NID_brainpoolP320t1, env, fips, "brainpoolP320t1"); -#else -#endif -#ifdef NID_brainpoolP384r1 - add_curve_if_supported(NID_brainpoolP384r1, env, fips, "brainpoolP384r1"); -#else -#endif -#ifdef NID_brainpoolP384t1 - add_curve_if_supported(NID_brainpoolP384t1, env, fips, "brainpoolP384t1"); -#else -#endif -#ifdef NID_brainpoolP512r1 - add_curve_if_supported(NID_brainpoolP512r1, env, fips, "brainpoolP512r1"); -#else -#endif -#ifdef NID_brainpoolP512t1 - add_curve_if_supported(NID_brainpoolP512t1, env, fips, "brainpoolP512t1"); -#else -#endif - //#if !defined(OPENSSL_NO_EC2M) -#ifdef NID_sect163k1 - add_curve_if_supported(NID_sect163k1, env, fips, "sect163k1"); -#else -#endif -#ifdef NID_sect163r1 - add_curve_if_supported(NID_sect163r1, env, fips, "sect163r1"); -#else -#endif -#ifdef NID_sect163r2 - add_curve_if_supported(NID_sect163r2, env, fips, "sect163r2"); -#else -#endif -#ifdef NID_sect193r1 - add_curve_if_supported(NID_sect193r1, env, fips, "sect193r1"); -#else -#endif -#ifdef NID_sect193r2 - add_curve_if_supported(NID_sect193r2, env, fips, "sect193r2"); -#else -#endif -#ifdef NID_sect233k1 - add_curve_if_supported(NID_sect233k1, env, fips, "sect233k1"); -#else -#endif -#ifdef NID_sect233r1 - add_curve_if_supported(NID_sect233r1, env, fips, "sect233r1"); -#else -#endif -#ifdef NID_sect239k1 - add_curve_if_supported(NID_sect239k1, env, fips, "sect239k1"); -#else -#endif -#ifdef NID_sect283k1 - add_curve_if_supported(NID_sect283k1, env, fips, "sect283k1"); -#else -#endif -#ifdef NID_sect283r1 - add_curve_if_supported(NID_sect283r1, env, fips, "sect283r1"); -#else -#endif -#ifdef NID_sect409k1 - add_curve_if_supported(NID_sect409k1, env, fips, "sect409k1"); -#else -#endif -#ifdef NID_sect409r1 - add_curve_if_supported(NID_sect409r1, env, fips, "sect409r1"); -#else -#endif -#ifdef NID_sect571k1 - add_curve_if_supported(NID_sect571k1, env, fips, "sect571k1"); -#else -#endif -#ifdef NID_sect571r1 - add_curve_if_supported(NID_sect571r1, env, fips, "sect571r1"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v1 - add_curve_if_supported(NID_X9_62_c2pnb163v1, env, fips, "c2pnb163v1"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v2 - add_curve_if_supported(NID_X9_62_c2pnb163v2, env, fips, "c2pnb163v2"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v3 - add_curve_if_supported(NID_X9_62_c2pnb163v3, env, fips, "c2pnb163v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb176v1 - add_curve_if_supported(NID_X9_62_c2pnb176v1, env, fips, "c2pnb176v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v1 - add_curve_if_supported(NID_X9_62_c2tnb191v1, env, fips, "c2tnb191v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v2 - add_curve_if_supported(NID_X9_62_c2tnb191v2, env, fips, "c2tnb191v2"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v3 - add_curve_if_supported(NID_X9_62_c2tnb191v3, env, fips, "c2tnb191v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb208w1 - add_curve_if_supported(NID_X9_62_c2pnb208w1, env, fips, "c2pnb208w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v1 - add_curve_if_supported(NID_X9_62_c2tnb239v1, env, fips, "c2tnb239v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v2 - add_curve_if_supported(NID_X9_62_c2tnb239v2, env, fips, "c2tnb239v2"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v3 - add_curve_if_supported(NID_X9_62_c2tnb239v3, env, fips, "c2tnb239v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb272w1 - add_curve_if_supported(NID_X9_62_c2pnb272w1, env, fips, "c2pnb272w1"); -#else -#endif -#ifdef NID_X9_62_c2pnb304w1 - add_curve_if_supported(NID_X9_62_c2pnb304w1, env, fips, "c2pnb304w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb359v1 - add_curve_if_supported(NID_X9_62_c2tnb359v1, env, fips, "c2tnb359v1"); -#else -#endif -#ifdef NID_X9_62_c2pnb368w1 - add_curve_if_supported(NID_X9_62_c2pnb368w1, env, fips, "c2pnb368w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb431r1 - add_curve_if_supported(NID_X9_62_c2tnb431r1, env, fips, "c2tnb431r1"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls3 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls3, env, fips, "wtls3"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls5 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls5, env, fips, "wtls5"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls10 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls10, env, fips, "wtls10"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls11 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls11, env, fips, "wtls11"); -#else -#endif - // Non-validated algorithms follow -#ifdef NID_secp112r1 - add_curve_if_supported(NID_secp112r1, env, fips, "secp112r1"); -#else -#endif -#ifdef NID_secp112r2 - add_curve_if_supported(NID_secp112r2, env, fips, "secp112r2"); -#else -#endif -#ifdef NID_secp128r1 - add_curve_if_supported(NID_secp128r1, env, fips, "secp128r1"); -#else -#endif -#ifdef NID_secp128r2 - add_curve_if_supported(NID_secp128r2, env, fips, "secp128r2"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls6 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls6, env, fips, "wtls6"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls8 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls8, env, fips, "wtls8"); -#else -#endif - //#if !defined(OPENSSL_NO_EC2M) -#ifdef NID_sect113r1 - add_curve_if_supported(NID_sect113r1, env, fips, "sect113r1"); -#else -#endif -#ifdef NID_sect113r2 - add_curve_if_supported(NID_sect113r2, env, fips, "sect113r2"); -#else -#endif -#ifdef NID_sect131r1 - add_curve_if_supported(NID_sect131r1, env, fips, "sect131r1"); -#else -#endif -#ifdef NID_sect131r2 - add_curve_if_supported(NID_sect131r2, env, fips, "sect131r2"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls1 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls1, env, fips, "wtls1"); -#else -#endif -#ifdef NID_wap_wsg_idm_ecid_wtls4 - add_curve_if_supported(NID_wap_wsg_idm_ecid_wtls4, env, fips, "wtls4"); -#else -#endif -#ifdef NID_ipsec3 - add_curve_if_supported(NID_ipsec3, env, fips, "ipsec3"); -#else -#endif -#ifdef NID_ipsec4 - add_curve_if_supported(NID_ipsec4, env, fips, "ipsec4"); -#else -#endif - - if (!fips) { -#ifdef HAVE_ED25519 - add_curve_if_supported(0, env, fips, "ed25519"); -#endif -#ifdef HAVE_ED448 - add_curve_if_supported(0, env, fips, "ed448"); -#endif -#ifdef HAVE_X25519 - add_curve_if_supported(0, env, fips, "x25519"); -#endif -#ifdef HAVE_X448 - add_curve_if_supported(0, env, fips, "x448"); -#endif - } -#endif -} - -#if defined(HAVE_EC) - -/* Check if the curve in nid is supported by the - current cryptolib and current FIPS state. -*/ - -bool is_curve_valid_by_nid(const int nid) { - bool ret = false; - -#if defined(HAVE_DH) -# if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_DH) - EVP_PKEY_CTX *pctx = NULL, *kctx = NULL; - EVP_PKEY *pkey = NULL, *params = NULL; - - if (NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) - goto out; - - if (1 != EVP_PKEY_paramgen_init(pctx)) - goto out; - - if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid)) - goto out; - - if (!EVP_PKEY_paramgen(pctx, ¶ms)) - goto out; - - if (NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) - goto out; - - if(1 != EVP_PKEY_keygen_init(kctx)) - goto out; - if (1 != EVP_PKEY_keygen(kctx, &pkey)) - goto out; - ret = true; - out: - if (pkey) EVP_PKEY_free(pkey); - if (kctx) EVP_PKEY_CTX_free(kctx); - if (params) EVP_PKEY_free(params); - if (pctx) EVP_PKEY_CTX_free(pctx); - -# else - EC_KEY *key; - - if (NULL == (key = EC_KEY_new_by_curve_name(nid))) - goto out; - - if(1 != EC_KEY_generate_key(key)) - goto out; - - ret = true; - out: - if (key) EC_KEY_free(key); -# endif -#endif /* HAVE_DH etc */ - - return ret; -} -#endif /* HAVE_EC */ - ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - curve_algorithms_lazy_init(env, FIPS_MODE(), &curve_algorithms_delayed_init); + curve_algorithms_lazy_init(env, FIPS_MODE()); return curve_algorithms_as_list(env, false); } diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp index 97d23dda76a6..297546ccf3ee 100644 --- a/lib/crypto/c_src/algorithms_curve.cpp +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -21,10 +21,359 @@ */ #include "algorithms_curve.h" +#include "crypto_openssl_resource.h" -curve_probe_t curve_probes[] = {}; +curve_probe_t curve_probes[] = { +#if defined(HAVE_EC) +#ifdef NID_secp160k1 + {.nid = NID_secp160k1, .sn = "secp160k1"}, +#else +#endif +#ifdef NID_secp160r1 + {.nid = NID_secp160r1, .sn = "secp160r1"}, +#else +#endif +#ifdef NID_secp160r2 + {.nid = NID_secp160r2, .sn = "secp160r2"}, +#else +#endif +#ifdef NID_secp192k1 + {.nid = NID_secp192k1, .sn = "secp192k1"}, +#else +#endif +#ifdef NID_secp224k1 + {.nid = NID_secp224k1, .sn = "secp224k1"}, +#else +#endif +#ifdef NID_secp224r1 + {.nid = NID_secp224r1, .sn = "secp224r1"}, +#else +#endif +#ifdef NID_secp256k1 + {.nid = NID_secp256k1, .sn = "secp256k1"}, +#else +#endif +#ifdef NID_secp384r1 + {.nid = NID_secp384r1, .sn = "secp384r1"}, +#else +#endif +#ifdef NID_secp521r1 + {.nid = NID_secp521r1, .sn = "secp521r1"}, +#else +#endif +#ifdef NID_X9_62_prime192v1 + {.nid = NID_X9_62_prime192v1, .sn = "secp192r1"}, + {.nid = NID_X9_62_prime192v1, .sn = "prime192v1"}, +#else +#endif +#ifdef NID_X9_62_prime192v2 + {.nid = NID_X9_62_prime192v2, .sn = "prime192v2"}, +#else +#endif +#ifdef NID_X9_62_prime192v3 + {.nid = NID_X9_62_prime192v3, .sn = "prime192v3"}, +#else +#endif +#ifdef NID_X9_62_prime239v1 + {.nid = NID_X9_62_prime239v1, .sn = "prime239v1"}, +#else +#endif +#ifdef NID_X9_62_prime239v2 + {.nid = NID_X9_62_prime239v2, .sn = "prime239v2"}, +#else +#endif +#ifdef NID_X9_62_prime239v3 + {.nid = NID_X9_62_prime239v3, .sn = "prime239v3"}, +#else +#endif +#ifdef NID_X9_62_prime256v1 + {.nid = NID_X9_62_prime256v1, .sn = "secp256r1"}, + {.nid = NID_X9_62_prime256v1, .sn = "prime256v1"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls7 + {.nid = NID_wap_wsg_idm_ecid_wtls7, .sn = "wtls7"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls9 + {.nid = NID_wap_wsg_idm_ecid_wtls9, .sn = "wtls9"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls12 + {.nid = NID_wap_wsg_idm_ecid_wtls12, .sn = "wtls12"}, +#else +#endif +#ifdef NID_brainpoolP160r1 + {.nid = NID_brainpoolP160r1, .sn = "brainpoolP160r1"}, +#else +#endif +#ifdef NID_brainpoolP160t1 + {.nid = NID_brainpoolP160t1, .sn = "brainpoolP160t1"}, +#else +#endif +#ifdef NID_brainpoolP192r1 + {.nid = NID_brainpoolP192r1, .sn = "brainpoolP192r1"}, +#else +#endif +#ifdef NID_brainpoolP192t1 + {.nid = NID_brainpoolP192t1, .sn = "brainpoolP192t1"}, +#else +#endif +#ifdef NID_brainpoolP224r1 + {.nid = NID_brainpoolP224r1, .sn = "brainpoolP224r1"}, +#else +#endif +#ifdef NID_brainpoolP224t1 + {.nid = NID_brainpoolP224t1, .sn = "brainpoolP224t1"}, +#else +#endif +#ifdef NID_brainpoolP256r1 + {.nid = NID_brainpoolP256r1, .sn = "brainpoolP256r1"}, +#else +#endif +#ifdef NID_brainpoolP256t1 + {.nid = NID_brainpoolP256t1, .sn = "brainpoolP256t1"}, +#else +#endif +#ifdef NID_brainpoolP320r1 + {.nid = NID_brainpoolP320r1, .sn = "brainpoolP320r1"}, +#else +#endif +#ifdef NID_brainpoolP320t1 + {.nid = NID_brainpoolP320t1, .sn = "brainpoolP320t1"}, +#else +#endif +#ifdef NID_brainpoolP384r1 + {.nid = NID_brainpoolP384r1, .sn = "brainpoolP384r1"}, +#else +#endif +#ifdef NID_brainpoolP384t1 + {.nid = NID_brainpoolP384t1, .sn = "brainpoolP384t1"}, +#else +#endif +#ifdef NID_brainpoolP512r1 + {.nid = NID_brainpoolP512r1, .sn = "brainpoolP512r1"}, +#else +#endif +#ifdef NID_brainpoolP512t1 + {.nid = NID_brainpoolP512t1, .sn = "brainpoolP512t1"}, +#else +#endif +// #if !defined(OPENSSL_NO_EC2M) +#ifdef NID_sect163k1 + {.nid = NID_sect163k1, .sn = "sect163k1"}, +#else +#endif +#ifdef NID_sect163r1 + {.nid = NID_sect163r1, .sn = "sect163r1"}, +#else +#endif +#ifdef NID_sect163r2 + {.nid = NID_sect163r2, .sn = "sect163r2"}, +#else +#endif +#ifdef NID_sect193r1 + {.nid = NID_sect193r1, .sn = "sect193r1"}, +#else +#endif +#ifdef NID_sect193r2 + {.nid = NID_sect193r2, .sn = "sect193r2"}, +#else +#endif +#ifdef NID_sect233k1 + {.nid = NID_sect233k1, .sn = "sect233k1"}, +#else +#endif +#ifdef NID_sect233r1 + {.nid = NID_sect233r1, .sn = "sect233r1"}, +#else +#endif +#ifdef NID_sect239k1 + {.nid = NID_sect239k1, .sn = "sect239k1"}, +#else +#endif +#ifdef NID_sect283k1 + {.nid = NID_sect283k1, .sn = "sect283k1"}, +#else +#endif +#ifdef NID_sect283r1 + {.nid = NID_sect283r1, .sn = "sect283r1"}, +#else +#endif +#ifdef NID_sect409k1 + {.nid = NID_sect409k1, .sn = "sect409k1"}, +#else +#endif +#ifdef NID_sect409r1 + {.nid = NID_sect409r1, .sn = "sect409r1"}, +#else +#endif +#ifdef NID_sect571k1 + {.nid = NID_sect571k1, .sn = "sect571k1"}, +#else +#endif +#ifdef NID_sect571r1 + {.nid = NID_sect571r1, .sn = "sect571r1"}, +#else +#endif +#ifdef NID_X9_62_c2pnb163v1 + {.nid = NID_X9_62_c2pnb163v1, .sn = "c2pnb163v1"}, +#else +#endif +#ifdef NID_X9_62_c2pnb163v2 + {.nid = NID_X9_62_c2pnb163v2, .sn = "c2pnb163v2"}, +#else +#endif +#ifdef NID_X9_62_c2pnb163v3 + {.nid = NID_X9_62_c2pnb163v3, .sn = "c2pnb163v3"}, +#else +#endif +#ifdef NID_X9_62_c2pnb176v1 + {.nid = NID_X9_62_c2pnb176v1, .sn = "c2pnb176v1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb191v1 + {.nid = NID_X9_62_c2tnb191v1, .sn = "c2tnb191v1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb191v2 + {.nid = NID_X9_62_c2tnb191v2, .sn = "c2tnb191v2"}, +#else +#endif +#ifdef NID_X9_62_c2tnb191v3 + {.nid = NID_X9_62_c2tnb191v3, .sn = "c2tnb191v3"}, +#else +#endif +#ifdef NID_X9_62_c2pnb208w1 + {.nid = NID_X9_62_c2pnb208w1, .sn = "c2pnb208w1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb239v1 + {.nid = NID_X9_62_c2tnb239v1, .sn = "c2tnb239v1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb239v2 + {.nid = NID_X9_62_c2tnb239v2, .sn = "c2tnb239v2"}, +#else +#endif +#ifdef NID_X9_62_c2tnb239v3 + {.nid = NID_X9_62_c2tnb239v3, .sn = "c2tnb239v3"}, +#else +#endif +#ifdef NID_X9_62_c2pnb272w1 + {.nid = NID_X9_62_c2pnb272w1, .sn = "c2pnb272w1"}, +#else +#endif +#ifdef NID_X9_62_c2pnb304w1 + {.nid = NID_X9_62_c2pnb304w1, .sn = "c2pnb304w1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb359v1 + {.nid = NID_X9_62_c2tnb359v1, .sn = "c2tnb359v1"}, +#else +#endif +#ifdef NID_X9_62_c2pnb368w1 + {.nid = NID_X9_62_c2pnb368w1, .sn = "c2pnb368w1"}, +#else +#endif +#ifdef NID_X9_62_c2tnb431r1 + {.nid = NID_X9_62_c2tnb431r1, .sn = "c2tnb431r1"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls3 + {.nid = NID_wap_wsg_idm_ecid_wtls3, .sn = "wtls3"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls5 + {.nid = NID_wap_wsg_idm_ecid_wtls5, .sn = "wtls5"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls10 + {.nid = NID_wap_wsg_idm_ecid_wtls10, .sn = "wtls10"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls11 + {.nid = NID_wap_wsg_idm_ecid_wtls11, .sn = "wtls11"}, +#else +#endif +// Non-validated algorithms follow +#ifdef NID_secp112r1 + {.nid = NID_secp112r1, .sn = "secp112r1"}, +#else +#endif +#ifdef NID_secp112r2 + {.nid = NID_secp112r2, .sn = "secp112r2"}, +#else +#endif +#ifdef NID_secp128r1 + {.nid = NID_secp128r1, .sn = "secp128r1"}, +#else +#endif +#ifdef NID_secp128r2 + {.nid = NID_secp128r2, .sn = "secp128r2"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls6 + {.nid = NID_wap_wsg_idm_ecid_wtls6, .sn = "wtls6"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls8 + {.nid = NID_wap_wsg_idm_ecid_wtls8, .sn = "wtls8"}, +#else +#endif +// #if !defined(OPENSSL_NO_EC2M) +#ifdef NID_sect113r1 + {.nid = NID_sect113r1, .sn = "sect113r1"}, +#else +#endif +#ifdef NID_sect113r2 + {.nid = NID_sect113r2, .sn = "sect113r2"}, +#else +#endif +#ifdef NID_sect131r1 + {.nid = NID_sect131r1, .sn = "sect131r1"}, +#else +#endif +#ifdef NID_sect131r2 + {.nid = NID_sect131r2, .sn = "sect131r2"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls1 + {.nid = NID_wap_wsg_idm_ecid_wtls1, .sn = "wtls1"}, +#else +#endif +#ifdef NID_wap_wsg_idm_ecid_wtls4 + {.nid = NID_wap_wsg_idm_ecid_wtls4, .sn = "wtls4"}, +#else +#endif +#ifdef NID_ipsec3 + {.nid = NID_ipsec3, .sn = "ipsec3"}, +#else +#endif +#ifdef NID_ipsec4 + {.nid = NID_ipsec4, .sn = "ipsec4"}, +#else +#endif -curve_collection_t curve_collection("crypto.curve_collection", curve_probes, sizeof(curve_probes) / sizeof(curve_probes[0])); +#if !defined(FIPS_SUPPORT) +#ifdef HAVE_ED25519 + {.nid = 0, .sn = "ed25519"}, +#endif +#ifdef HAVE_ED448 + {.nid = 0, .sn = "ed448"}, +#endif +#ifdef HAVE_X25519 + {.nid = 0, .sn = "x25519"}, +#endif +#ifdef HAVE_X448 + {.nid = 0, .sn = "x448"}, +#endif +#endif // FIPS_SUPPORT +#endif // HAVE_EC +}; + +curve_collection_t curve_collection("crypto.curve_collection", curve_probes, + sizeof(curve_probes) / sizeof(curve_probes[0])); // // Implementation of Curve Algorithm storage API @@ -34,12 +383,115 @@ extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv *env, const bool fips_ena return curve_collection.lazy_init(env, fips_enabled); } -extern "C" void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, const unsigned unavail_flags) { - const curve_availability_t curve = { - .str_v3 = str_v3, .flags = unavail_flags, .atom = create_or_existing_atom(env, str_v3)}; - curve_collection.algorithms.push_back(curve); -} - extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { return curve_collection.to_list(env, fips_enabled); } + +/*================================================================ + Curves +*/ + +/* Check if the curve in nid is supported by the + current cryptolib and current FIPS state. +*/ + +bool curve_probe_t::is_curve_valid_by_nid() { +#ifdef HAVE_EC +#if defined(HAVE_DH) +#if defined(HAS_EVP_PKEY_CTX) && (!DISABLE_EVP_DH) + auto_evp_pkey_t pkey(nullptr); + auto_evp_pkey_t params(nullptr); + + auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); + if (!pctx) + return false; + if (1 != EVP_PKEY_paramgen_init(pctx.pointer)) + return false; + if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx.pointer, nid)) + return false; + if (!EVP_PKEY_paramgen(pctx.pointer, ¶ms.pointer)) + return false; + + auto_evp_pkey_ctx_t kctx(EVP_PKEY_CTX_new(params.pointer, nullptr)); + if (!kctx) + return false; + + if (1 != EVP_PKEY_keygen_init(kctx.pointer)) + return false; + if (1 != EVP_PKEY_keygen(kctx.pointer, &pkey.pointer)) + return false; + + return true; +#else + EC_KEY *key; + + if (NULL == (key = EC_KEY_new_by_curve_name(nid))) + goto out; + + if (1 != EC_KEY_generate_key(key)) + goto out; + + ret = true; +out: + if (key) + EC_KEY_free(key); +#endif +#endif /* HAVE_DH etc */ + + return ret; +#else + return false; +#endif // HAVE_EC +} + +void curve_probe_t::probe(ErlNifEnv *env, bool fips_mode, std::vector &output) { + this->atom = create_or_existing_atom(env, this->sn, this->atom); + curve_availability_t algo = {.init = this}; + + // Some curves can be pre-checked by their NID. Passing NID=0 will skip this check + if (nid && !this->is_curve_valid_by_nid()) { + return; // invalid/unsupported curves are skipped + } + +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + { + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + unsigned unavail_flags = 0; + + /* This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added */ + if (fips_enabled) { + OSSL_PARAM params[2]; + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "fips=yes"); + if (!pctx) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; /* EC keygen context not available */ + } + if (EVP_PKEY_keygen_init(pctx) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; + } + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char *) str_v3, 0); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx, params) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + goto add_anyway; + } + if (EVP_PKEY_generate(pctx, &pkey) <= 0) { + unavail_flags |= FIPS_CURVE_INIT_FAILED; + } + } + add_anyway: + curve_add_algorithm(env, str_v3, unavail_flags); + EVP_PKEY_free(pkey); /* NULL is allowed */ + EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ + } +#else + curve_add_algorithm(env, str_v3, 0); +#endif + + output.push_back(std::move(algo)); +} + +ERL_NIF_TERM curve_availability_t::get_atom() const { return this->init->atom; } diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h index 54cd8865f070..f659620b6bb3 100644 --- a/lib/crypto/c_src/algorithms_curve.h +++ b/lib/crypto/c_src/algorithms_curve.h @@ -33,7 +33,6 @@ extern "C" { // Curve Algorithms storage API // size_t curve_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled); -void curve_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavail_flags); ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); #ifdef __cplusplus @@ -41,10 +40,10 @@ ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); #endif #ifdef __cplusplus +struct curve_probe_t; struct curve_availability_t { - const char *str_v3; // the algorithm name as in OpenSSL 3.x + const curve_probe_t *init; // the probe which created this record, contains name, atom, etc. unsigned flags; // combination of CURVE_AVAIL_FLAGS - ERL_NIF_TERM atom; // as returned to the library user on a query bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT @@ -54,7 +53,7 @@ struct curve_availability_t { #endif } // Return the atom which goes to the Erlang caller - ERL_NIF_TERM get_atom() const { return this->atom; } + ERL_NIF_TERM get_atom() const; }; enum CURVE_AVAIL_FLAGS { @@ -62,8 +61,15 @@ enum CURVE_AVAIL_FLAGS { }; struct curve_probe_t { + size_t nid = 0; // NID_xxxx value of OpenSSL + const char *sn = nullptr; // serves as Erlang atom name, also equal to SN_xxxxx macro of OpenSSL + ERL_NIF_TERM atom = 0; // Atom for this->sn is cached here + // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); + +private: + bool is_curve_valid_by_nid(); // used by the probe() to check this->nid }; // Forward declaration, find diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index 5bd6e28c60dd..52353b010436 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -128,11 +128,12 @@ void digest_availability_t::create_md_resource(bool fips_mode) { #endif // HAS_3_0_API } -void digest_probe_t::probe(ErlNifEnv *env, const bool fips_mode, std::vector &output) { +void digest_probe_t::probe(ErlNifEnv *, const bool fips_mode, std::vector &output) { digest_availability_t algo = {.init = this, .flags = this->flags_hint, .xof_default_length = this->xof_default_length}; + // Unavailable are skipped. Available are added. Forbidden are added, but flagged with FIPS_FORBIDDEN_DIGEST. algo.create_md_resource(fips_mode); if (algo.md) { - output.push_back(algo); + output.push_back(std::move(algo)); } } @@ -148,9 +149,8 @@ extern "C" digest_availability_t *get_digest_type(ERL_NIF_TERM type) { digest_availability_t::~digest_availability_t() { if (this->md) { #if defined(HAS_3_0_API) - EVP_MD_free(this->md); + EVP_MD_free(const_cast(this->md)); #else - // Old API creates it as const and deletes as mutable, hence the const_cast EVP_MD_meth_free(const_cast(this->md)); #endif // HAS_3_0_API this->md = nullptr; diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index a5c7272c5458..ed0604522463 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -49,7 +49,7 @@ ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); // created again. struct digest_availability_t { // The definition used to create this record - const struct digest_probe_t *init; + const struct digest_probe_t *init = nullptr; // combination of DIGEST_TYPE_FLAGS from init + detected in runtime size_t flags = 0; // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp index 507a0a69ecf0..02dd6d615847 100644 --- a/lib/crypto/c_src/algorithms_pubkey.cpp +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -72,9 +72,9 @@ ERL_NIF_TERM pubkey_availability_t::get_atom() const { return this->init->atom; #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) // FIPS is supported AND enabled here -void pubkey_probe_t::probe_algorithm_against_fips(size_t flags) const { - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); - /* failed: algorithm not available, do not add */ +void pubkey_availability_t::probe_algorithm_against_fips() { + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); + // failed: algorithm not available, do not add if (ctx) { if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ flags |= FIPS_FORBIDDEN_PKEY_KEYGEN; @@ -113,12 +113,12 @@ void pubkey_probe_t::probe_algorithm_against_fips(size_t flags) const { // for FIPS we will attempt to initialize the pubkey context to verify whether the // algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. void pubkey_probe_t::probe(ErlNifEnv *env, bool fips_enabled, std::vector &output) { - size_t flags = 0; - if (!fips_enabled) { // No check for non-fips, all algorithms are welcome - return output.push_back({.init = this, .flags = flags}); - } + this->atom = create_or_existing_atom(env, this->str, this->atom); + pubkey_availability_t algo = {.init = this}; #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - flags = this->probe_algorithm_against_fips(flags); + if (fips_enabled) { // attempt to instantiate the algorithm and set availability flags + algo->probe_algorithm_against_fips(); + } #endif // FIPS_SUPPORT && HAS_3_0_API - output.push_back({.init = this, .flags = flags}); + return output.push_back(algo); } diff --git a/lib/crypto/c_src/algorithms_pubkey.h b/lib/crypto/c_src/algorithms_pubkey.h index 119f23c306f3..d87716ecaf72 100644 --- a/lib/crypto/c_src/algorithms_pubkey.h +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -40,48 +40,48 @@ void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailab } #endif -enum PKEY_AVAIL_FLAGS { - FIPS_PKEY_NOT_AVAIL = 1, - FIPS_FORBIDDEN_PKEY_KEYGEN = 2, /* not available by name */ - FIPS_FORBIDDEN_PKEY_SIGN = 4, /* not available for signing */ - FIPS_FORBIDDEN_PKEY_VERIFY = 8, /* not available for verification */ - FIPS_FORBIDDEN_PKEY_ENCRYPT = 16, /* not available for encryption */ - FIPS_FORBIDDEN_PKEY_DERIVE = 32, /* not available for key derivation */ - FIPS_FORBIDDEN_PKEY_ALL = FIPS_FORBIDDEN_PKEY_KEYGEN | FIPS_FORBIDDEN_PKEY_SIGN | FIPS_FORBIDDEN_PKEY_VERIFY | - FIPS_FORBIDDEN_PKEY_ENCRYPT | FIPS_FORBIDDEN_PKEY_DERIVE -}; - #ifdef __cplusplus - struct pubkey_probe_t; struct pubkey_availability_t { const pubkey_probe_t *init; // the pubkey_probe_t used to create this record - size_t flags; // combination of PKEY_AVAIL_FLAGS + + struct { + bool not_available : 1; + bool fips_forbidden_keygen : 1; + bool fips_forbidden_sign : 1; + bool fips_forbidden_verify : 1; + bool fips_forbidden_encrypt : 1; + bool fips_forbidden_derive : 1; + } flags; bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT - return (this->flags == FIPS_FORBIDDEN_PKEY_ALL || this->flags == FIPS_PKEY_NOT_AVAIL) && FIPS_MODE(); + // Forbidden in FIPS if all operations are forbidden, or if algorithm is not available at all + return ((this->flags.fips_forbidden_keygen && this->flags.fips_forbidden_sign && + this->flags.fips_forbidden_verify && this->flags.fips_forbidden_encrypt && + this->flags.fips_forbidden_derive) || + this->flags.not_available) && + FIPS_MODE(); #else return false; #endif } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; + // Update this->flags for FIPS compatibility +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + void probe_algorithm_against_fips(); // fips is supported AND enabled here +#endif // FIPS_SUPPORT && HAS_3_0_API }; struct pubkey_probe_t { const char *str = nullptr; - const char *str_v3 = nullptr; // keep this nullptr to avoid duplcaticatiuonon, .str will be used instead + const char *str_v3 = nullptr; // if this is nullptr, .str will be used instead ERL_NIF_TERM atom = 0; // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' void probe(ErlNifEnv *env, bool fips_enabled, std::vector &output); - -private: -#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - void probe_algorithm_against_fips(size_t flags) const; // fips is supported AND enabled here -#endif // FIPS_SUPPORT && HAS_3_0_API }; using pubkey_collection_t = algorithm_collection_t; diff --git a/lib/crypto/c_src/crypto_openssl_resource.h b/lib/crypto/c_src/crypto_openssl_resource.h new file mode 100644 index 000000000000..d1758058bd9b --- /dev/null +++ b/lib/crypto/c_src/crypto_openssl_resource.h @@ -0,0 +1,59 @@ +/* +* %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +#pragma once + +#include "common.h" + +#ifdef __cplusplus + +// A generic struct holding a pointer, constructable with a pointer or as null, and auto-destructable. +// The bool operator allows using the struct in if() condit +// When inheriting: implement the destructor with the call to OpenSSL free function for corresponding resource +template +struct auto_openssl_resource_t { + T *pointer = nullptr; + explicit auto_openssl_resource_t(T *p) : pointer(p) {} + explicit operator bool() const { return this->pointer != nullptr; } +}; + +struct auto_evp_pkey_t: auto_openssl_resource_t { + explicit auto_evp_pkey_t(EVP_PKEY *p) : auto_openssl_resource_t(p) {} + ~auto_evp_pkey_t() { + EVP_PKEY_free(this->pointer); + } +}; + +struct auto_evp_pkey_ctx_t: auto_openssl_resource_t { + explicit auto_evp_pkey_ctx_t(EVP_PKEY_CTX *c) : auto_openssl_resource_t(c) {} + ~auto_evp_pkey_ctx_t() { + EVP_PKEY_CTX_free(this->pointer); + } +}; + +struct auto_ec_key_t: auto_openssl_resource_t { + explicit auto_ec_key_t(EC_KEY *c) : auto_openssl_resource_t(c) {} + ~auto_ec_key_t() { + EC_KEY_free(this->pointer); + } +}; + +#endif // __cplusplus From 466634f521577e145a4aa2785124de63ee2596f5 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Tue, 11 Nov 2025 22:02:53 +0100 Subject: [PATCH 11/15] Trying with SSL API 3.0 and FIPS --- lib/crypto/c_src/algorithms_collection.h | 6 +- lib/crypto/c_src/algorithms_curve.cpp | 267 ++++++++++----------- lib/crypto/c_src/algorithms_curve.h | 17 +- lib/crypto/c_src/algorithms_digest.cpp | 37 ++- lib/crypto/c_src/algorithms_digest.h | 28 +-- lib/crypto/c_src/algorithms_pubkey.cpp | 87 ++++--- lib/crypto/c_src/algorithms_pubkey.h | 29 ++- lib/crypto/c_src/crypto_openssl_resource.h | 47 ++-- lib/crypto/c_src/fips.c | 9 +- 9 files changed, 260 insertions(+), 267 deletions(-) diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h index 14675914d0cf..2c68744796a2 100644 --- a/lib/crypto/c_src/algorithms_collection.h +++ b/lib/crypto/c_src/algorithms_collection.h @@ -58,8 +58,8 @@ struct mutex_lock_and_auto_release { ~mutex_lock_and_auto_release() { enif_mutex_unlock(mutex); } }; -// Stores array of algorithms for detected algorithms of type AlgorithmT and to -// populate the array, a second type is provided: ProbeT, this is a struct containing +// Stores a static array of algorithms for detected algorithms of type AlgorithmT and to +// populate the array, a second type is provided: ProbeT, this is a type of struct containing // logic to detect algorithm availability and create AlgorithmT. // // The collections for all types of algorithms are statically created before the crypto @@ -78,7 +78,7 @@ struct algorithm_collection_t { const char *debug_name; public: - explicit algorithm_collection_t(const char *debug_name, ProbeT *probes_, const size_t probe_count_) : + explicit algorithm_collection_t(const char* debug_name, ProbeT* probes_, const size_t probe_count_) : lazy_init_done(false), probes(probes_), probe_count(probe_count_), mutex(nullptr), debug_name(debug_name) {} ~algorithm_collection_t() { destroy_mutex(); } diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp index 297546ccf3ee..2de8f77a7195 100644 --- a/lib/crypto/c_src/algorithms_curve.cpp +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -20,370 +20,371 @@ * %CopyrightEnd% */ +#include #include "algorithms_curve.h" #include "crypto_openssl_resource.h" curve_probe_t curve_probes[] = { #if defined(HAVE_EC) #ifdef NID_secp160k1 - {.nid = NID_secp160k1, .sn = "secp160k1"}, + {.nid = NID_secp160k1, .sn = "secp160k1"}, #else #endif #ifdef NID_secp160r1 - {.nid = NID_secp160r1, .sn = "secp160r1"}, + {.nid = NID_secp160r1, .sn = "secp160r1"}, #else #endif #ifdef NID_secp160r2 - {.nid = NID_secp160r2, .sn = "secp160r2"}, + {.nid = NID_secp160r2, .sn = "secp160r2"}, #else #endif #ifdef NID_secp192k1 - {.nid = NID_secp192k1, .sn = "secp192k1"}, + {.nid = NID_secp192k1, .sn = "secp192k1"}, #else #endif #ifdef NID_secp224k1 - {.nid = NID_secp224k1, .sn = "secp224k1"}, + {.nid = NID_secp224k1, .sn = "secp224k1"}, #else #endif #ifdef NID_secp224r1 - {.nid = NID_secp224r1, .sn = "secp224r1"}, + {.nid = NID_secp224r1, .sn = "secp224r1"}, #else #endif #ifdef NID_secp256k1 - {.nid = NID_secp256k1, .sn = "secp256k1"}, + {.nid = NID_secp256k1, .sn = "secp256k1"}, #else #endif #ifdef NID_secp384r1 - {.nid = NID_secp384r1, .sn = "secp384r1"}, + {.nid = NID_secp384r1, .sn = "secp384r1"}, #else #endif #ifdef NID_secp521r1 - {.nid = NID_secp521r1, .sn = "secp521r1"}, + {.nid = NID_secp521r1, .sn = "secp521r1"}, #else #endif #ifdef NID_X9_62_prime192v1 - {.nid = NID_X9_62_prime192v1, .sn = "secp192r1"}, - {.nid = NID_X9_62_prime192v1, .sn = "prime192v1"}, + {.nid = NID_X9_62_prime192v1, .sn = "secp192r1"}, + {.nid = NID_X9_62_prime192v1, .sn = "prime192v1"}, #else #endif #ifdef NID_X9_62_prime192v2 - {.nid = NID_X9_62_prime192v2, .sn = "prime192v2"}, + {.nid = NID_X9_62_prime192v2, .sn = "prime192v2"}, #else #endif #ifdef NID_X9_62_prime192v3 - {.nid = NID_X9_62_prime192v3, .sn = "prime192v3"}, + {.nid = NID_X9_62_prime192v3, .sn = "prime192v3"}, #else #endif #ifdef NID_X9_62_prime239v1 - {.nid = NID_X9_62_prime239v1, .sn = "prime239v1"}, + {.nid = NID_X9_62_prime239v1, .sn = "prime239v1"}, #else #endif #ifdef NID_X9_62_prime239v2 - {.nid = NID_X9_62_prime239v2, .sn = "prime239v2"}, + {.nid = NID_X9_62_prime239v2, .sn = "prime239v2"}, #else #endif #ifdef NID_X9_62_prime239v3 - {.nid = NID_X9_62_prime239v3, .sn = "prime239v3"}, + {.nid = NID_X9_62_prime239v3, .sn = "prime239v3"}, #else #endif #ifdef NID_X9_62_prime256v1 - {.nid = NID_X9_62_prime256v1, .sn = "secp256r1"}, - {.nid = NID_X9_62_prime256v1, .sn = "prime256v1"}, + {.nid = NID_X9_62_prime256v1, .sn = "secp256r1"}, + {.nid = NID_X9_62_prime256v1, .sn = "prime256v1"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls7 - {.nid = NID_wap_wsg_idm_ecid_wtls7, .sn = "wtls7"}, + {.nid = NID_wap_wsg_idm_ecid_wtls7, .sn = "wtls7"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls9 - {.nid = NID_wap_wsg_idm_ecid_wtls9, .sn = "wtls9"}, + {.nid = NID_wap_wsg_idm_ecid_wtls9, .sn = "wtls9"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls12 - {.nid = NID_wap_wsg_idm_ecid_wtls12, .sn = "wtls12"}, + {.nid = NID_wap_wsg_idm_ecid_wtls12, .sn = "wtls12"}, #else #endif #ifdef NID_brainpoolP160r1 - {.nid = NID_brainpoolP160r1, .sn = "brainpoolP160r1"}, + {.nid = NID_brainpoolP160r1, .sn = "brainpoolP160r1"}, #else #endif #ifdef NID_brainpoolP160t1 - {.nid = NID_brainpoolP160t1, .sn = "brainpoolP160t1"}, + {.nid = NID_brainpoolP160t1, .sn = "brainpoolP160t1"}, #else #endif #ifdef NID_brainpoolP192r1 - {.nid = NID_brainpoolP192r1, .sn = "brainpoolP192r1"}, + {.nid = NID_brainpoolP192r1, .sn = "brainpoolP192r1"}, #else #endif #ifdef NID_brainpoolP192t1 - {.nid = NID_brainpoolP192t1, .sn = "brainpoolP192t1"}, + {.nid = NID_brainpoolP192t1, .sn = "brainpoolP192t1"}, #else #endif #ifdef NID_brainpoolP224r1 - {.nid = NID_brainpoolP224r1, .sn = "brainpoolP224r1"}, + {.nid = NID_brainpoolP224r1, .sn = "brainpoolP224r1"}, #else #endif #ifdef NID_brainpoolP224t1 - {.nid = NID_brainpoolP224t1, .sn = "brainpoolP224t1"}, + {.nid = NID_brainpoolP224t1, .sn = "brainpoolP224t1"}, #else #endif #ifdef NID_brainpoolP256r1 - {.nid = NID_brainpoolP256r1, .sn = "brainpoolP256r1"}, + {.nid = NID_brainpoolP256r1, .sn = "brainpoolP256r1"}, #else #endif #ifdef NID_brainpoolP256t1 - {.nid = NID_brainpoolP256t1, .sn = "brainpoolP256t1"}, + {.nid = NID_brainpoolP256t1, .sn = "brainpoolP256t1"}, #else #endif #ifdef NID_brainpoolP320r1 - {.nid = NID_brainpoolP320r1, .sn = "brainpoolP320r1"}, + {.nid = NID_brainpoolP320r1, .sn = "brainpoolP320r1"}, #else #endif #ifdef NID_brainpoolP320t1 - {.nid = NID_brainpoolP320t1, .sn = "brainpoolP320t1"}, + {.nid = NID_brainpoolP320t1, .sn = "brainpoolP320t1"}, #else #endif #ifdef NID_brainpoolP384r1 - {.nid = NID_brainpoolP384r1, .sn = "brainpoolP384r1"}, + {.nid = NID_brainpoolP384r1, .sn = "brainpoolP384r1"}, #else #endif #ifdef NID_brainpoolP384t1 - {.nid = NID_brainpoolP384t1, .sn = "brainpoolP384t1"}, + {.nid = NID_brainpoolP384t1, .sn = "brainpoolP384t1"}, #else #endif #ifdef NID_brainpoolP512r1 - {.nid = NID_brainpoolP512r1, .sn = "brainpoolP512r1"}, + {.nid = NID_brainpoolP512r1, .sn = "brainpoolP512r1"}, #else #endif #ifdef NID_brainpoolP512t1 - {.nid = NID_brainpoolP512t1, .sn = "brainpoolP512t1"}, + {.nid = NID_brainpoolP512t1, .sn = "brainpoolP512t1"}, #else #endif // #if !defined(OPENSSL_NO_EC2M) #ifdef NID_sect163k1 - {.nid = NID_sect163k1, .sn = "sect163k1"}, + {.nid = NID_sect163k1, .sn = "sect163k1"}, #else #endif #ifdef NID_sect163r1 - {.nid = NID_sect163r1, .sn = "sect163r1"}, + {.nid = NID_sect163r1, .sn = "sect163r1"}, #else #endif #ifdef NID_sect163r2 - {.nid = NID_sect163r2, .sn = "sect163r2"}, + {.nid = NID_sect163r2, .sn = "sect163r2"}, #else #endif #ifdef NID_sect193r1 - {.nid = NID_sect193r1, .sn = "sect193r1"}, + {.nid = NID_sect193r1, .sn = "sect193r1"}, #else #endif #ifdef NID_sect193r2 - {.nid = NID_sect193r2, .sn = "sect193r2"}, + {.nid = NID_sect193r2, .sn = "sect193r2"}, #else #endif #ifdef NID_sect233k1 - {.nid = NID_sect233k1, .sn = "sect233k1"}, + {.nid = NID_sect233k1, .sn = "sect233k1"}, #else #endif #ifdef NID_sect233r1 - {.nid = NID_sect233r1, .sn = "sect233r1"}, + {.nid = NID_sect233r1, .sn = "sect233r1"}, #else #endif #ifdef NID_sect239k1 - {.nid = NID_sect239k1, .sn = "sect239k1"}, + {.nid = NID_sect239k1, .sn = "sect239k1"}, #else #endif #ifdef NID_sect283k1 - {.nid = NID_sect283k1, .sn = "sect283k1"}, + {.nid = NID_sect283k1, .sn = "sect283k1"}, #else #endif #ifdef NID_sect283r1 - {.nid = NID_sect283r1, .sn = "sect283r1"}, + {.nid = NID_sect283r1, .sn = "sect283r1"}, #else #endif #ifdef NID_sect409k1 - {.nid = NID_sect409k1, .sn = "sect409k1"}, + {.nid = NID_sect409k1, .sn = "sect409k1"}, #else #endif #ifdef NID_sect409r1 - {.nid = NID_sect409r1, .sn = "sect409r1"}, + {.nid = NID_sect409r1, .sn = "sect409r1"}, #else #endif #ifdef NID_sect571k1 - {.nid = NID_sect571k1, .sn = "sect571k1"}, + {.nid = NID_sect571k1, .sn = "sect571k1"}, #else #endif #ifdef NID_sect571r1 - {.nid = NID_sect571r1, .sn = "sect571r1"}, + {.nid = NID_sect571r1, .sn = "sect571r1"}, #else #endif #ifdef NID_X9_62_c2pnb163v1 - {.nid = NID_X9_62_c2pnb163v1, .sn = "c2pnb163v1"}, + {.nid = NID_X9_62_c2pnb163v1, .sn = "c2pnb163v1"}, #else #endif #ifdef NID_X9_62_c2pnb163v2 - {.nid = NID_X9_62_c2pnb163v2, .sn = "c2pnb163v2"}, + {.nid = NID_X9_62_c2pnb163v2, .sn = "c2pnb163v2"}, #else #endif #ifdef NID_X9_62_c2pnb163v3 - {.nid = NID_X9_62_c2pnb163v3, .sn = "c2pnb163v3"}, + {.nid = NID_X9_62_c2pnb163v3, .sn = "c2pnb163v3"}, #else #endif #ifdef NID_X9_62_c2pnb176v1 - {.nid = NID_X9_62_c2pnb176v1, .sn = "c2pnb176v1"}, + {.nid = NID_X9_62_c2pnb176v1, .sn = "c2pnb176v1"}, #else #endif #ifdef NID_X9_62_c2tnb191v1 - {.nid = NID_X9_62_c2tnb191v1, .sn = "c2tnb191v1"}, + {.nid = NID_X9_62_c2tnb191v1, .sn = "c2tnb191v1"}, #else #endif #ifdef NID_X9_62_c2tnb191v2 - {.nid = NID_X9_62_c2tnb191v2, .sn = "c2tnb191v2"}, + {.nid = NID_X9_62_c2tnb191v2, .sn = "c2tnb191v2"}, #else #endif #ifdef NID_X9_62_c2tnb191v3 - {.nid = NID_X9_62_c2tnb191v3, .sn = "c2tnb191v3"}, + {.nid = NID_X9_62_c2tnb191v3, .sn = "c2tnb191v3"}, #else #endif #ifdef NID_X9_62_c2pnb208w1 - {.nid = NID_X9_62_c2pnb208w1, .sn = "c2pnb208w1"}, + {.nid = NID_X9_62_c2pnb208w1, .sn = "c2pnb208w1"}, #else #endif #ifdef NID_X9_62_c2tnb239v1 - {.nid = NID_X9_62_c2tnb239v1, .sn = "c2tnb239v1"}, + {.nid = NID_X9_62_c2tnb239v1, .sn = "c2tnb239v1"}, #else #endif #ifdef NID_X9_62_c2tnb239v2 - {.nid = NID_X9_62_c2tnb239v2, .sn = "c2tnb239v2"}, + {.nid = NID_X9_62_c2tnb239v2, .sn = "c2tnb239v2"}, #else #endif #ifdef NID_X9_62_c2tnb239v3 - {.nid = NID_X9_62_c2tnb239v3, .sn = "c2tnb239v3"}, + {.nid = NID_X9_62_c2tnb239v3, .sn = "c2tnb239v3"}, #else #endif #ifdef NID_X9_62_c2pnb272w1 - {.nid = NID_X9_62_c2pnb272w1, .sn = "c2pnb272w1"}, + {.nid = NID_X9_62_c2pnb272w1, .sn = "c2pnb272w1"}, #else #endif #ifdef NID_X9_62_c2pnb304w1 - {.nid = NID_X9_62_c2pnb304w1, .sn = "c2pnb304w1"}, + {.nid = NID_X9_62_c2pnb304w1, .sn = "c2pnb304w1"}, #else #endif #ifdef NID_X9_62_c2tnb359v1 - {.nid = NID_X9_62_c2tnb359v1, .sn = "c2tnb359v1"}, + {.nid = NID_X9_62_c2tnb359v1, .sn = "c2tnb359v1"}, #else #endif #ifdef NID_X9_62_c2pnb368w1 - {.nid = NID_X9_62_c2pnb368w1, .sn = "c2pnb368w1"}, + {.nid = NID_X9_62_c2pnb368w1, .sn = "c2pnb368w1"}, #else #endif #ifdef NID_X9_62_c2tnb431r1 - {.nid = NID_X9_62_c2tnb431r1, .sn = "c2tnb431r1"}, + {.nid = NID_X9_62_c2tnb431r1, .sn = "c2tnb431r1"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls3 - {.nid = NID_wap_wsg_idm_ecid_wtls3, .sn = "wtls3"}, + {.nid = NID_wap_wsg_idm_ecid_wtls3, .sn = "wtls3"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls5 - {.nid = NID_wap_wsg_idm_ecid_wtls5, .sn = "wtls5"}, + {.nid = NID_wap_wsg_idm_ecid_wtls5, .sn = "wtls5"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls10 - {.nid = NID_wap_wsg_idm_ecid_wtls10, .sn = "wtls10"}, + {.nid = NID_wap_wsg_idm_ecid_wtls10, .sn = "wtls10"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls11 - {.nid = NID_wap_wsg_idm_ecid_wtls11, .sn = "wtls11"}, + {.nid = NID_wap_wsg_idm_ecid_wtls11, .sn = "wtls11"}, #else #endif // Non-validated algorithms follow #ifdef NID_secp112r1 - {.nid = NID_secp112r1, .sn = "secp112r1"}, + {.nid = NID_secp112r1, .sn = "secp112r1"}, #else #endif #ifdef NID_secp112r2 - {.nid = NID_secp112r2, .sn = "secp112r2"}, + {.nid = NID_secp112r2, .sn = "secp112r2"}, #else #endif #ifdef NID_secp128r1 - {.nid = NID_secp128r1, .sn = "secp128r1"}, + {.nid = NID_secp128r1, .sn = "secp128r1"}, #else #endif #ifdef NID_secp128r2 - {.nid = NID_secp128r2, .sn = "secp128r2"}, + {.nid = NID_secp128r2, .sn = "secp128r2"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls6 - {.nid = NID_wap_wsg_idm_ecid_wtls6, .sn = "wtls6"}, + {.nid = NID_wap_wsg_idm_ecid_wtls6, .sn = "wtls6"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls8 - {.nid = NID_wap_wsg_idm_ecid_wtls8, .sn = "wtls8"}, + {.nid = NID_wap_wsg_idm_ecid_wtls8, .sn = "wtls8"}, #else #endif // #if !defined(OPENSSL_NO_EC2M) #ifdef NID_sect113r1 - {.nid = NID_sect113r1, .sn = "sect113r1"}, + {.nid = NID_sect113r1, .sn = "sect113r1"}, #else #endif #ifdef NID_sect113r2 - {.nid = NID_sect113r2, .sn = "sect113r2"}, + {.nid = NID_sect113r2, .sn = "sect113r2"}, #else #endif #ifdef NID_sect131r1 - {.nid = NID_sect131r1, .sn = "sect131r1"}, + {.nid = NID_sect131r1, .sn = "sect131r1"}, #else #endif #ifdef NID_sect131r2 - {.nid = NID_sect131r2, .sn = "sect131r2"}, + {.nid = NID_sect131r2, .sn = "sect131r2"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls1 - {.nid = NID_wap_wsg_idm_ecid_wtls1, .sn = "wtls1"}, + {.nid = NID_wap_wsg_idm_ecid_wtls1, .sn = "wtls1"}, #else #endif #ifdef NID_wap_wsg_idm_ecid_wtls4 - {.nid = NID_wap_wsg_idm_ecid_wtls4, .sn = "wtls4"}, + {.nid = NID_wap_wsg_idm_ecid_wtls4, .sn = "wtls4"}, #else #endif #ifdef NID_ipsec3 - {.nid = NID_ipsec3, .sn = "ipsec3"}, + {.nid = NID_ipsec3, .sn = "ipsec3"}, #else #endif #ifdef NID_ipsec4 - {.nid = NID_ipsec4, .sn = "ipsec4"}, + {.nid = NID_ipsec4, .sn = "ipsec4"}, #else #endif #if !defined(FIPS_SUPPORT) #ifdef HAVE_ED25519 - {.nid = 0, .sn = "ed25519"}, + {.nid = 0, .sn = "ed25519"}, #endif #ifdef HAVE_ED448 - {.nid = 0, .sn = "ed448"}, + {.nid = 0, .sn = "ed448"}, #endif #ifdef HAVE_X25519 - {.nid = 0, .sn = "x25519"}, + {.nid = 0, .sn = "x25519"}, #endif #ifdef HAVE_X448 - {.nid = 0, .sn = "x448"}, + {.nid = 0, .sn = "x448"}, #endif #endif // FIPS_SUPPORT #endif // HAVE_EC }; curve_collection_t curve_collection("crypto.curve_collection", curve_probes, - sizeof(curve_probes) / sizeof(curve_probes[0])); + std::size(curve_probes)); // // Implementation of Curve Algorithm storage API // -extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled) { +extern "C" size_t curve_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { return curve_collection.lazy_init(env, fips_enabled); } -extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { +extern "C" ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv* env, const bool fips_enabled) { return curve_collection.to_list(env, fips_enabled); } @@ -423,28 +424,23 @@ bool curve_probe_t::is_curve_valid_by_nid() { return true; #else - EC_KEY *key; + auto_ec_key_t key(EC_KEY_new_by_curve_name(nid)); - if (NULL == (key = EC_KEY_new_by_curve_name(nid))) - goto out; - - if (1 != EC_KEY_generate_key(key)) - goto out; - - ret = true; -out: - if (key) - EC_KEY_free(key); + if (!key) + return false; + if (1 != EC_KEY_generate_key(key.pointer)) + return false; + return true; #endif #endif /* HAVE_DH etc */ - return ret; + return false; #else return false; #endif // HAVE_EC } -void curve_probe_t::probe(ErlNifEnv *env, bool fips_mode, std::vector &output) { +void curve_probe_t::probe(ErlNifEnv* env, const bool fips_mode, std::vector& output) { this->atom = create_or_existing_atom(env, this->sn, this->atom); curve_availability_t algo = {.init = this}; @@ -453,45 +449,38 @@ void curve_probe_t::probe(ErlNifEnv *env, bool fips_mode, std::vectorinit->atom; } + +void curve_availability_t::probe_under_fips(bool fips_mode) { #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - { - EVP_PKEY_CTX *pctx = NULL; - EVP_PKEY *pkey = NULL; - unsigned unavail_flags = 0; + // This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added + if (!fips_mode) + return; - /* This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added */ - if (fips_enabled) { - OSSL_PARAM params[2]; + OSSL_PARAM params[2]; - pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "fips=yes"); - if (!pctx) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; /* EC keygen context not available */ - } - if (EVP_PKEY_keygen_init(pctx) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; - } - params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char *) str_v3, 0); - params[1] = OSSL_PARAM_construct_end(); - if (EVP_PKEY_CTX_set_params(pctx, params) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - goto add_anyway; - } - if (EVP_PKEY_generate(pctx, &pkey) <= 0) { - unavail_flags |= FIPS_CURVE_INIT_FAILED; - } - } - add_anyway: - curve_add_algorithm(env, str_v3, unavail_flags); - EVP_PKEY_free(pkey); /* NULL is allowed */ - EVP_PKEY_CTX_free(pctx); /* NULL is allowed */ + auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", "fips=yes")); + if (!pctx) { + this->flags.curve_init_failed = true; + return; /* EC keygen context not available */ + } + if (EVP_PKEY_keygen_init(pctx.pointer) <= 0) { + this->flags.curve_init_failed = true; + return; + } + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, const_cast(this->init->sn), 0); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_CTX_set_params(pctx.pointer, params) <= 0) { + this->flags.curve_init_failed = true; + return; + } + auto_evp_pkey_t pkey(nullptr); + if (EVP_PKEY_generate(pctx.pointer, &pkey.pointer) <= 0) { + this->flags.curve_init_failed = true; } -#else - curve_add_algorithm(env, str_v3, 0); #endif - - output.push_back(std::move(algo)); } - -ERL_NIF_TERM curve_availability_t::get_atom() const { return this->init->atom; } diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h index f659620b6bb3..f1a9bdbd36e3 100644 --- a/lib/crypto/c_src/algorithms_curve.h +++ b/lib/crypto/c_src/algorithms_curve.h @@ -41,23 +41,26 @@ ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); #ifdef __cplusplus struct curve_probe_t; + struct curve_availability_t { const curve_probe_t *init; // the probe which created this record, contains name, atom, etc. - unsigned flags; // combination of CURVE_AVAIL_FLAGS + struct { + bool fips_forbidden: 1; + bool curve_init_failed: 1; // not possible to create with fips=yes + } flags; bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT - return this->flags != 0 && FIPS_MODE(); + // Available if not forbidden with fips=yes, and if curve init did not fail + return (this->flags.fips_forbidden || this->flags.curve_init_failed) && FIPS_MODE(); #else return false; #endif } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; -}; - -enum CURVE_AVAIL_FLAGS { - FIPS_CURVE_INIT_FAILED = 1 // could not find by name or initialize + // Instantiate the algorithm (if FIPS is enabled) and set flags if not available + void probe_under_fips(bool fips_mode); }; struct curve_probe_t { @@ -65,7 +68,7 @@ struct curve_probe_t { const char *sn = nullptr; // serves as Erlang atom name, also equal to SN_xxxxx macro of OpenSSL ERL_NIF_TERM atom = 0; // Atom for this->sn is cached here - // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + // Perform a probe on the algorithm. In case of success, fill the struct and push into the 'output' void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); private: diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index 52353b010436..c95e74be8f33 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -33,30 +33,24 @@ static digest_probe_t digest_probes[] = { #ifdef HAVE_RIPEMD160 {.str = "ripemd160", .str_v3 = "RIPEMD160", .v1_ctor = &EVP_ripemd160}, #endif - {.str = "sha", .str_v3 = "SHA1", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha1}, + {.str = "sha", .str_v3 = "SHA1", .pbkdf2 = true, .v1_ctor = &EVP_sha1}, #ifdef HAVE_SHA224 - {.str = "sha224", .str_v3 = "SHA2-224", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha224}, + {.str = "sha224", .str_v3 = "SHA2-224", .pbkdf2 = true, .v1_ctor = &EVP_sha224}, #endif #ifdef HAVE_SHA256 - {.str = "sha256", .str_v3 = "SHA2-256", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha256}, + {.str = "sha256", .str_v3 = "SHA2-256", .pbkdf2 = true, .v1_ctor = &EVP_sha256}, #endif #ifdef HAVE_SHA384 - {.str = "sha384", .str_v3 = "SHA2-384", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha384}, + {.str = "sha384", .str_v3 = "SHA2-384", .pbkdf2 = true, .v1_ctor = &EVP_sha384}, #endif #ifdef HAVE_SHA512 - {.str = "sha512", .str_v3 = "SHA2-512", .flags_hint = PBKDF2_ELIGIBLE_DIGEST, .v1_ctor = &EVP_sha512}, + {.str = "sha512", .str_v3 = "SHA2-512", .pbkdf2 = true, .v1_ctor = &EVP_sha512}, #endif #ifdef HAVE_SHA512_224 - {.str = "sha512_224", - .str_v3 = "SHA2-512/224", - .flags_hint = PBKDF2_ELIGIBLE_DIGEST, - .v1_ctor = &EVP_sha512_224}, + {.str = "sha512_224", .str_v3 = "SHA2-512/224", .pbkdf2 = true, .v1_ctor = &EVP_sha512_224}, #endif #ifdef HAVE_SHA512_256 - {.str = "sha512_256", - .str_v3 = "SHA2-512/256", - .flags_hint = PBKDF2_ELIGIBLE_DIGEST, - .v1_ctor = &EVP_sha512_256}, + {.str = "sha512_256", .str_v3 = "SHA2-512/256", .pbkdf2 = true, .v1_ctor = &EVP_sha512_256}, #endif #ifdef HAVE_SHA3_224 {.str = "sha3_224", .str_v3 = "SHA3-224", .v1_ctor = &EVP_sha3_224}, @@ -88,19 +82,19 @@ static digest_probe_t digest_probes[] = { }; digest_collection_t digest_collection("crypto.digest.digest_collection", digest_probes, - sizeof(digest_probes) / sizeof(digest_probes[0])); + std::size(digest_probes)); ERL_NIF_TERM digest_availability_t::get_atom() const { return this->init->atom; } #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) // Initialize an algorithm to check that all its dependencies are valid in FIPS -bool digest_availability_t::is_valid_in_fips(const EVP_MD *md) const { +bool digest_availability_t::check_valid_in_fips(const EVP_MD *md) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); int usable = 0; if (md) { // Try to initialize the digest algorithm for use, this will check the dependencies - if (EVP_DigestInit_ex(ctx, md, NULL) == 1) { + if (EVP_DigestInit_ex(ctx, md, nullptr) == 1) { usable = 1; } } @@ -112,14 +106,14 @@ bool digest_availability_t::is_valid_in_fips(const EVP_MD *md) const { void digest_availability_t::create_md_resource(bool fips_mode) { #ifdef HAS_3_0_API - EVP_MD *fetched_md = EVP_MD_fetch(NULL, this->init->str_v3, NULL); + EVP_MD *fetched_md = EVP_MD_fetch(nullptr, this->init->str_v3, nullptr); // Record failed algorithm instantiation for FIPS enabled & OpenSSL API 3.0 only - if (fips_mode && !is_usable_algorithm(fetched_md)) { - flags |= FIPS_FORBIDDEN_DIGEST; + if (fips_mode && !check_valid_in_fips(fetched_md)) { + flags.fips_forbidden = true; EVP_MD_free(fetched_md); // NULL is allowed } else { - this->flags &= ~FIPS_FORBIDDEN_DIGEST; + this->flags.fips_forbidden = false; this->md = fetched_md; } #else @@ -129,7 +123,8 @@ void digest_availability_t::create_md_resource(bool fips_mode) { } void digest_probe_t::probe(ErlNifEnv *, const bool fips_mode, std::vector &output) { - digest_availability_t algo = {.init = this, .flags = this->flags_hint, .xof_default_length = this->xof_default_length}; + digest_availability_t algo = { + .init = this, .flags = {.pbkdf2_eligible = this->pbkdf2}, .xof_default_length = this->xof_default_length}; // Unavailable are skipped. Available are added. Forbidden are added, but flagged with FIPS_FORBIDDEN_DIGEST. algo.create_md_resource(fips_mode); if (algo.md) { diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index ed0604522463..56228568b0c7 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -50,8 +50,10 @@ ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); struct digest_availability_t { // The definition used to create this record const struct digest_probe_t *init = nullptr; - // combination of DIGEST_TYPE_FLAGS from init + detected in runtime - size_t flags = 0; + struct { + bool fips_forbidden: 1; + bool pbkdf2_eligible: 1; + } flags; // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. const EVP_MD *md = nullptr; // 0 or default digest length for XOF digests @@ -61,7 +63,7 @@ struct digest_availability_t { bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT - return (this->flags & FIPS_FORBIDDEN_DIGEST) && FIPS_MODE(); + return this->flags.fips_forbidden && FIPS_MODE(); #else return false; #endif @@ -71,16 +73,10 @@ struct digest_availability_t { // Fetches the algorithm and sets the initial flags void create_md_resource(bool fips_mode); - // Attempts to instantiate the algorithm (forbidden algorithms will fail) -#if defined(HAS_3_0_API) - bool is_usable_algorithm(const EVP_MD *md) const; -#endif // HAS_3_0_API -}; - -// masks in the `flags` field of digest_type_t -enum DIGEST_TYPE_FLAGS { - FIPS_FORBIDDEN_DIGEST = 1, /* no support in FIPS for digest */ - PBKDF2_ELIGIBLE_DIGEST = 2 +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + // Initialize an algorithm to check that all its dependencies are valid in FIPS + static bool check_valid_in_fips(const EVP_MD *md); +#endif }; // This runs for each algorithm at library start and every time FIPS mode changes. @@ -93,9 +89,9 @@ struct digest_probe_t { const char *str_v3 = nullptr; // This will be updated to created atomfound exi ERL_NIF_TERM atom = 0; - // initial DIGEST_TYPE_FLAGS value for digest_type_t::flags - const size_t flags_hint = 0; - // OpenSSL 1.0 API to create resource for this digest algorithm (not used in 3.0 API) + // Hints that the algorithm is eligible for PBKDF2 + const bool pbkdf2 = false; + // OpenSSL 1.0 API to create a resource for this digest algorithm (not used in 3.0 API) const EVP_MD *(*v1_ctor)() = nullptr; size_t xof_default_length = 0; diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp index 02dd6d615847..1c518d225710 100644 --- a/lib/crypto/c_src/algorithms_pubkey.cpp +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -22,102 +22,101 @@ #include "algorithms_pubkey.h" #include "algorithms_collection.h" +#include "crypto_openssl_resource.h" pubkey_probe_t pubkey_probes[] = { - {.str = "rsa"}, + {.str = "rsa"}, #ifdef HAVE_DSA - {.str = "dss"}, + {.str = "dss"}, #endif #ifdef HAVE_DH - {.str = "dh"}, + {.str = "dh"}, #endif #if defined(HAVE_EC) #if !defined(OPENSSL_NO_EC2M) - {.str = "ec_gf2m"}, + {.str = "ec_gf2m"}, #endif - {.str = "ecdsa"}, {.str = "ecdh"}, + {.str = "ecdsa"}, {.str = "ecdh"}, #endif // Non-validated algorithms follow // Don't know if Edward curves are fips validated #if defined(HAVE_EDDSA) - {.str = "eddsa"}, + {.str = "eddsa"}, #endif #if defined(HAVE_EDDH) - {.str = "eddh"}, + {.str = "eddh"}, #endif - {.str = "srp"}, + {.str = "srp"}, #ifdef HAVE_ML_DSA - {.str = "mldsa44"}, {.str = "mldsa65"}, {.str = "mldsa87"}, + {.str = "mldsa44"}, {.str = "mldsa65"}, {.str = "mldsa87"}, #endif }; pubkey_collection_t pubkey_collection("crypto.pkey_collection", pubkey_probes, - sizeof(pubkey_probes) / sizeof(pubkey_probes[0])); + std::size(pubkey_probes)); // // Implementation of Pubkey Algorithm storage API // // C API: Proxy the call to generic algorithm_collection_t -extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, const bool fips_enabled) { +extern "C" size_t pubkey_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { return pubkey_collection.lazy_init(env, fips_enabled); } // C API: Proxy the call to generic algorithm_collection_t -extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, const bool fips_enabled) { +extern "C" ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv* env, const bool fips_enabled) { return pubkey_collection.to_list(env, fips_enabled); } ERL_NIF_TERM pubkey_availability_t::get_atom() const { return this->init->atom; } +// Result: flags set if FIPS is not supported #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) -// FIPS is supported AND enabled here -void pubkey_availability_t::probe_algorithm_against_fips() { - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); +void pubkey_availability_t::check_against_fips() { + auto_evp_pkey_ctx_t ctx(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + // failed: algorithm not available, do not add - if (ctx) { - if (EVP_PKEY_keygen_init(ctx) <= 0) { /* can't generate keys */ - flags |= FIPS_FORBIDDEN_PKEY_KEYGEN; - } - EVP_PKEY_CTX_free(ctx); + if (!ctx) { + this->flags.not_available = true; + return; + } + if (EVP_PKEY_keygen_init(ctx.pointer) <= 0) { // can't generate keys? + this->flags.fips_forbidden_keygen = true; + } - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_sign_init(ctx) <= 0) { /* can't sign */ - flags |= FIPS_FORBIDDEN_PKEY_SIGN; - } - EVP_PKEY_CTX_free(ctx); + // Drop previous pkey_ctx, create new + ctx.reset(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + if (EVP_PKEY_sign_init(ctx.pointer) <= 0) { // can't sign? + this->flags.fips_forbidden_sign = true; + } - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_verify_init(ctx) <= 0) { /* can't verify */ - flags |= FIPS_FORBIDDEN_PKEY_VERIFY; - } - EVP_PKEY_CTX_free(ctx); + // Drop previous pkey_ctx, create new + ctx.reset(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + if (EVP_PKEY_verify_init(ctx.pointer) <= 0) { // can't verify? + flags.fips_forbidden_verify = true; + } - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_encrypt_init(ctx) <= 0) { /* can't encrypt/decrypt */ - flags |= FIPS_FORBIDDEN_PKEY_ENCRYPT; - } - EVP_PKEY_CTX_free(ctx); + ctx.reset(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + if (EVP_PKEY_encrypt_init(ctx.pointer) <= 0) { // can't encrypt/decrypt? + flags.fips_forbidden_encrypt = true; + } - ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, NULL); - if (EVP_PKEY_derive_init(ctx) <= 0) { /* can't derive */ - flags |= FIPS_FORBIDDEN_PKEY_DERIVE; - } - EVP_PKEY_CTX_free(ctx); - } else { - flags |= FIPS_PKEY_NOT_AVAIL; + ctx.reset(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + if (EVP_PKEY_derive_init(ctx.pointer) <= 0) { // can't derive? + flags.fips_forbidden_derive = true; } } #endif // FIPS_SUPPORT && HAS_3_0_API // for FIPS we will attempt to initialize the pubkey context to verify whether the // algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. -void pubkey_probe_t::probe(ErlNifEnv *env, bool fips_enabled, std::vector &output) { +void pubkey_probe_t::probe(ErlNifEnv* env, bool fips_enabled, std::vector& output) { this->atom = create_or_existing_atom(env, this->str, this->atom); pubkey_availability_t algo = {.init = this}; #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) if (fips_enabled) { // attempt to instantiate the algorithm and set availability flags - algo->probe_algorithm_against_fips(); + algo.check_against_fips(); } #endif // FIPS_SUPPORT && HAS_3_0_API return output.push_back(algo); diff --git a/lib/crypto/c_src/algorithms_pubkey.h b/lib/crypto/c_src/algorithms_pubkey.h index d87716ecaf72..22a1435bf788 100644 --- a/lib/crypto/c_src/algorithms_pubkey.h +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -32,9 +32,9 @@ extern "C" { // // Pubkey Algorithms storage C API // -size_t pubkey_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled); -ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); -void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailable, ERL_NIF_TERM atom); +size_t pubkey_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); +ERL_NIF_TERM pubkey_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); +void pubkey_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailable, ERL_NIF_TERM atom); #ifdef __cplusplus } @@ -44,10 +44,10 @@ void pubkey_add_algorithm(ErlNifEnv *env, const char *str_v3, unsigned unavailab struct pubkey_probe_t; struct pubkey_availability_t { - const pubkey_probe_t *init; // the pubkey_probe_t used to create this record + const pubkey_probe_t* init; // the pubkey_probe_t used to create this record struct { - bool not_available : 1; + bool not_available : 1; // algorithm init failed bool fips_forbidden_keygen : 1; bool fips_forbidden_sign : 1; bool fips_forbidden_verify : 1; @@ -58,30 +58,29 @@ struct pubkey_availability_t { bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT // Forbidden in FIPS if all operations are forbidden, or if algorithm is not available at all - return ((this->flags.fips_forbidden_keygen && this->flags.fips_forbidden_sign && - this->flags.fips_forbidden_verify && this->flags.fips_forbidden_encrypt && - this->flags.fips_forbidden_derive) || - this->flags.not_available) && - FIPS_MODE(); + return (this->flags.not_available || + this->flags.fips_forbidden_keygen && this->flags.fips_forbidden_sign && + this->flags.fips_forbidden_verify && this->flags.fips_forbidden_encrypt && + this->flags.fips_forbidden_derive) && + FIPS_MODE(); #else return false; #endif } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; - // Update this->flags for FIPS compatibility #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - void probe_algorithm_against_fips(); // fips is supported AND enabled here + void check_against_fips(); // Result: flags set if FIPS is not supported #endif // FIPS_SUPPORT && HAS_3_0_API }; struct pubkey_probe_t { - const char *str = nullptr; - const char *str_v3 = nullptr; // if this is nullptr, .str will be used instead + const char* str = nullptr; + const char* str_v3 = nullptr; // if this is nullptr, .str will be used instead ERL_NIF_TERM atom = 0; // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' - void probe(ErlNifEnv *env, bool fips_enabled, std::vector &output); + void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); }; using pubkey_collection_t = algorithm_collection_t; diff --git a/lib/crypto/c_src/crypto_openssl_resource.h b/lib/crypto/c_src/crypto_openssl_resource.h index d1758058bd9b..1aa801bbba82 100644 --- a/lib/crypto/c_src/crypto_openssl_resource.h +++ b/lib/crypto/c_src/crypto_openssl_resource.h @@ -1,5 +1,5 @@ /* -* %CopyrightBegin% + * %CopyrightBegin% * * SPDX-License-Identifier: Apache-2.0 * @@ -26,33 +26,46 @@ #ifdef __cplusplus // A generic struct holding a pointer, constructable with a pointer or as null, and auto-destructable. -// The bool operator allows using the struct in if() condit -// When inheriting: implement the destructor with the call to OpenSSL free function for corresponding resource +// The bool operator allows using the struct in if() conditions +// When inheriting: implement the destructor with the call to the corresponding OpenSSL free function template struct auto_openssl_resource_t { - T *pointer = nullptr; - explicit auto_openssl_resource_t(T *p) : pointer(p) {} + T* pointer = nullptr; + explicit auto_openssl_resource_t(T* p) : pointer(p) {} explicit operator bool() const { return this->pointer != nullptr; } }; -struct auto_evp_pkey_t: auto_openssl_resource_t { - explicit auto_evp_pkey_t(EVP_PKEY *p) : auto_openssl_resource_t(p) {} - ~auto_evp_pkey_t() { - EVP_PKEY_free(this->pointer); +struct auto_evp_pkey_t : auto_openssl_resource_t { + explicit auto_evp_pkey_t(EVP_PKEY* p) : auto_openssl_resource_t(p) {} + ~auto_evp_pkey_t() { reset(nullptr); } + void reset(EVP_PKEY* new_value) { + if (this->pointer) { + EVP_PKEY_free(this->pointer); + } + this->pointer = new_value; } }; -struct auto_evp_pkey_ctx_t: auto_openssl_resource_t { - explicit auto_evp_pkey_ctx_t(EVP_PKEY_CTX *c) : auto_openssl_resource_t(c) {} - ~auto_evp_pkey_ctx_t() { - EVP_PKEY_CTX_free(this->pointer); +struct auto_evp_pkey_ctx_t : auto_openssl_resource_t { + explicit auto_evp_pkey_ctx_t(EVP_PKEY_CTX* c) : auto_openssl_resource_t(c) {} + ~auto_evp_pkey_ctx_t() { reset(nullptr); } + void reset(EVP_PKEY_CTX* new_value) { + if (this->pointer) { + EVP_PKEY_CTX_free(this->pointer); + } + this->pointer = new_value; } }; -struct auto_ec_key_t: auto_openssl_resource_t { - explicit auto_ec_key_t(EC_KEY *c) : auto_openssl_resource_t(c) {} - ~auto_ec_key_t() { - EC_KEY_free(this->pointer); +struct auto_ec_key_t : auto_openssl_resource_t { + explicit auto_ec_key_t(EC_KEY* c) : auto_openssl_resource_t(c) {} + ~auto_ec_key_t() { reset(nullptr); } + void reset(EC_KEY* new_value) { + // TODO: EC_KEY_free is deprecated since OpenSSL 3.0 + if (this->pointer) { + EC_KEY_free(this->pointer); + } + this->pointer = new_value; } }; diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index fc46c9b300f7..558112656c79 100644 --- a/lib/crypto/c_src/fips.c +++ b/lib/crypto/c_src/fips.c @@ -33,12 +33,10 @@ ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #endif } -ERL_NIF_TERM enable_fips_mode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Boolean) */ +ERL_NIF_TERM enable_fips_mode_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { return enable_fips_mode(env, argv[0]); } - ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) #ifdef FIPS_SUPPORT { @@ -59,9 +57,10 @@ ERL_NIF_TERM enable_fips_mode(ErlNifEnv* env, ERL_NIF_TERM fips_mode_to_set) } #else { + // Can't set FIPS mode if no FIPS support in the OpenSSL library, fail any attempt to enable it if (fips_mode_to_set == atom_true) return atom_false; - else if (fips_mode_to_set == atom_false) return atom_true; - else return enif_make_badarg(env); + if (fips_mode_to_set == atom_false) return atom_true; + return enif_make_badarg(env); } #endif From 7b7ffb262351b617073a99eb053220198b6ba2b3 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 12 Nov 2025 02:27:00 +0100 Subject: [PATCH 12/15] Syntax and warnings fixes --- lib/crypto/c_src/algorithms.c | 14 ++++++++------ lib/crypto/c_src/algorithms_collection.h | 2 +- lib/crypto/c_src/algorithms_curve.cpp | 4 +++- lib/crypto/c_src/algorithms_curve.h | 7 +++++-- lib/crypto/c_src/algorithms_digest.cpp | 4 +++- lib/crypto/c_src/algorithms_digest.h | 8 ++++---- lib/crypto/c_src/algorithms_pubkey.cpp | 4 +++- lib/crypto/c_src/algorithms_pubkey.h | 9 ++++++--- 8 files changed, 33 insertions(+), 19 deletions(-) diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 3b3dae08e834..708fa5561d0f 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -23,6 +23,7 @@ #include "algorithms.h" #include "algorithms_pubkey.h" #include "algorithms_digest.h" +#include "algorithms_curve.h" #include "cipher.h" #include "common.h" #include "mac.h" @@ -230,25 +231,26 @@ void init_rsa_opts_types(ErlNifEnv* env) { ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { digest_types_lazy_init(env, FIPS_MODE()); + // Filter the results by the result of algorithm.is_forbidden_in_fips() == true return digest_types_as_list(env, true); } ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef FIPS_SUPPORT - pubkey_algorithms_lazy_init(env, 1, &pubkey_algorithms_delayed_init); - /* Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == true */ + pubkey_algorithms_lazy_init(env, 1); + // Filter the results by the result of algorithm.is_forbidden_in_fips() == true return pubkey_algorithms_as_list(env, true); #else - return enif_make_list(env, 0); /* nothing is forbidden */ + return enif_make_list(env, 0); // nothing is forbidden #endif } ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { #ifdef FIPS_SUPPORT - /* Filter the results by IS_CIPHER_FORBIDDEN_IN_FIPS() == true */ + // Filter the results by the result of algorithm.is_forbidden_in_fips() == true return cipher_types_as_list(env, true); #else - return enif_make_list(env, 0); /* nothing is forbidden */ + return enif_make_list(env, 0); // nothing is forbidden #endif } @@ -269,6 +271,6 @@ ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_N } ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - curve_algorithms_lazy_init(env, FIPS_MODE(), &curve_algorithms_delayed_init); + curve_algorithms_lazy_init(env, FIPS_MODE()); return curve_algorithms_as_list(env, true); } diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h index 2c68744796a2..b0c39d349b3a 100644 --- a/lib/crypto/c_src/algorithms_collection.h +++ b/lib/crypto/c_src/algorithms_collection.h @@ -122,7 +122,7 @@ struct algorithm_collection_t { mutex_lock_and_auto_release critical_section(this->mutex); this->algorithms.clear(); - for (auto i = 0; i < probe_count; i++) { + for (size_t i = 0; i < probe_count; i++) { // For each probe, call probe() member function, in case of success the probe code // will use the passed 'this->algorithms' reference to add an algorithm to the collection. probes[i].probe(env, fips_enabled, this->algorithms); diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp index 2de8f77a7195..ae4d51b5402a 100644 --- a/lib/crypto/c_src/algorithms_curve.cpp +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -24,6 +24,8 @@ #include "algorithms_curve.h" #include "crypto_openssl_resource.h" +#include + curve_probe_t curve_probes[] = { #if defined(HAVE_EC) #ifdef NID_secp160k1 @@ -374,7 +376,7 @@ curve_probe_t curve_probes[] = { }; curve_collection_t curve_collection("crypto.curve_collection", curve_probes, - std::size(curve_probes)); + sizeof(curve_probes)/sizeof(curve_probes[0])); // // Implementation of Curve Algorithm storage API diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h index f1a9bdbd36e3..3efd486dd355 100644 --- a/lib/crypto/c_src/algorithms_curve.h +++ b/lib/crypto/c_src/algorithms_curve.h @@ -43,11 +43,11 @@ ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); struct curve_probe_t; struct curve_availability_t { - const curve_probe_t *init; // the probe which created this record, contains name, atom, etc. + const curve_probe_t *init = nullptr; // the probe which created this record, contains name, atom, etc. struct { bool fips_forbidden: 1; bool curve_init_failed: 1; // not possible to create with fips=yes - } flags; + } flags = {}; bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT @@ -63,6 +63,9 @@ struct curve_availability_t { void probe_under_fips(bool fips_mode); }; +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. struct curve_probe_t { size_t nid = 0; // NID_xxxx value of OpenSSL const char *sn = nullptr; // serves as Erlang atom name, also equal to SN_xxxxx macro of OpenSSL diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index c95e74be8f33..2df70789a4e8 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -23,6 +23,8 @@ #include "algorithms_digest.h" #include "algorithms_collection.h" +#include + static digest_probe_t digest_probes[] = { #ifdef HAVE_MD4 {.str = "md4", .str_v3 = "MD4", .v1_ctor = &EVP_md4}, @@ -82,7 +84,7 @@ static digest_probe_t digest_probes[] = { }; digest_collection_t digest_collection("crypto.digest.digest_collection", digest_probes, - std::size(digest_probes)); + sizeof(digest_probes)/sizeof(digest_probes[0])); ERL_NIF_TERM digest_availability_t::get_atom() const { return this->init->atom; } diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index 56228568b0c7..2eb3b3a814bb 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -53,7 +53,7 @@ struct digest_availability_t { struct { bool fips_forbidden: 1; bool pbkdf2_eligible: 1; - } flags; + } flags = {}; // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. const EVP_MD *md = nullptr; // 0 or default digest length for XOF digests @@ -79,9 +79,9 @@ struct digest_availability_t { #endif }; -// This runs for each algorithm at library start and every time FIPS mode changes. -// Describes data required by digest availability probing algorithm (separate branches for -// OpenSSL API < 3.0, 3.0, and for FIPS enabled/disabled). +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. struct digest_probe_t { // the algorithm name as in OpenSSL < 3, also atom used by Erlang API const char *str = nullptr; diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp index 1c518d225710..e0af3fef0ba0 100644 --- a/lib/crypto/c_src/algorithms_pubkey.cpp +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -24,6 +24,8 @@ #include "algorithms_collection.h" #include "crypto_openssl_resource.h" +#include + pubkey_probe_t pubkey_probes[] = { {.str = "rsa"}, #ifdef HAVE_DSA @@ -53,7 +55,7 @@ pubkey_probe_t pubkey_probes[] = { }; pubkey_collection_t pubkey_collection("crypto.pkey_collection", pubkey_probes, - std::size(pubkey_probes)); + sizeof(pubkey_probes)/sizeof(pubkey_probes[0])); // // Implementation of Pubkey Algorithm storage API diff --git a/lib/crypto/c_src/algorithms_pubkey.h b/lib/crypto/c_src/algorithms_pubkey.h index 22a1435bf788..f9dd45d7bd9b 100644 --- a/lib/crypto/c_src/algorithms_pubkey.h +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -44,7 +44,7 @@ void pubkey_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailab struct pubkey_probe_t; struct pubkey_availability_t { - const pubkey_probe_t* init; // the pubkey_probe_t used to create this record + const pubkey_probe_t* init = nullptr; // the pubkey_probe_t used to create this record struct { bool not_available : 1; // algorithm init failed @@ -53,7 +53,7 @@ struct pubkey_availability_t { bool fips_forbidden_verify : 1; bool fips_forbidden_encrypt : 1; bool fips_forbidden_derive : 1; - } flags; + } flags = {}; bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT @@ -74,12 +74,15 @@ struct pubkey_availability_t { #endif // FIPS_SUPPORT && HAS_3_0_API }; +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. struct pubkey_probe_t { const char* str = nullptr; const char* str_v3 = nullptr; // if this is nullptr, .str will be used instead ERL_NIF_TERM atom = 0; - // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' + // Perform a probe on the algorithm. In case of success, fill the struct and push into the 'output' void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); }; From c1f436e86cc31fed96990d88d72e96229c407603 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 12 Nov 2025 04:57:20 +0100 Subject: [PATCH 13/15] KEM algorithms storage and probes --- lib/crypto/c_src/Makefile.in | 1 + lib/crypto/c_src/algorithms.c | 100 +-------------------- lib/crypto/c_src/algorithms_curve.cpp | 2 - lib/crypto/c_src/algorithms_digest.cpp | 84 ++++++++++------- lib/crypto/c_src/algorithms_digest.h | 38 +++++--- lib/crypto/c_src/algorithms_kem.cpp | 93 +++++++++++++++++++ lib/crypto/c_src/algorithms_kem.h | 79 ++++++++++++++++ lib/crypto/c_src/algorithms_pubkey.cpp | 2 - lib/crypto/c_src/crypto_openssl_resource.h | 13 +++ lib/crypto/c_src/hash.c | 46 +++++----- lib/crypto/c_src/pkey.c | 18 ++-- 11 files changed, 297 insertions(+), 179 deletions(-) create mode 100644 lib/crypto/c_src/algorithms_kem.cpp create mode 100644 lib/crypto/c_src/algorithms_kem.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 557159f4dc86..5067673000c2 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -95,6 +95,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/algorithms_digest$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_pubkey$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_curve$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_kem$(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 708fa5561d0f..55f076b0eb6a 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -24,48 +24,19 @@ #include "algorithms_pubkey.h" #include "algorithms_digest.h" #include "algorithms_curve.h" +#include "algorithms_kem.h" #include "cipher.h" #include "common.h" #include "mac.h" -#include -#include "algorithms_digest.h" - -struct kem_availability_t { - const char* str_v3; /* the algorithm name as in OpenSSL 3.x */ - unsigned flags; /* combination of KEM_AVAIL_FLAGS */ - ERL_NIF_TERM atom; /* as returned to the library user on a query */ -}; - -enum KEM_AVAIL_FLAGS { - FIPS_KEM_NOT_AVAIL = 1, -}; - -#ifdef FIPS_SUPPORT -# define IS_KEM_FORBIDDEN_IN_FIPS(p) (((p)->flags & FIPS_KEM_NOT_AVAIL) == FIPS_KEM_NOT_AVAIL && FIPS_MODE()) -#else -# define IS_KEM_FORBIDDEN_IN_FIPS(P) false -#endif - -/* Stores all known KEM algorithms with their FIPS unavailability flag if - * FIPS is enabled */ -static struct kem_availability_array_t { - size_t count; - struct kem_availability_t algorithm[3]; /* increase when extending the list */ -} algo_kem; - -void init_kem_types(void); - static size_t algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ void init_rsa_opts_types(ErlNifEnv* env); - void init_algorithms_types(ErlNifEnv* env) { init_mac_types(env); init_cipher_types(env); - init_kem_types(); init_rsa_opts_types(env); /* ciphers and macs are initiated statically */ } @@ -87,78 +58,9 @@ ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return pubkey_algorithms_as_list(env, false); } -#ifdef HAVE_ML_KEM -static void add_kem_algorithm(const char* str_v3, const unsigned unavail_flags, ERL_NIF_TERM atom) { - struct kem_availability_t* algo = &algo_kem.algorithm[algo_kem.count]; - algo->str_v3 = str_v3; - algo->flags = unavail_flags; - algo->atom = atom; - algo_kem.count++; -} -#endif - -#ifdef HAVE_ML_KEM -/* - * for FIPS will attempt to initialize the KEM context to verify whether the - * algorithm is allowed, for non-FIPS the old behavior - always allow. - */ -static void probe_kem_algorithm(const char* str_v3, ERL_NIF_TERM atom) { - unsigned unavailable = 0; -#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) - EVP_KEM *kem = EVP_KEM_fetch(NULL, str_v3, "fips=yes"); - if (!kem) { - return; /* not available by name */ - } - - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, str_v3, "fips=yes"); - /* failed: algorithm not available, do not add */ - if (ctx) { - if (EVP_PKEY_encapsulate_init(ctx, NULL) == 1) { - EVP_PKEY_CTX_free(ctx); - } else { - unavailable |= FIPS_KEM_NOT_AVAIL; - } - } - - EVP_KEM_free(kem); -#endif /* FIPS_SUPPORT && HAS_3_0_API */ - - add_kem_algorithm(str_v3, unavailable, atom); -} -#endif - -void init_kem_types(void) { - algo_kem.count = 0; -#ifdef HAVE_ML_KEM - probe_kem_algorithm("mlkem512", atom_mlkem512); - probe_kem_algorithm("mlkem768", atom_mlkem768); - probe_kem_algorithm("mlkem1024", atom_mlkem1024); - /* When adding a new algorithm, update the size of algo_kem.algorithm array */ -#endif - ASSERT(algo_kem.count <= sizeof(algo_kem.algorithm)/sizeof(algo_kem.algorithm[0])); -} - -static ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, const bool fips_forbidden) -{ - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (size_t i = 0; i < algo_kem.count; i++) { - struct kem_availability_t* p = &algo_kem.algorithm[i]; - if (IS_KEM_FORBIDDEN_IN_FIPS(p) == fips_forbidden) { - hd = enif_make_list_cell(env, p->atom, hd); - } - } - - return hd; -} - ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef HAVE_ML_KEM return kem_algorithms_as_list(env, false); -#else - return enif_make_list(env, 0); -#endif } diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp index ae4d51b5402a..6fd53fb59c51 100644 --- a/lib/crypto/c_src/algorithms_curve.cpp +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -24,8 +24,6 @@ #include "algorithms_curve.h" #include "crypto_openssl_resource.h" -#include - curve_probe_t curve_probes[] = { #if defined(HAVE_EC) #ifdef NID_secp160k1 diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index 2df70789a4e8..2718b08ab6cc 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -23,30 +23,28 @@ #include "algorithms_digest.h" #include "algorithms_collection.h" -#include - static digest_probe_t digest_probes[] = { #ifdef HAVE_MD4 - {.str = "md4", .str_v3 = "MD4", .v1_ctor = &EVP_md4}, + {.str = "md4", .str_v3 = "MD4", .v1_ctor = &EVP_md4}, #endif #ifdef HAVE_MD5 - {.str = "md5", .str_v3 = "MD5", .v1_ctor = &EVP_md5}, + {.str = "md5", .str_v3 = "MD5", .v1_ctor = &EVP_md5}, #endif #ifdef HAVE_RIPEMD160 - {.str = "ripemd160", .str_v3 = "RIPEMD160", .v1_ctor = &EVP_ripemd160}, + {.str = "ripemd160", .str_v3 = "RIPEMD160", .v1_ctor = &EVP_ripemd160}, #endif - {.str = "sha", .str_v3 = "SHA1", .pbkdf2 = true, .v1_ctor = &EVP_sha1}, + {.str = "sha", .str_v3 = "SHA1", .pbkdf2 = true, .v1_ctor = &EVP_sha1}, #ifdef HAVE_SHA224 - {.str = "sha224", .str_v3 = "SHA2-224", .pbkdf2 = true, .v1_ctor = &EVP_sha224}, + {.str = "sha224", .str_v3 = "SHA2-224", .pbkdf2 = true, .v1_ctor = &EVP_sha224}, #endif #ifdef HAVE_SHA256 - {.str = "sha256", .str_v3 = "SHA2-256", .pbkdf2 = true, .v1_ctor = &EVP_sha256}, + {.str = "sha256", .str_v3 = "SHA2-256", .pbkdf2 = true, .v1_ctor = &EVP_sha256}, #endif #ifdef HAVE_SHA384 - {.str = "sha384", .str_v3 = "SHA2-384", .pbkdf2 = true, .v1_ctor = &EVP_sha384}, + {.str = "sha384", .str_v3 = "SHA2-384", .pbkdf2 = true, .v1_ctor = &EVP_sha384}, #endif #ifdef HAVE_SHA512 - {.str = "sha512", .str_v3 = "SHA2-512", .pbkdf2 = true, .v1_ctor = &EVP_sha512}, + {.str = "sha512", .str_v3 = "SHA2-512", .pbkdf2 = true, .v1_ctor = &EVP_sha512}, #endif #ifdef HAVE_SHA512_224 {.str = "sha512_224", .str_v3 = "SHA2-512/224", .pbkdf2 = true, .v1_ctor = &EVP_sha512_224}, @@ -55,43 +53,43 @@ static digest_probe_t digest_probes[] = { {.str = "sha512_256", .str_v3 = "SHA2-512/256", .pbkdf2 = true, .v1_ctor = &EVP_sha512_256}, #endif #ifdef HAVE_SHA3_224 - {.str = "sha3_224", .str_v3 = "SHA3-224", .v1_ctor = &EVP_sha3_224}, + {.str = "sha3_224", .str_v3 = "SHA3-224", .v1_ctor = &EVP_sha3_224}, #endif #ifdef HAVE_SHA3_256 - {.str = "sha3_256", .str_v3 = "SHA3-256", .v1_ctor = &EVP_sha3_256}, + {.str = "sha3_256", .str_v3 = "SHA3-256", .v1_ctor = &EVP_sha3_256}, #endif #ifdef HAVE_SHA3_384 - {.str = "sha3_384", .str_v3 = "SHA3-384", .v1_ctor = &EVP_sha3_384}, + {.str = "sha3_384", .str_v3 = "SHA3-384", .v1_ctor = &EVP_sha3_384}, #endif #ifdef HAVE_SHA3_512 - {.str = "sha3_512", .str_v3 = "SHA3-512", .v1_ctor = &EVP_sha3_512}, + {.str = "sha3_512", .str_v3 = "SHA3-512", .v1_ctor = &EVP_sha3_512}, #endif #ifdef HAVE_SHAKE128 - {.str = "shake128", .str_v3 = "SHAKE-128", .v1_ctor = &EVP_shake128, .xof_default_length = 6}, + {.str = "shake128", .str_v3 = "SHAKE-128", .v1_ctor = &EVP_shake128, .xof_default_length = 6}, #endif #ifdef HAVE_SHAKE256 - {.str = "shake256", .str_v3 = "SHAKE-256", .v1_ctor = &EVP_shake256, .xof_default_length = 32}, + {.str = "shake256", .str_v3 = "SHAKE-256", .v1_ctor = &EVP_shake256, .xof_default_length = 32}, #endif #ifdef HAVE_SM3 - {.str = "sm3", .str_v3 = "SM3", .v1_ctor = &EVP_sm3}, + {.str = "sm3", .str_v3 = "SM3", .v1_ctor = &EVP_sm3}, #endif #ifdef HAVE_BLAKE2 - {.str = "blake2b", .str_v3 = "BLAKE2b512", .v1_ctor = &EVP_blake2b512}, + {.str = "blake2b", .str_v3 = "BLAKE2b512", .v1_ctor = &EVP_blake2b512}, #endif #ifdef HAVE_BLAKE2 - {.str = "blake2s", .str_v3 = "BLAKE2s256", .v1_ctor = &EVP_blake2s256}, + {.str = "blake2s", .str_v3 = "BLAKE2s256", .v1_ctor = &EVP_blake2s256}, #endif }; digest_collection_t digest_collection("crypto.digest.digest_collection", digest_probes, - sizeof(digest_probes)/sizeof(digest_probes[0])); + sizeof(digest_probes) / sizeof(digest_probes[0])); ERL_NIF_TERM digest_availability_t::get_atom() const { return this->init->atom; } #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) // Initialize an algorithm to check that all its dependencies are valid in FIPS -bool digest_availability_t::check_valid_in_fips(const EVP_MD *md) { - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); +bool digest_availability_t::check_valid_in_fips(const EVP_MD* md) { + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); int usable = 0; if (md) { @@ -108,13 +106,14 @@ bool digest_availability_t::check_valid_in_fips(const EVP_MD *md) { void digest_availability_t::create_md_resource(bool fips_mode) { #ifdef HAS_3_0_API - EVP_MD *fetched_md = EVP_MD_fetch(nullptr, this->init->str_v3, nullptr); + EVP_MD* fetched_md = EVP_MD_fetch(nullptr, this->init->str_v3, nullptr); // Record failed algorithm instantiation for FIPS enabled & OpenSSL API 3.0 only if (fips_mode && !check_valid_in_fips(fetched_md)) { flags.fips_forbidden = true; EVP_MD_free(fetched_md); // NULL is allowed - } else { + } + else { this->flags.fips_forbidden = false; this->md = fetched_md; } @@ -124,7 +123,7 @@ void digest_availability_t::create_md_resource(bool fips_mode) { #endif // HAS_3_0_API } -void digest_probe_t::probe(ErlNifEnv *, const bool fips_mode, std::vector &output) { +void digest_probe_t::probe(ErlNifEnv*, const bool fips_mode, std::vector& output) { digest_availability_t algo = { .init = this, .flags = {.pbkdf2_eligible = this->pbkdf2}, .xof_default_length = this->xof_default_length}; // Unavailable are skipped. Available are added. Forbidden are added, but flagged with FIPS_FORBIDDEN_DIGEST. @@ -134,22 +133,45 @@ void digest_probe_t::probe(ErlNifEnv *, const bool fips_mode, std::vectormd) { #if defined(HAS_3_0_API) - EVP_MD_free(const_cast(this->md)); + EVP_MD_free(const_cast(this->md)); #else - EVP_MD_meth_free(const_cast(this->md)); + EVP_MD_meth_free(const_cast(this->md)); #endif // HAS_3_0_API this->md = nullptr; } } + +extern "C" bool is_digest_forbidden_in_fips(const digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return true; // "forbidden" when there's no digest + } + return static_cast(p.ptr)->is_forbidden_in_fips(); +} + +extern "C" const EVP_MD* digest_availability_md(const digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return nullptr; // "no md resource" when there's no digest + } + return static_cast(p.ptr)->md; +} + +extern "C" size_t digest_availability_xof_default_length(const digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return 0; // "no xof default length" when there's no digest + } + return static_cast(p.ptr)->xof_default_length; +} diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index 2eb3b3a814bb..cdd37eca84c1 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -24,19 +24,31 @@ #include "algorithms_collection.h" +struct digest_availability_t; + #ifdef __cplusplus extern "C" { #endif #include "common.h" +// Wraps a pointer to digest_availability_t which is a C++ struct with C++ features, for use in C API +struct digest_availability_Cptr { + void* ptr; +}; + // // C Digest storage API // -void digest_types_lazy_init(ErlNifEnv *env, bool fips_mode); -void digest_types_delayed_init(ErlNifEnv *env); -struct digest_availability_t *get_digest_type(ERL_NIF_TERM type); -ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); +void digest_types_lazy_init(ErlNifEnv* env, bool fips_mode); +void digest_types_delayed_init(ErlNifEnv* env); +ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, bool fips_forbidden); + +// Lookup and access fields +struct digest_availability_Cptr get_digest_type(ERL_NIF_TERM type); // linear lookup by atom +bool is_digest_forbidden_in_fips(struct digest_availability_Cptr p); // access C++ member from C +const EVP_MD* digest_availability_md(struct digest_availability_Cptr p); // access field +size_t digest_availability_xof_default_length(struct digest_availability_Cptr p); // access field #ifdef __cplusplus } @@ -49,13 +61,13 @@ ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); // created again. struct digest_availability_t { // The definition used to create this record - const struct digest_probe_t *init = nullptr; + const struct digest_probe_t* init = nullptr; struct { - bool fips_forbidden: 1; - bool pbkdf2_eligible: 1; + bool fips_forbidden : 1; + bool pbkdf2_eligible : 1; } flags = {}; // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. - const EVP_MD *md = nullptr; + const EVP_MD* md = nullptr; // 0 or default digest length for XOF digests size_t xof_default_length = 0; @@ -75,7 +87,7 @@ struct digest_availability_t { void create_md_resource(bool fips_mode); #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) // Initialize an algorithm to check that all its dependencies are valid in FIPS - static bool check_valid_in_fips(const EVP_MD *md); + static bool check_valid_in_fips(const EVP_MD* md); #endif }; @@ -84,19 +96,19 @@ struct digest_availability_t { // result in a new available algorithm creation. struct digest_probe_t { // the algorithm name as in OpenSSL < 3, also atom used by Erlang API - const char *str = nullptr; + const char* str = nullptr; // the algorithm name as in OpenSSL 3.x - const char *str_v3 = nullptr; + const char* str_v3 = nullptr; // This will be updated to created atomfound exi ERL_NIF_TERM atom = 0; // Hints that the algorithm is eligible for PBKDF2 const bool pbkdf2 = false; // OpenSSL 1.0 API to create a resource for this digest algorithm (not used in 3.0 API) - const EVP_MD *(*v1_ctor)() = nullptr; + const EVP_MD* (*v1_ctor)() = nullptr; size_t xof_default_length = 0; // Perform probe on the algorithm. In case of success, fill the struct and push into the 'output' - void probe(ErlNifEnv *env, bool fips_mode, std::vector &output); + void probe(ErlNifEnv* env, bool fips_mode, std::vector& output); }; using digest_collection_t = algorithm_collection_t; diff --git a/lib/crypto/c_src/algorithms_kem.cpp b/lib/crypto/c_src/algorithms_kem.cpp new file mode 100644 index 000000000000..d30387a6a7b6 --- /dev/null +++ b/lib/crypto/c_src/algorithms_kem.cpp @@ -0,0 +1,93 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_kem.h" +#include "algorithms_collection.h" +#include "crypto_openssl_resource.h" + +kem_probe_t kem_probes[] = { +#ifdef HAVE_ML_KEM + {.str_v3 = "mlkem512"}, + {.str_v3 = "mlkem768"}, + {.str_v3 = "mlkem1024"}, +#endif +}; + +kem_collection_t kem_collection("crypto.kem_collection", kem_probes, sizeof(kem_probes) / sizeof(kem_probes[0])); + +// +// Implementation of KEM Algorithm storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" size_t kem_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + return kem_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, const bool fips_enabled) { + return kem_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM kem_availability_t::get_atom() const { return this->init->atom; } + +// +// for FIPS will attempt to initialize the KEM context to verify whether the +// algorithm is allowed, for non-FIPS the old behavior - always allow. +// +bool kem_availability_t::check_kem_algorithm(bool fips_enabled) { +#ifdef HAVE_ML_KEM +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + const auto_evp_kem_t kem(EVP_KEM_fetch(nullptr, this->init->str_v3, nullptr)); + if (!kem) { + return false; // not available by name + } + + const auto_evp_pkey_ctx_t ctx(EVP_PKEY_CTX_new_from_name(nullptr, this->init->str_v3, nullptr)); + // failed: algorithm not available, do not add + if (ctx) { + if (EVP_PKEY_encapsulate_init(ctx.pointer, nullptr) != 1) { + this->flags.fips_forbidden = true; + } + } +#endif // FIPS_SUPPORT && HAS_3_0_API + return true; +#else + return false; +#endif // HAVE_ML_KEM +} + +// for FIPS we will attempt to initialize the pubkey context to verify whether the +// algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. +void kem_probe_t::probe(ErlNifEnv* env, const bool fips_enabled, std::vector& output) { + // Nothing will happen if HAVE_ML_KEM is not defined, the output will remain empty +#ifdef HAVE_ML_KEM + this->atom = create_or_existing_atom(env, this->str_v3, this->atom); + kem_availability_t algo = {.init = this}; +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + if (!algo.check_kem_algorithm(fips_enabled)) { + return; // failed to find the algorithm, do not add + } +#endif // FIPS_SUPPORT && HAS_3_0_API + return output.push_back(algo); +#endif +} diff --git a/lib/crypto/c_src/algorithms_kem.h b/lib/crypto/c_src/algorithms_kem.h new file mode 100644 index 000000000000..b0e31b43fbdb --- /dev/null +++ b/lib/crypto/c_src/algorithms_kem.h @@ -0,0 +1,79 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once +#include "algorithms_collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// +// KEM Algorithms storage C API +// +size_t kem_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); +ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); +void kem_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailable, ERL_NIF_TERM atom); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +struct kem_probe_t; + +struct kem_availability_t { + const kem_probe_t* init = nullptr; // the pubkey_probe_t used to create this record + + struct { + bool fips_forbidden : 1; + } flags = {}; + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +#else + return false; +#endif + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; + bool check_kem_algorithm(bool fips_enabled); +}; + +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. +struct kem_probe_t { + const char* str_v3 = nullptr; // if this is nullptr, .str will be used instead + ERL_NIF_TERM atom = 0; + + // Perform a probe on the algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); +}; + +using kem_collection_t = algorithm_collection_t; +extern kem_collection_t kem_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp index e0af3fef0ba0..ed0fe44d1e3f 100644 --- a/lib/crypto/c_src/algorithms_pubkey.cpp +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -24,8 +24,6 @@ #include "algorithms_collection.h" #include "crypto_openssl_resource.h" -#include - pubkey_probe_t pubkey_probes[] = { {.str = "rsa"}, #ifdef HAVE_DSA diff --git a/lib/crypto/c_src/crypto_openssl_resource.h b/lib/crypto/c_src/crypto_openssl_resource.h index 1aa801bbba82..99594936a4c4 100644 --- a/lib/crypto/c_src/crypto_openssl_resource.h +++ b/lib/crypto/c_src/crypto_openssl_resource.h @@ -69,4 +69,17 @@ struct auto_ec_key_t : auto_openssl_resource_t { } }; +#ifdef HAVE_ML_KEM +struct auto_evp_kem_t : auto_openssl_resource_t { + explicit auto_evp_kem_t(EVP_KEM* kem) : auto_openssl_resource_t(kem) {} + ~auto_evp_kem_t() { reset(nullptr); } + void reset(EVP_KEM* new_value) { + if (this->pointer) { + EVP_KEM_free(this->pointer); + } + this->pointer = new_value; + } +}; +#endif + #endif // __cplusplus diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index d0e02bfa5da4..6044f5a146b3 100644 --- a/lib/crypto/c_src/hash.c +++ b/lib/crypto/c_src/hash.c @@ -72,46 +72,46 @@ int init_hash_ctx(ErlNifEnv* env, ErlNifBinary* rt_buf) { } ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type) */ - struct digest_availability_t *digp = NULL; - const EVP_MD *md; +{ /* (Type) */ + const EVP_MD* md; ERL_NIF_TERM keys[3] = { atom_type, atom_size, atom_block_size }; ERL_NIF_TERM values[3]; ERL_NIF_TERM ret; - int ok; ASSERT(argc == 1); - if ((digp = get_digest_type(argv[0])) == NULL) + struct digest_availability_Cptr digp = get_digest_type(argv[0]); + if (digp.ptr == NULL) return enif_make_badarg(env); - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) return RAISE_NOTSUP(env); - if ((md = digp->md.p) == NULL) + if ((md = digest_availability_md(digp)) == NULL) return RAISE_NOTSUP(env); values[0] = enif_make_int(env, EVP_MD_type(md)); values[1] = enif_make_int(env, EVP_MD_size(md)); values[2] = enif_make_int(env, EVP_MD_block_size(md)); - ok = enif_make_map_from_arrays(env, keys, values, 3, &ret); + + int ok = enif_make_map_from_arrays(env, keys, values, 3, &ret); ASSERT(ok); (void)ok; return ret; } ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Data) */ - struct digest_availability_t *digp = NULL; const EVP_MD *md; ErlNifBinary data; ERL_NIF_TERM ret; unsigned ret_size; unsigned char *outp; - if ((digp = get_digest_type(argv[0])) == NULL) + struct digest_availability_Cptr digp = get_digest_type(argv[0]); + if (digp.ptr == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) return EXCP_NOTSUP_N(env, 0, "Bad digest type in FIPS"); - if ((md = digp->md.p) == NULL) + if ((md = digest_availability_md(digp)) == NULL) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in this cryptolib"); if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) @@ -119,19 +119,20 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,4,0) /* Set xoflen for SHAKE digests if needed */ - if (digp->xof_default_length) { + unsigned xof_default_length = digest_availability_xof_default_length(digp); + if (xof_default_length) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); OSSL_PARAM params[2]; if (!ctx) { return EXCP_ERROR(env, "EVP_MD_CTX_new failed"); } - params[0] = OSSL_PARAM_construct_uint("xoflen", &digp->xof_default_length); + params[0] = OSSL_PARAM_construct_uint("xoflen", &xof_default_length); params[1] = OSSL_PARAM_construct_end(); if (EVP_DigestInit_ex2(ctx, md, params) != 1) { assign_goto(ret, done, EXCP_ERROR(env, "EVP_DigestInit failed")); } - ret_size = digp->xof_default_length; + ret_size = xof_default_length; if ((outp = enif_make_new_binary(env, ret_size, &ret)) == NULL) { assign_goto(ret, done, EXCP_ERROR(env, "Can't allocate binary")); } @@ -167,23 +168,23 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type) */ - struct digest_availability_t *digp = NULL; struct evp_md_ctx *ctx = NULL; ERL_NIF_TERM ret; - if ((digp = get_digest_type(argv[0])) == NULL) + struct digest_availability_Cptr digp = get_digest_type(argv[0]); + if (digp.ptr == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in FIPS"); - if (digp->md.p == NULL) + if (digest_availability_md(digp) == NULL) return EXCP_NOTSUP_N(env, 0, "Unsupported digest type"); if ((ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate nif resource"); if ((ctx->ctx = EVP_MD_CTX_new()) == NULL) assign_goto(ret, done, EXCP_ERROR(env, "Low-level call EVP_MD_CTX_new failed")); - if (EVP_DigestInit(ctx->ctx, digp->md.p) != 1) + if (EVP_DigestInit(ctx->ctx, digest_availability_md(digp)) != 1) assign_goto(ret, done, EXCP_ERROR(env, "Low-level call EVP_DigestInit failed")); #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,4,0) @@ -191,9 +192,10 @@ ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * The default digest length for shake128 and shake256 was removed * in OpenSSL 3.4, so we set them to be backward compatible with ourself. */ - if (digp->xof_default_length) { + unsigned xof_default_length = digest_availability_xof_default_length(digp); + if (xof_default_length) { OSSL_PARAM params[2]; - params[0] = OSSL_PARAM_construct_uint("xoflen", &digp->xof_default_length); + params[0] = OSSL_PARAM_construct_uint("xoflen", &xof_default_length); params[1] = OSSL_PARAM_construct_end(); if (!EVP_MD_CTX_set_params(ctx->ctx, params)) { assign_goto(ret, done, EXCP_ERROR(env, "Can't set param xoflen")); diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 9832947da749..06975771239e 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -20,8 +20,9 @@ * %CopyrightEnd% */ -#include "pkey.h" #include "algorithms_digest.h" + +#include "pkey.h" #include "bn.h" #include "dss.h" #include "ec.h" @@ -156,11 +157,8 @@ static int check_pkey_algorithm_type(ErlNifEnv *env, } -static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, - int type_arg_num, ERL_NIF_TERM type, - const EVP_MD **md, - ERL_NIF_TERM *err_return) -{ + static int get_pkey_digest_type(ErlNifEnv* env, ERL_NIF_TERM algorithm, const int type_arg_num, ERL_NIF_TERM type, + const EVP_MD** md, ERL_NIF_TERM* err_return) { struct digest_availability_t *digp = NULL; *md = NULL; @@ -185,13 +183,13 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, if ((digp = get_digest_type(type)) == NULL) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Bad digest type")); - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Digest type forbidden in FIPS")); - if (digp->md.p == NULL) + if (get_digest_availability_field_md(digp) == NULL) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Digest type not supported")); - *md = digp->md.p; + *md = get_digest_availability_field_md(digp); return 1; notsup: @@ -203,7 +201,7 @@ static int get_pkey_sign_digest(ErlNifEnv *env, int algorithm_arg_num, int type_arg_num, int data_arg_num, unsigned char *md_value, const EVP_MD **mdp, unsigned char **tbsp, size_t *tbslenp, - ERL_NIF_TERM *err_return) + ERL_NIF_TERM * err_return) { int ret; const ERL_NIF_TERM *tpl_terms; From 9e88a4077fe42006ec12e3f9f87e5978456a0398 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 12 Nov 2025 05:29:37 +0100 Subject: [PATCH 14/15] RSA opts and fix errors/warnings accessing C api for digests --- lib/crypto/c_src/Makefile.in | 1 + lib/crypto/c_src/algorithms.c | 65 +-------------------- lib/crypto/c_src/algorithms_digest.cpp | 23 +++++++- lib/crypto/c_src/algorithms_digest.h | 2 + lib/crypto/c_src/algorithms_kem.h | 5 +- lib/crypto/c_src/algorithms_rsaopt.cpp | 78 ++++++++++++++++++++++++++ lib/crypto/c_src/algorithms_rsaopt.h | 77 +++++++++++++++++++++++++ lib/crypto/c_src/hash.c | 23 ++++---- lib/crypto/c_src/mac.c | 25 ++++----- lib/crypto/c_src/pbkdf2_hmac.c | 10 ++-- 10 files changed, 213 insertions(+), 96 deletions(-) create mode 100644 lib/crypto/c_src/algorithms_rsaopt.cpp create mode 100644 lib/crypto/c_src/algorithms_rsaopt.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 5067673000c2..b7bee694c3f2 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -96,6 +96,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/algorithms_pubkey$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_curve$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_kem$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_rsaopt$(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 55f076b0eb6a..1112ae7205c3 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -31,20 +31,14 @@ static size_t algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ -void init_rsa_opts_types(ErlNifEnv* env); void init_algorithms_types(ErlNifEnv* env) { init_mac_types(env); init_cipher_types(env); - init_rsa_opts_types(env); /* ciphers and macs are initiated statically */ } -/*================================================================ - Hash algorithms -*/ - ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { digest_types_lazy_init(env, FIPS_MODE()); @@ -54,31 +48,20 @@ ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { pubkey_algorithms_lazy_init(env, FIPS_MODE()); - // Filter the results by IS_PUBKEY_FORBIDDEN_IN_FIPS() == false return pubkey_algorithms_as_list(env, false); } ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + kem_algorithms_lazy_init(env, FIPS_MODE()); return kem_algorithms_as_list(env, false); } - -/*================================================================ - Cipher key algorithms -*/ - ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - /* Exclude old API ciphers. Filter the results by IS_CIPHER_FORBIDDEN_IN_FIPS() == false */ return cipher_types_as_list(env, false); } - -/*================================================================ - MAC key algorithms -*/ - ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { return mac_types_as_list(env, false); @@ -90,61 +73,20 @@ ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return curve_algorithms_as_list(env, false); } -/*================================================================ - RSA Options -*/ - ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { const size_t cnt = FIPS_MODE() ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; return enif_make_list_from_array(env, algo_rsa_opts, cnt); } -void init_rsa_opts_types(ErlNifEnv* env) { - // Validated algorithms first - algo_rsa_opts_cnt = 0; -#ifdef HAS_EVP_PKEY_CTX -# ifdef HAVE_RSA_PKCS1_PSS_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_pss_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pss_saltlen"); -# endif -# ifdef HAVE_RSA_MGF1_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_mgf1_md"); -# endif -# ifdef HAVE_RSA_OAEP_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_oaep_padding"); -# endif -# ifdef HAVE_RSA_OAEP_MD - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_label"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_oaep_md"); -# endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"signature_md"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_pkcs1_padding"); - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_x931_padding"); -#ifdef HAVE_RSA_SSLV23_PADDING - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_sslv23_padding"); -#endif - algo_rsa_opts[algo_rsa_opts_cnt++] = enif_make_atom(env,"rsa_no_padding"); - algo_rsa_opts_fips_cnt = algo_rsa_opts_cnt; - - ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(algo_rsa_opts[0])); -} - ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { digest_types_lazy_init(env, FIPS_MODE()); - // Filter the results by the result of algorithm.is_forbidden_in_fips() == true return digest_types_as_list(env, true); } ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef FIPS_SUPPORT pubkey_algorithms_lazy_init(env, 1); - // Filter the results by the result of algorithm.is_forbidden_in_fips() == true return pubkey_algorithms_as_list(env, true); -#else - return enif_make_list(env, 0); // nothing is forbidden -#endif } ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -157,11 +99,8 @@ ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ER } ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef FIPS_SUPPORT + kem_algorithms_lazy_init(env, FIPS_MODE()); return kem_algorithms_as_list(env, true); -#else - return enif_make_list(env, 0); /* nothing is forbidden */ -#endif } ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index 2718b08ab6cc..3ca3c27ca8b4 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -159,7 +159,16 @@ extern "C" bool is_digest_forbidden_in_fips(const digest_availability_Cptr p) { if (p.ptr == nullptr) { return true; // "forbidden" when there's no digest } - return static_cast(p.ptr)->is_forbidden_in_fips(); + const auto algo = static_cast(p.ptr); + return algo->is_forbidden_in_fips(); +} + +extern "C" const char* digest_availability_str_v3(const digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return ""; // "no name" when there's no digest + } + const auto algo = static_cast(p.ptr); + return algo->init->str_v3; } extern "C" const EVP_MD* digest_availability_md(const digest_availability_Cptr p) { @@ -173,5 +182,15 @@ extern "C" size_t digest_availability_xof_default_length(const digest_availabili if (p.ptr == nullptr) { return 0; // "no xof default length" when there's no digest } - return static_cast(p.ptr)->xof_default_length; + const auto algo = static_cast(p.ptr); + return algo->xof_default_length; } + +extern "C" int is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return 0; // "no digest" is not eligible + } + const auto algo = static_cast(p.ptr); + const auto eligible = algo->flags.pbkdf2_eligible; + return eligible ? 1 : 0; +} \ No newline at end of file diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index cdd37eca84c1..243f4710f987 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -49,6 +49,8 @@ struct digest_availability_Cptr get_digest_type(ERL_NIF_TERM type); // linear lo bool is_digest_forbidden_in_fips(struct digest_availability_Cptr p); // access C++ member from C const EVP_MD* digest_availability_md(struct digest_availability_Cptr p); // access field size_t digest_availability_xof_default_length(struct digest_availability_Cptr p); // access field +const char* digest_availability_str_v3(struct digest_availability_Cptr p); // access str_v3 name (field of probe) +int is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p); // check PBKDF2 availability bit #ifdef __cplusplus } diff --git a/lib/crypto/c_src/algorithms_kem.h b/lib/crypto/c_src/algorithms_kem.h index b0e31b43fbdb..186862de3fa5 100644 --- a/lib/crypto/c_src/algorithms_kem.h +++ b/lib/crypto/c_src/algorithms_kem.h @@ -34,7 +34,6 @@ extern "C" { // size_t kem_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); -void kem_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailable, ERL_NIF_TERM atom); #ifdef __cplusplus } @@ -44,7 +43,7 @@ void kem_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailable, struct kem_probe_t; struct kem_availability_t { - const kem_probe_t* init = nullptr; // the pubkey_probe_t used to create this record + const kem_probe_t* init = nullptr; // the rsaopt_probe_t used to create this record struct { bool fips_forbidden : 1; @@ -66,7 +65,7 @@ struct kem_availability_t { // its availability. Each probe() call done by the algorithm_collection_t might or might not // result in a new available algorithm creation. struct kem_probe_t { - const char* str_v3 = nullptr; // if this is nullptr, .str will be used instead + const char* str_v3 = nullptr; ERL_NIF_TERM atom = 0; // Perform a probe on the algorithm. In case of success, fill the struct and push into the 'output' diff --git a/lib/crypto/c_src/algorithms_rsaopt.cpp b/lib/crypto/c_src/algorithms_rsaopt.cpp new file mode 100644 index 000000000000..a4ce4adcf437 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.cpp @@ -0,0 +1,78 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_rsaopt.h" +#include "algorithms_collection.h" + +rsaopt_probe_t rsaopt_probes[] = { +#ifdef HAS_EVP_PKEY_CTX +#ifdef HAVE_RSA_PKCS1_PSS_PADDING + {.str_v3 = "rsa_pkcs1_pss_padding"}, + {.str_v3 = "rsa_pss_saltlen"}, +#endif +#ifdef HAVE_RSA_MGF1_MD + {.str_v3 = "rsa_mgf1_md"}, +#endif +#ifdef HAVE_RSA_OAEP_PADDING + {.str_v3 = "rsa_pkcs1_oaep_padding"}, +#endif +#ifdef HAVE_RSA_OAEP_MD + {.str_v3 = "rsa_oaep_label"}, + {.str_v3 = "rsa_oaep_md"}, +#endif + {.str_v3 = "signature_md"}, +#endif + {.str_v3 = "rsa_pkcs1_padding"}, + {.str_v3 = "rsa_x931_padding"}, +#ifdef HAVE_RSA_SSLV23_PADDING + {.str_v3 = "rsa_sslv23_padding"}, +#endif + {.str_v3 = "rsa_no_padding"}, +}; + +rsaopt_collection_t rsaopt_collection("crypto.rsaopt_collection", rsaopt_probes, + sizeof(rsaopt_probes) / sizeof(rsaopt_probes[0])); + +// +// Implementation of Known RSA Options storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" size_t rsaopts_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + return rsaopt_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM rsaopt_as_list(ErlNifEnv* env, const bool fips_enabled) { + return rsaopt_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM rsaopt_availability_t::get_atom() const { return this->init->atom; } + +// for FIPS we will attempt to initialize the pubkey context to verify whether the +// algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. +void rsaopt_probe_t::probe(ErlNifEnv* env, const bool fips_enabled, std::vector& output) { + this->atom = create_or_existing_atom(env, this->str_v3, this->atom); + const rsaopt_availability_t algo = {.init = this}; + // No extra checks, just convert name to atom and add + return output.push_back(algo); +} diff --git a/lib/crypto/c_src/algorithms_rsaopt.h b/lib/crypto/c_src/algorithms_rsaopt.h new file mode 100644 index 000000000000..0c4f9035ea98 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.h @@ -0,0 +1,77 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once +#include "algorithms_collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// +// Supported RSA Options storage C API +// +size_t rsaopts_lazy_init(ErlNifEnv* env, bool fips_enabled); +ERL_NIF_TERM rsaopts_as_list(ErlNifEnv* env, bool fips_enabled); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +struct rsaopt_probe_t; + +struct rsaopt_availability_t { + const rsaopt_probe_t* init = nullptr; // the rsaopt_probe_t used to create this record + + struct { + bool fips_forbidden : 1; + } flags = {}; + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +#else + return false; +#endif + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; +}; + +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. +struct rsaopt_probe_t { + const char* str_v3 = nullptr; + ERL_NIF_TERM atom = 0; + + // Attempt to add a new known RSA option. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); +}; + +using rsaopt_collection_t = algorithm_collection_t; +extern rsaopt_collection_t rsaopt_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index 6044f5a146b3..6c8ed17e7d27 100644 --- a/lib/crypto/c_src/hash.c +++ b/lib/crypto/c_src/hash.c @@ -80,21 +80,24 @@ ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(argc == 1); - struct digest_availability_Cptr digp = get_digest_type(argv[0]); - if (digp.ptr == NULL) - return enif_make_badarg(env); - if (is_digest_forbidden_in_fips(digp)) - return RAISE_NOTSUP(env); - - if ((md = digest_availability_md(digp)) == NULL) - return RAISE_NOTSUP(env); + { + struct digest_availability_Cptr digp = get_digest_type(argv[0]); + if (digp.ptr == NULL) + return enif_make_badarg(env); + if (is_digest_forbidden_in_fips(digp)) + return RAISE_NOTSUP(env); + if ((md = digest_availability_md(digp)) == NULL) + return RAISE_NOTSUP(env); + } values[0] = enif_make_int(env, EVP_MD_type(md)); values[1] = enif_make_int(env, EVP_MD_size(md)); values[2] = enif_make_int(env, EVP_MD_block_size(md)); - int ok = enif_make_map_from_arrays(env, keys, values, 3, &ret); - ASSERT(ok); (void)ok; + { + int ok = enif_make_map_from_arrays(env, keys, values, 3, &ret); + ASSERT(ok); (void)ok; + } return ret; } diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 04af8bb7c3a1..462a6ad3f63e 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -324,14 +324,14 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ********/ case HMAC_mac: { - struct digest_availability_t *digp; + struct digest_availability_Cptr digp = get_digest_type(argv[1]); - if ((digp = get_digest_type(argv[1])) == NULL) + if (digp.ptr == NULL) { return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) { return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; @@ -339,15 +339,15 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if defined(HAS_3_0_API) name = "HMAC"; - subalg = digp->str_v3; + subalg = digest_availability_str_v3(digp); #else /* Old style */ - if (digp->md.p == NULL) + if (digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + md = digest_availability_md(digp); # if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_HMAC) # ifdef HAVE_PKEY_new_raw_private_key /* Preferred for new applications according to EVP_PKEY_new_mac_key(3) */ @@ -676,27 +676,26 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ********/ case HMAC_mac: { - struct digest_availability_t *digp; - - if ((digp = get_digest_type(argv[1])) == NULL) + struct digest_availability_Cptr digp = get_digest_type(argv[1]); + if (digp.ptr == NULL) { return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC"); goto err; } - if (IS_DIGEST_FORBIDDEN_IN_FIPS(digp)) + if (is_digest_forbidden_in_fips(digp)) { return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS"); goto err; } # if defined(HAS_3_0_API) - digest = digp->str_v3; + digest = digest_availability_str_v3(digp); # else - if (digp->md.p == NULL) + if (digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + md = digest_availability_md(digp); # ifdef HAVE_PKEY_new_raw_private_key /* Preferred for new applications according to EVP_PKEY_new_mac_key(3) */ diff --git a/lib/crypto/c_src/pbkdf2_hmac.c b/lib/crypto/c_src/pbkdf2_hmac.c index 1b3ba5e1bd5b..4d0e505faa84 100644 --- a/lib/crypto/c_src/pbkdf2_hmac.c +++ b/lib/crypto/c_src/pbkdf2_hmac.c @@ -30,13 +30,13 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, { ErlNifBinary pass, salt, out; ErlNifUInt64 iter, keylen; - struct digest_availability_t* digp = NULL; - if ((digp = get_digest_type(argv[0])) == NULL) + struct digest_availability_Cptr digp = get_digest_type(argv[0]); + if (digp.ptr == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (digp->md.p == NULL) + if (digest_availability_md(digp) == NULL) return EXCP_BADARG_N(env, 0, "md.p is not NULL"); - if ((digp->flags & PBKDF2_ELIGIBLE_DIGEST) == 0) + if (is_digest_eligible_for_pbkdf2(digp)) return EXCP_BADARG_N(env, 0, "Not eligible digest type"); if (!enif_inspect_binary(env, argv[1], &pass)) @@ -57,7 +57,7 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, if (!PKCS5_PBKDF2_HMAC((const char *)pass.data, pass.size, salt.data, salt.size, iter, - digp->md.p, + digest_availability_md(digp), keylen, out.data)) { enif_release_binary(&out); return EXCP_ERROR(env, "Low-level call failed"); From 4d2feb7c528f43ebf3830c30b76790021fc0f3d6 Mon Sep 17 00:00:00 2001 From: Dmytro Lytovchenko Date: Wed, 12 Nov 2025 17:54:49 +0100 Subject: [PATCH 15/15] rsa opts and macs added, ciphers placeholder added --- lib/crypto/c_src/Makefile.in | 3 + lib/crypto/c_src/algorithms.c | 67 ++--- lib/crypto/c_src/algorithms.h | 2 - lib/crypto/c_src/algorithms_cipher.cpp | 52 ++++ lib/crypto/c_src/algorithms_cipher.h | 82 ++++++ lib/crypto/c_src/algorithms_collection.cpp | 18 +- lib/crypto/c_src/algorithms_collection.h | 29 +- lib/crypto/c_src/algorithms_curve.cpp | 27 +- lib/crypto/c_src/algorithms_curve.h | 12 +- lib/crypto/c_src/algorithms_digest.cpp | 14 +- lib/crypto/c_src/algorithms_digest.h | 12 +- lib/crypto/c_src/algorithms_kem.cpp | 3 +- lib/crypto/c_src/algorithms_kem.h | 6 +- lib/crypto/c_src/algorithms_mac.cpp | 159 +++++++++++ lib/crypto/c_src/algorithms_mac.h | 114 ++++++++ lib/crypto/c_src/algorithms_pubkey.cpp | 5 +- lib/crypto/c_src/algorithms_pubkey.h | 10 +- lib/crypto/c_src/algorithms_rsaopt.cpp | 1 - lib/crypto/c_src/algorithms_rsaopt.h | 6 +- lib/crypto/c_src/auto_openssl_resource.cpp | 62 +++++ lib/crypto/c_src/auto_openssl_resource.h | 93 +++++++ lib/crypto/c_src/crypto.c | 2 - lib/crypto/c_src/crypto_openssl_resource.h | 85 ------ lib/crypto/c_src/hash.c | 12 +- lib/crypto/c_src/mac.c | 299 ++++----------------- lib/crypto/c_src/mac.h | 4 - lib/crypto/c_src/pbkdf2_hmac.c | 4 +- lib/crypto/c_src/pkey.c | 11 +- 28 files changed, 744 insertions(+), 450 deletions(-) create mode 100644 lib/crypto/c_src/algorithms_cipher.cpp create mode 100644 lib/crypto/c_src/algorithms_cipher.h create mode 100644 lib/crypto/c_src/algorithms_mac.cpp create mode 100644 lib/crypto/c_src/algorithms_mac.h create mode 100644 lib/crypto/c_src/auto_openssl_resource.cpp create mode 100644 lib/crypto/c_src/auto_openssl_resource.h delete mode 100644 lib/crypto/c_src/crypto_openssl_resource.h diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index b7bee694c3f2..a86d1d3aa8e4 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -97,6 +97,9 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/algorithms_curve$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_kem$(TYPEMARKER).cpp.o \ $(OBJDIR)/algorithms_rsaopt$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_mac$(TYPEMARKER).cpp.o \ + $(OBJDIR)/algorithms_cipher$(TYPEMARKER).cpp.o \ + $(OBJDIR)/auto_openssl_resource(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 1112ae7205c3..2188c8b30190 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -21,64 +21,60 @@ */ #include "algorithms.h" -#include "algorithms_pubkey.h" -#include "algorithms_digest.h" -#include "algorithms_curve.h" -#include "algorithms_kem.h" #include "cipher.h" #include "common.h" #include "mac.h" -static size_t algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; -static ERL_NIF_TERM algo_rsa_opts[11]; /* increase when extending the list */ +#include "algorithms_cipher.h" +#include "algorithms_curve.h" +#include "algorithms_digest.h" +#include "algorithms_kem.h" +#include "algorithms_mac.h" +#include "algorithms_pubkey.h" -void init_algorithms_types(ErlNifEnv* env) -{ - init_mac_types(env); - init_cipher_types(env); - /* ciphers and macs are initiated statically */ -} +// +// Supported Algorithms (filter on fips_forbidden == false) +// -ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { digest_types_lazy_init(env, FIPS_MODE()); return digest_types_as_list(env, false); } -ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { pubkey_algorithms_lazy_init(env, FIPS_MODE()); return pubkey_algorithms_as_list(env, false); } -ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +ERL_NIF_TERM kem_algorithms_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { kem_algorithms_lazy_init(env, FIPS_MODE()); return kem_algorithms_as_list(env, false); } -ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + cipher_algorithms_lazy_init(env, FIPS_MODE()); return cipher_types_as_list(env, false); } -ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - return mac_types_as_list(env, false); +ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + mac_algorithms_lazy_init(env, FIPS_MODE()); + return mac_algorithms_as_list(env, false); } -ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ +ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { curve_algorithms_lazy_init(env, FIPS_MODE()); return curve_algorithms_as_list(env, false); } -ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - const size_t cnt = FIPS_MODE() ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt; - return enif_make_list_from_array(env, algo_rsa_opts, cnt); +ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + mac_algorithms_lazy_init(env, FIPS_MODE()); + return mac_algorithms_as_list(env, false); } +// +// Forbidden Algorithms (filter on fips_forbidden == true) +// + ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { digest_types_lazy_init(env, FIPS_MODE()); return digest_types_as_list(env, true); @@ -90,12 +86,8 @@ ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ER } ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef FIPS_SUPPORT - // Filter the results by the result of algorithm.is_forbidden_in_fips() == true + cipher_algorithms_lazy_init(env, FIPS_MODE()); return cipher_types_as_list(env, true); -#else - return enif_make_list(env, 0); // nothing is forbidden -#endif } ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -104,11 +96,8 @@ ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_N } ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef FIPS_SUPPORT - return mac_types_as_list(env, true); -#else - return enif_make_list(env, 0); /* not forbidden */ -#endif + mac_algorithms_lazy_init(env, FIPS_MODE()); + return mac_algorithms_as_list(env, true); } ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h index b47f98bfbea0..5c296a5470c1 100644 --- a/lib/crypto/c_src/algorithms.h +++ b/lib/crypto/c_src/algorithms.h @@ -25,8 +25,6 @@ #include "common.h" -void init_algorithms_types(ErlNifEnv* env); - ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM fips_forbidden_hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/algorithms_cipher.cpp b/lib/crypto/c_src/algorithms_cipher.cpp new file mode 100644 index 000000000000..637f39448ee1 --- /dev/null +++ b/lib/crypto/c_src/algorithms_cipher.cpp @@ -0,0 +1,52 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_cipher.h" + +cipher_probe_t cipher_probes[] = {}; + +cipher_collection_t cipher_collection("crypto.cipher_collection", cipher_probes, sizeof(cipher_probes) / sizeof(cipher_probes[0])); + +// +// Implementation of Known Cipher Algorithms storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" size_t cipher_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + return cipher_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM cipher_algorithms_as_list(ErlNifEnv* env, const bool fips_enabled) { + return cipher_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM cipher_availability_t::get_atom() const { return this->init->atom; } + +// for FIPS we will attempt to initialize the pubkey context to verify whether the +// algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. +void cipher_probe_t::probe(ErlNifEnv* env, const bool fips_enabled, std::vector& output) { + this->atom = create_or_existing_atom(env, this->str_v3, this->atom); + const cipher_availability_t algo = {.init = this}; + // No extra checks, just convert name to atom and add + return output.push_back(algo); +} diff --git a/lib/crypto/c_src/algorithms_cipher.h b/lib/crypto/c_src/algorithms_cipher.h new file mode 100644 index 000000000000..c0178a7506a4 --- /dev/null +++ b/lib/crypto/c_src/algorithms_cipher.h @@ -0,0 +1,82 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// +// Supported Cipher Algorithms storage C API +// +size_t cipher_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); +ERL_NIF_TERM cipher_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +#include "algorithms_collection.h" + +struct cipher_probe_t; + +// Describes a cipher algorithm added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. +struct cipher_availability_t { + const cipher_probe_t* init = nullptr; // the cipher_probe_t used to create this record + + struct { + bool fips_forbidden : 1; + } flags = {}; + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +#else + return false; +#endif + } + bool is_available() const { return true; } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; +}; + +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. +struct cipher_probe_t { + const char* str_v3 = nullptr; + ERL_NIF_TERM atom = 0; + + // Attempt to add a new known Cipher algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); +}; + +using cipher_collection_t = algorithm_collection_t; +extern cipher_collection_t cipher_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_collection.cpp b/lib/crypto/c_src/algorithms_collection.cpp index 83bf8a4e17d6..6d8be0e1470c 100644 --- a/lib/crypto/c_src/algorithms_collection.cpp +++ b/lib/crypto/c_src/algorithms_collection.cpp @@ -21,28 +21,42 @@ */ #include "algorithms_collection.h" +#include "algorithms_cipher.h" #include "algorithms_curve.h" #include "algorithms_digest.h" +#include "algorithms_kem.h" +#include "algorithms_mac.h" #include "algorithms_pubkey.h" +#include "algorithms_rsaopt.h" extern "C" bool create_algorithm_mutexes() { - return pubkey_collection.create_mutex() && curve_collection.create_mutex() && digest_collection.create_mutex(); + return pubkey_collection.create_mutex() && curve_collection.create_mutex() && digest_collection.create_mutex() && + kem_collection.create_mutex() && rsaopt_collection.create_mutex() && mac_collection.create_mutex() && + cipher_collection.create_mutex(); } extern "C" void free_algorithm_mutexes(void) { pubkey_collection.destroy_mutex(); curve_collection.destroy_mutex(); digest_collection.destroy_mutex(); + kem_collection.destroy_mutex(); + rsaopt_collection.destroy_mutex(); + mac_collection.destroy_mutex(); + cipher_collection.destroy_mutex(); } extern "C" void algorithms_reset_cache() { pubkey_collection.reset(); curve_collection.reset(); digest_collection.reset(); + kem_collection.reset(); + rsaopt_collection.reset(); + mac_collection.reset(); + cipher_collection.reset(); } // Ensure atoms are not created repeatedly. Pass atom=0 to attempt creating an existing atom (then a new atom). -ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom) { +ERL_NIF_TERM create_or_existing_atom(ErlNifEnv* env, const char* atom_name, ERL_NIF_TERM atom) { if (!atom) { enif_make_existing_atom(env, atom_name, &atom, ERL_NIF_UTF8); if (!atom) { diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h index b0c39d349b3a..74733a47bfb2 100644 --- a/lib/crypto/c_src/algorithms_collection.h +++ b/lib/crypto/c_src/algorithms_collection.h @@ -25,10 +25,11 @@ #ifdef __cplusplus extern "C" { #endif + #include "common.h" // -// C API which affect all collections at once +// C API which affects all collections at once // // Creates protective mutex for each collection to allow for safe lazy init and reinit @@ -52,9 +53,9 @@ void algorithms_reset_cache(void); // ... protected code // } <- auto released here struct mutex_lock_and_auto_release { - ErlNifMutex *mutex; + ErlNifMutex* mutex; - explicit mutex_lock_and_auto_release(ErlNifMutex *m) : mutex(m) { enif_mutex_lock(m); } + explicit mutex_lock_and_auto_release(ErlNifMutex* m) : mutex(m) { enif_mutex_lock(m); } ~mutex_lock_and_auto_release() { enif_mutex_unlock(mutex); } }; @@ -64,18 +65,18 @@ struct mutex_lock_and_auto_release { // // The collections for all types of algorithms are statically created before the crypto // library is initialized, but the mutex must be additionally constructed (call only once). -template +template struct algorithm_collection_t { private: bool lazy_init_done; // each probe is executed every time we reset and repopulate algorithms list. Probes are not const, because // their implementations might want to cache something like found existing atoms by string - ProbeT *probes; + ProbeT* probes; const size_t probe_count; // contains detected and supported algorithms std::vector algorithms; - ErlNifMutex *mutex; - const char *debug_name; + ErlNifMutex* mutex; + const char* debug_name; public: explicit algorithm_collection_t(const char* debug_name, ProbeT* probes_, const size_t probe_count_) : @@ -94,7 +95,7 @@ struct algorithm_collection_t { auto end() { return this->algorithms.end(); } bool create_mutex() { - this->mutex = enif_mutex_create(const_cast(debug_name)); + this->mutex = enif_mutex_create(const_cast(debug_name)); return this->mutex != nullptr; } @@ -113,7 +114,7 @@ struct algorithm_collection_t { } // Checks whether the init has already been done for the array, otherwise will invoke init_fn - size_t lazy_init(ErlNifEnv *env, const bool fips_enabled) { + size_t lazy_init(ErlNifEnv* env, const bool fips_enabled) { size_t result = 0; if (this->lazy_init_done) { return this->algorithms.size(); @@ -133,12 +134,12 @@ struct algorithm_collection_t { return result; } - ERL_NIF_TERM to_list(ErlNifEnv *env, const bool fips_forbidden) const { + ERL_NIF_TERM to_list(ErlNifEnv* env, const bool fips_forbidden) const { ERL_NIF_TERM hd = enif_make_list(env, 0); - for (const auto &algo: this->algorithms) { + for (const auto& algo : this->algorithms) { // Any of the forbidden flags is not set, then something is available - if (algo.is_forbidden_in_fips() == fips_forbidden) { + if (algo.is_available() && algo.is_forbidden_in_fips() == fips_forbidden) { hd = enif_make_list_cell(env, algo.get_atom(), hd); } } @@ -146,7 +147,7 @@ struct algorithm_collection_t { } }; -// Ensure atoms are not created repeatedly -ERL_NIF_TERM create_or_existing_atom(ErlNifEnv *env, const char *atom_name, ERL_NIF_TERM atom = 0); +// Helper: Ensure atoms are not created repeatedly +ERL_NIF_TERM create_or_existing_atom(ErlNifEnv* env, const char* atom_name, ERL_NIF_TERM atom = 0); #endif diff --git a/lib/crypto/c_src/algorithms_curve.cpp b/lib/crypto/c_src/algorithms_curve.cpp index 6fd53fb59c51..d64ee14a502f 100644 --- a/lib/crypto/c_src/algorithms_curve.cpp +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -20,9 +20,12 @@ * %CopyrightEnd% */ +extern "C" { #include +} + #include "algorithms_curve.h" -#include "crypto_openssl_resource.h" +#include "auto_openssl_resource.h" curve_probe_t curve_probes[] = { #if defined(HAVE_EC) @@ -374,7 +377,7 @@ curve_probe_t curve_probes[] = { }; curve_collection_t curve_collection("crypto.curve_collection", curve_probes, - sizeof(curve_probes)/sizeof(curve_probes[0])); + sizeof(curve_probes) / sizeof(curve_probes[0])); // // Implementation of Curve Algorithm storage API @@ -400,10 +403,10 @@ bool curve_probe_t::is_curve_valid_by_nid() { #ifdef HAVE_EC #if defined(HAVE_DH) #if defined(HAS_EVP_PKEY_CTX) && (!DISABLE_EVP_DH) - auto_evp_pkey_t pkey(nullptr); - auto_evp_pkey_t params(nullptr); + auto_evp_pkey_t pkey; + auto_evp_pkey_t params; - auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); + const auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); if (!pctx) return false; if (1 != EVP_PKEY_paramgen_init(pctx.pointer)) @@ -413,7 +416,7 @@ bool curve_probe_t::is_curve_valid_by_nid() { if (!EVP_PKEY_paramgen(pctx.pointer, ¶ms.pointer)) return false; - auto_evp_pkey_ctx_t kctx(EVP_PKEY_CTX_new(params.pointer, nullptr)); + const auto_evp_pkey_ctx_t kctx(EVP_PKEY_CTX_new(params.pointer, nullptr)); if (!kctx) return false; @@ -463,24 +466,24 @@ void curve_availability_t::probe_under_fips(bool fips_mode) { OSSL_PARAM params[2]; - auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", "fips=yes")); + const auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", "fips=yes")); if (!pctx) { - this->flags.curve_init_failed = true; - return; /* EC keygen context not available */ + this->flags.algorithm_init_failed = true; + return; // EC keygen context not available } if (EVP_PKEY_keygen_init(pctx.pointer) <= 0) { - this->flags.curve_init_failed = true; + this->flags.algorithm_init_failed = true; return; } params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, const_cast(this->init->sn), 0); params[1] = OSSL_PARAM_construct_end(); if (EVP_PKEY_CTX_set_params(pctx.pointer, params) <= 0) { - this->flags.curve_init_failed = true; + this->flags.algorithm_init_failed = true; return; } auto_evp_pkey_t pkey(nullptr); if (EVP_PKEY_generate(pctx.pointer, &pkey.pointer) <= 0) { - this->flags.curve_init_failed = true; + this->flags.algorithm_init_failed = true; } #endif } diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h index 3efd486dd355..46c61a43ec5f 100644 --- a/lib/crypto/c_src/algorithms_curve.h +++ b/lib/crypto/c_src/algorithms_curve.h @@ -21,7 +21,6 @@ */ #pragma once -#include "algorithms_collection.h" #ifdef __cplusplus extern "C" { @@ -40,23 +39,28 @@ ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); #endif #ifdef __cplusplus +#include "algorithms_collection.h" struct curve_probe_t; +// Describes a curve algorithm added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. struct curve_availability_t { const curve_probe_t *init = nullptr; // the probe which created this record, contains name, atom, etc. struct { bool fips_forbidden: 1; - bool curve_init_failed: 1; // not possible to create with fips=yes + bool algorithm_init_failed: 1; // not possible to create with fips=yes } flags = {}; bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT // Available if not forbidden with fips=yes, and if curve init did not fail - return (this->flags.fips_forbidden || this->flags.curve_init_failed) && FIPS_MODE(); + return (this->flags.fips_forbidden || this->flags.algorithm_init_failed) && FIPS_MODE(); #else return false; #endif } + bool is_available() const { return !this->flags.algorithm_init_failed; } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; // Instantiate the algorithm (if FIPS is enabled) and set flags if not available @@ -67,7 +71,7 @@ struct curve_availability_t { // its availability. Each probe() call done by the algorithm_collection_t might or might not // result in a new available algorithm creation. struct curve_probe_t { - size_t nid = 0; // NID_xxxx value of OpenSSL + int nid = 0; // NID_xxxx value (an OpenSSL macro) const char *sn = nullptr; // serves as Erlang atom name, also equal to SN_xxxxx macro of OpenSSL ERL_NIF_TERM atom = 0; // Atom for this->sn is cached here diff --git a/lib/crypto/c_src/algorithms_digest.cpp b/lib/crypto/c_src/algorithms_digest.cpp index 3ca3c27ca8b4..6f76edaf1b31 100644 --- a/lib/crypto/c_src/algorithms_digest.cpp +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -21,7 +21,6 @@ */ #include "algorithms_digest.h" -#include "algorithms_collection.h" static digest_probe_t digest_probes[] = { #ifdef HAVE_MD4 @@ -163,7 +162,7 @@ extern "C" bool is_digest_forbidden_in_fips(const digest_availability_Cptr p) { return algo->is_forbidden_in_fips(); } -extern "C" const char* digest_availability_str_v3(const digest_availability_Cptr p) { +extern "C" const char* get_digest_availability_str_v3(const digest_availability_Cptr p) { if (p.ptr == nullptr) { return ""; // "no name" when there's no digest } @@ -171,14 +170,14 @@ extern "C" const char* digest_availability_str_v3(const digest_availability_Cptr return algo->init->str_v3; } -extern "C" const EVP_MD* digest_availability_md(const digest_availability_Cptr p) { +extern "C" const EVP_MD* get_digest_availability_md(const digest_availability_Cptr p) { if (p.ptr == nullptr) { return nullptr; // "no md resource" when there's no digest } return static_cast(p.ptr)->md; } -extern "C" size_t digest_availability_xof_default_length(const digest_availability_Cptr p) { +extern "C" size_t get_digest_availability_xof_default_length(const digest_availability_Cptr p) { if (p.ptr == nullptr) { return 0; // "no xof default length" when there's no digest } @@ -186,11 +185,10 @@ extern "C" size_t digest_availability_xof_default_length(const digest_availabili return algo->xof_default_length; } -extern "C" int is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p) { +extern "C" bool is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p) { if (p.ptr == nullptr) { - return 0; // "no digest" is not eligible + return false; // "no digest" is not eligible } const auto algo = static_cast(p.ptr); - const auto eligible = algo->flags.pbkdf2_eligible; - return eligible ? 1 : 0; + return algo->flags.pbkdf2_eligible; } \ No newline at end of file diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h index 243f4710f987..1b0bd00fb686 100644 --- a/lib/crypto/c_src/algorithms_digest.h +++ b/lib/crypto/c_src/algorithms_digest.h @@ -22,8 +22,6 @@ #pragma once -#include "algorithms_collection.h" - struct digest_availability_t; #ifdef __cplusplus @@ -47,16 +45,17 @@ ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env, bool fips_forbidden); // Lookup and access fields struct digest_availability_Cptr get_digest_type(ERL_NIF_TERM type); // linear lookup by atom bool is_digest_forbidden_in_fips(struct digest_availability_Cptr p); // access C++ member from C -const EVP_MD* digest_availability_md(struct digest_availability_Cptr p); // access field -size_t digest_availability_xof_default_length(struct digest_availability_Cptr p); // access field -const char* digest_availability_str_v3(struct digest_availability_Cptr p); // access str_v3 name (field of probe) -int is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p); // check PBKDF2 availability bit +const EVP_MD* get_digest_availability_md(struct digest_availability_Cptr p); // access field +size_t get_digest_availability_xof_default_length(struct digest_availability_Cptr p); // access field +const char* get_digest_availability_str_v3(struct digest_availability_Cptr p); // access str_v3 name (field of probe) +bool is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p); // check PBKDF2 availability bit #ifdef __cplusplus } #endif #ifdef __cplusplus +#include "algorithms_collection.h" // Describes a digest method added by the init function, and checked for compatibility // with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and @@ -82,6 +81,7 @@ struct digest_availability_t { return false; #endif } + bool is_available() const { return true; } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; diff --git a/lib/crypto/c_src/algorithms_kem.cpp b/lib/crypto/c_src/algorithms_kem.cpp index d30387a6a7b6..8e06c31d2e0c 100644 --- a/lib/crypto/c_src/algorithms_kem.cpp +++ b/lib/crypto/c_src/algorithms_kem.cpp @@ -21,8 +21,7 @@ */ #include "algorithms_kem.h" -#include "algorithms_collection.h" -#include "crypto_openssl_resource.h" +#include "auto_openssl_resource.h" kem_probe_t kem_probes[] = { #ifdef HAVE_ML_KEM diff --git a/lib/crypto/c_src/algorithms_kem.h b/lib/crypto/c_src/algorithms_kem.h index 186862de3fa5..5d3be77264c4 100644 --- a/lib/crypto/c_src/algorithms_kem.h +++ b/lib/crypto/c_src/algorithms_kem.h @@ -21,7 +21,6 @@ */ #pragma once -#include "algorithms_collection.h" #ifdef __cplusplus extern "C" { @@ -40,8 +39,12 @@ ERL_NIF_TERM kem_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); #endif #ifdef __cplusplus +#include "algorithms_collection.h" struct kem_probe_t; +// Describes a KEM algorithm added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. struct kem_availability_t { const kem_probe_t* init = nullptr; // the rsaopt_probe_t used to create this record @@ -56,6 +59,7 @@ struct kem_availability_t { return false; #endif } + bool is_available() const { return true; } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; bool check_kem_algorithm(bool fips_enabled); diff --git a/lib/crypto/c_src/algorithms_mac.cpp b/lib/crypto/c_src/algorithms_mac.cpp new file mode 100644 index 000000000000..e5e655810119 --- /dev/null +++ b/lib/crypto/c_src/algorithms_mac.cpp @@ -0,0 +1,159 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_mac.h" + +mac_probe_t mac_probes[] = { + { + .str = "poly1305", + .str_v3 = "POLY1305", + .fips_forbidden_hint = true, +#ifdef HAVE_POLY1305 + // If we have POLY then we have EVP_PKEY + .pkey_type = EVP_PKEY_POLY1305, + .type = POLY1305_mac, + .key_len = 32, +#else + .pkey_type = EVP_PKEY_NONE, +#endif + }, + + {.str = "hmac", + .str_v3 = "HMAC", +#if defined(HAS_EVP_PKEY_CTX) && (!DISABLE_EVP_HMAC) + .pkey_type = EVP_PKEY_HMAC, +#else + // HMAC is always supported, but possibly with low-level routines + .pkey_type = EVP_PKEY_NONE, +#endif + .type = HMAC_mac}, + + { + .str = "cmac", + .str_v3 = "CMAC", +#ifdef HAVE_CMAC + // If we have CMAC then we have EVP_PKEY + .pkey_type = EVP_PKEY_CMAC, + .type = CMAC_mac, +#else + .pkey_type = EVP_PKEY_NONE +#endif + }, +}; + +mac_collection_t mac_collection("crypto.mac_collection", mac_probes, sizeof(mac_probes) / sizeof(mac_probes[0])); + +// +// Implementation of Known MAC Algorithms storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" size_t mac_algorithms_lazy_init(ErlNifEnv* env, const bool fips_enabled) { + return mac_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM mac_algorithms_as_list(ErlNifEnv* env, const bool fips_enabled) { + return mac_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM mac_availability_t::get_atom() const { return this->init->atom; } + +bool mac_availability_t::is_available() const { return this->init->type != NO_mac; } + +void mac_availability_t::check_fips_availability(const bool fips_enabled) { +#ifdef HAS_3_0_API +#ifdef FIPS_SUPPORT + // Initialize an algorithm to check that all its dependencies are valid in FIPS + if (this->evp_mac) { + auto_evp_mac_ctx_t ctx(EVP_MAC_CTX_new(this->evp_mac.pointer)); + + // Dummy key and parameters. + constexpr unsigned char key[64] = {}; + OSSL_PARAM params[2]; + params[0] = OSSL_PARAM_construct_utf8_string("digest", const_cast("SHA256"), 0); + params[1] = OSSL_PARAM_construct_end(); + + // Try to initialize the digest algorithm for use, this will check the dependencies + if (EVP_MAC_init(ctx.pointer, key, sizeof(key), params) == 1) { + this->flags.fips_forbidden = true; + } + } +#endif /* FIPS_SUPPORT */ +#endif /* HAS_3_0_API */ +} + +void mac_availability_t::update_flags(const bool fips_enabled) { +#if defined(HAS_3_0_API) + this->evp_mac.reset(EVP_MAC_fetch(nullptr, this->init->str_v3, nullptr)); + if (!this->evp_mac) { + this->flags.algorithm_init_failed = true; + } else { + this->check_fips_availability(fips_enabled); + } + // const int unavail_flags = is_valid_in_fips(fetched_mac); // Also tests for NULL + // p->unavail_flags = 0; /* mark available */ + // p->evp_mac = fetched_mac; + // } else { + // p->unavail_flags |= unavail_flags; + // EVP_MAC_free(fetched_mac); + // } +#endif +} + +// for FIPS we will attempt to initialize the pubkey context to verify whether the +// algorithm is allowed, for non-FIPS keeping the old behavior - always allow the algorithm. +void mac_probe_t::probe(ErlNifEnv* env, const bool fips_enabled, std::vector& output) { + this->atom = create_or_existing_atom(env, this->str_v3, this->atom); + mac_availability_t algo = {.init = this, .flags = {.fips_forbidden = this->fips_forbidden_hint}}; + algo.check_fips_availability(fips_enabled); + // No extra checks, just convert name to atom and add + return output.push_back(algo); +} + +extern "C" struct mac_availability_Cptr get_mac_type(ERL_NIF_TERM type, const size_t key_len) +{ + for (auto& p : mac_collection) { + if (type == p.get_atom() && key_len == p.init->key_len) { + return mac_availability_Cptr{.ptr = &p}; + } + } + return mac_availability_Cptr{}; // nullptr +} + +extern "C" struct mac_availability_Cptr get_mac_type_no_key(ERL_NIF_TERM type) +{ + for (auto& p : mac_collection) { + if (type == p.get_atom() ) { + return mac_availability_Cptr{.ptr = &p}; + } + } + return mac_availability_Cptr{}; // nullptr +} + +extern "C" bool is_mac_forbidden_in_fips(const mac_availability_Cptr p) { + if (p.ptr == nullptr) { + return true; // "forbidden" when there's no digest + } + const auto algo = static_cast(p.ptr); + return algo->is_forbidden_in_fips(); +} diff --git a/lib/crypto/c_src/algorithms_mac.h b/lib/crypto/c_src/algorithms_mac.h new file mode 100644 index 000000000000..c41f18eb72d8 --- /dev/null +++ b/lib/crypto/c_src/algorithms_mac.h @@ -0,0 +1,114 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +// Wraps a pointer to mac_availability_t which is a C++ struct with C++ features, for use in C API +struct mac_availability_Cptr { + void* ptr; +}; + +// +// Supported MAC Options storage C API +// +size_t mac_algorithms_lazy_init(ErlNifEnv* env, bool fips_enabled); +ERL_NIF_TERM mac_algorithms_as_list(ErlNifEnv* env, bool fips_enabled); +struct mac_availability_Cptr get_mac_type(ERL_NIF_TERM type, size_t key_len); +struct mac_availability_Cptr get_mac_type_no_key(ERL_NIF_TERM type); +bool is_mac_forbidden_in_fips(struct mac_availability_Cptr p); +int get_mac_availability_type(struct mac_availability_Cptr p); // access field +EVP_MAC* get_mac_availability_resource(struct mac_availability_Cptr p); // access field evp_mac (OpenSSL resource) + +#ifdef __cplusplus +} // end extern "C" +#endif + +enum MAC_TYPE { + NO_mac, + HMAC_mac, + CMAC_mac, + POLY1305_mac, +}; + +#ifdef __cplusplus + +#include "algorithms_collection.h" +#include "auto_openssl_resource.h" + +struct mac_probe_t; + +// Describes a MAC algorithm added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. +struct mac_availability_t { + const mac_probe_t* init = nullptr; // the mac_probe_t used to create this record +#if defined(HAS_3_0_API) + auto_evp_mac_t evp_mac; // OpenSSL resource, frees automatically +#endif + + struct { + bool algorithm_init_failed : 1; + bool fips_forbidden : 1; + } flags = {}; + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +#else + return false; +#endif + } + bool is_available() const; + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; + + void check_fips_availability(bool fips_enabled); + void update_flags(bool fips_enabled); +}; + +// A probe contains data required for creating the algorithm description structure and testing +// its availability. Each probe() call done by the algorithm_collection_t might or might not +// result in a new available algorithm creation. +struct mac_probe_t { + const char* str = nullptr; + const char* str_v3 = nullptr; + ERL_NIF_TERM atom = 0; + // Suggests that the algorithm is not available in FIPS to skip the probe + bool fips_forbidden_hint = false; + int pkey_type = 0; // contains EVP_PKEY_* macro (a NID) + MAC_TYPE type = NO_mac; + size_t key_len = 0; + + // Attempt to add a new MAC algorithm. In case of success, fill the struct and push into the 'output' + void probe(ErlNifEnv* env, bool fips_enabled, std::vector& output); +}; + +using mac_collection_t = algorithm_collection_t; +extern mac_collection_t mac_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_pubkey.cpp b/lib/crypto/c_src/algorithms_pubkey.cpp index ed0fe44d1e3f..b0889b164f00 100644 --- a/lib/crypto/c_src/algorithms_pubkey.cpp +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -21,8 +21,7 @@ */ #include "algorithms_pubkey.h" -#include "algorithms_collection.h" -#include "crypto_openssl_resource.h" +#include "auto_openssl_resource.h" pubkey_probe_t pubkey_probes[] = { {.str = "rsa"}, @@ -78,7 +77,7 @@ void pubkey_availability_t::check_against_fips() { // failed: algorithm not available, do not add if (!ctx) { - this->flags.not_available = true; + this->flags.algorithm_init_failed = true; return; } if (EVP_PKEY_keygen_init(ctx.pointer) <= 0) { // can't generate keys? diff --git a/lib/crypto/c_src/algorithms_pubkey.h b/lib/crypto/c_src/algorithms_pubkey.h index f9dd45d7bd9b..aa2d2839ef11 100644 --- a/lib/crypto/c_src/algorithms_pubkey.h +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -21,7 +21,6 @@ */ #pragma once -#include "algorithms_collection.h" #ifdef __cplusplus extern "C" { @@ -41,13 +40,17 @@ void pubkey_add_algorithm(ErlNifEnv* env, const char* str_v3, unsigned unavailab #endif #ifdef __cplusplus +#include "algorithms_collection.h" struct pubkey_probe_t; +// Describes a public key algorithm added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. struct pubkey_availability_t { const pubkey_probe_t* init = nullptr; // the pubkey_probe_t used to create this record struct { - bool not_available : 1; // algorithm init failed + bool algorithm_init_failed : 1; // algorithm init failed bool fips_forbidden_keygen : 1; bool fips_forbidden_sign : 1; bool fips_forbidden_verify : 1; @@ -58,7 +61,7 @@ struct pubkey_availability_t { bool is_forbidden_in_fips() const { #ifdef FIPS_SUPPORT // Forbidden in FIPS if all operations are forbidden, or if algorithm is not available at all - return (this->flags.not_available || + return (this->flags.algorithm_init_failed || this->flags.fips_forbidden_keygen && this->flags.fips_forbidden_sign && this->flags.fips_forbidden_verify && this->flags.fips_forbidden_encrypt && this->flags.fips_forbidden_derive) && @@ -67,6 +70,7 @@ struct pubkey_availability_t { return false; #endif } + bool is_available() const { return !this->flags.algorithm_init_failed; } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; #if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) diff --git a/lib/crypto/c_src/algorithms_rsaopt.cpp b/lib/crypto/c_src/algorithms_rsaopt.cpp index a4ce4adcf437..952d6c2ed8e3 100644 --- a/lib/crypto/c_src/algorithms_rsaopt.cpp +++ b/lib/crypto/c_src/algorithms_rsaopt.cpp @@ -21,7 +21,6 @@ */ #include "algorithms_rsaopt.h" -#include "algorithms_collection.h" rsaopt_probe_t rsaopt_probes[] = { #ifdef HAS_EVP_PKEY_CTX diff --git a/lib/crypto/c_src/algorithms_rsaopt.h b/lib/crypto/c_src/algorithms_rsaopt.h index 0c4f9035ea98..43d6745bb8b0 100644 --- a/lib/crypto/c_src/algorithms_rsaopt.h +++ b/lib/crypto/c_src/algorithms_rsaopt.h @@ -21,7 +21,6 @@ */ #pragma once -#include "algorithms_collection.h" #ifdef __cplusplus extern "C" { @@ -40,8 +39,12 @@ ERL_NIF_TERM rsaopts_as_list(ErlNifEnv* env, bool fips_enabled); #endif #ifdef __cplusplus +#include "algorithms_collection.h" struct rsaopt_probe_t; +// Describes a RSA option added by the collection's probe function, and checked for compatibility +// with FIPS if FIPS mode was on. If the FIPS mode changes this will be destroyed and +// created again. struct rsaopt_availability_t { const rsaopt_probe_t* init = nullptr; // the rsaopt_probe_t used to create this record @@ -56,6 +59,7 @@ struct rsaopt_availability_t { return false; #endif } + bool is_available() const { return true; } // Return the atom which goes to the Erlang caller ERL_NIF_TERM get_atom() const; }; diff --git a/lib/crypto/c_src/auto_openssl_resource.cpp b/lib/crypto/c_src/auto_openssl_resource.cpp new file mode 100644 index 000000000000..a791e31be862 --- /dev/null +++ b/lib/crypto/c_src/auto_openssl_resource.cpp @@ -0,0 +1,62 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +#include "auto_openssl_resource.h" + +void auto_evp_pkey_t::free_resource(EVP_PKEY* p) { + if (p) { + EVP_PKEY_free(p); + } +} + +void auto_evp_pkey_ctx_t::free_resource(EVP_PKEY_CTX* p) { + if (p) { + EVP_PKEY_CTX_free(p); + } +} + +void auto_ec_key_t::free_resource(EC_KEY* p) { + if (p) { + // TODO: EC_KEY_free is deprecated since OpenSSL 3.0 + EC_KEY_free(p); + } +} + +#ifdef HAVE_ML_KEM +void auto_evp_kem_t::free_resource(EVP_KEM* p) { + if (p) { + EVP_KEM_free(p); + } +} +#endif + +void auto_evp_mac_t::free_resource(EVP_MAC* p) { + if (p) { + EVP_MAC_free(p); + } +} + + +void auto_evp_mac_ctx_t::free_resource(EVP_MAC_CTX* p) { + if (p) { + EVP_MAC_CTX_free(p); + } +} diff --git a/lib/crypto/c_src/auto_openssl_resource.h b/lib/crypto/c_src/auto_openssl_resource.h new file mode 100644 index 000000000000..eddb5c8482bc --- /dev/null +++ b/lib/crypto/c_src/auto_openssl_resource.h @@ -0,0 +1,93 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#include "common.h" +} + +// A generic struct holding a pointer, constructable with a pointer or as null, and auto-destructable. +// The bool operator allows using the struct in if() conditions +// When inheriting: implement the destructor with the call to the corresponding OpenSSL free function +template +struct auto_openssl_resource_t { + ResourceT pointer = nullptr; + + auto_openssl_resource_t() = default; + explicit auto_openssl_resource_t(ResourceT p) : pointer(p) {} + + auto_openssl_resource_t(auto_openssl_resource_t const& other) = delete; // no copy + auto_openssl_resource_t& operator=(auto_openssl_resource_t const&) = delete; // no copy assign + + auto_openssl_resource_t(auto_openssl_resource_t&& other) = default; // allow move + auto_openssl_resource_t& operator=(auto_openssl_resource_t&&) = default; // allow move assign + + ~auto_openssl_resource_t() { ImplementingT::free_resource(this->pointer); } + + explicit operator bool() const { return this->pointer != nullptr; } + + void reset(ResourceT new_value) { + ImplementingT::free_resource(this->pointer); + this->pointer = new_value; + } +}; + +struct auto_evp_pkey_t : auto_openssl_resource_t { + auto_evp_pkey_t() = default; + explicit auto_evp_pkey_t(EVP_PKEY* p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_PKEY* p); +}; + +struct auto_evp_pkey_ctx_t : auto_openssl_resource_t { + auto_evp_pkey_ctx_t() = default; + explicit auto_evp_pkey_ctx_t(EVP_PKEY_CTX* p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_PKEY_CTX* p); +}; + +struct auto_ec_key_t : auto_openssl_resource_t { + auto_ec_key_t() = default; + explicit auto_ec_key_t(EC_KEY* p) : auto_openssl_resource_t(p) {} + static void free_resource(EC_KEY* p); +}; + +#ifdef HAVE_ML_KEM +struct auto_evp_kem_t : auto_openssl_resource_t { + auto_evp_kem_t() = default; + explicit auto_evp_kem_t(EVP_KEM* p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_KEM* p); +}; +#endif + +struct auto_evp_mac_t : auto_openssl_resource_t { + auto_evp_mac_t() = default; + explicit auto_evp_mac_t(EVP_MAC* p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_MAC* p); +}; + +struct auto_evp_mac_ctx_t : auto_openssl_resource_t { + auto_evp_mac_ctx_t() = default; + explicit auto_evp_mac_ctx_t(EVP_MAC_CTX* p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_MAC_CTX* p); +}; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index bfe5d74d657f..52d4ef3cee63 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -353,8 +353,6 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) #endif /* OPENSSL_THREADS */ #endif - init_algorithms_types(env); - library_initialized = 1; ret = 0; diff --git a/lib/crypto/c_src/crypto_openssl_resource.h b/lib/crypto/c_src/crypto_openssl_resource.h deleted file mode 100644 index 99594936a4c4..000000000000 --- a/lib/crypto/c_src/crypto_openssl_resource.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * %CopyrightBegin% - * - * SPDX-License-Identifier: Apache-2.0 - * - * Copyright Ericsson AB 2010-2025. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ -#pragma once - -#include "common.h" - -#ifdef __cplusplus - -// A generic struct holding a pointer, constructable with a pointer or as null, and auto-destructable. -// The bool operator allows using the struct in if() conditions -// When inheriting: implement the destructor with the call to the corresponding OpenSSL free function -template -struct auto_openssl_resource_t { - T* pointer = nullptr; - explicit auto_openssl_resource_t(T* p) : pointer(p) {} - explicit operator bool() const { return this->pointer != nullptr; } -}; - -struct auto_evp_pkey_t : auto_openssl_resource_t { - explicit auto_evp_pkey_t(EVP_PKEY* p) : auto_openssl_resource_t(p) {} - ~auto_evp_pkey_t() { reset(nullptr); } - void reset(EVP_PKEY* new_value) { - if (this->pointer) { - EVP_PKEY_free(this->pointer); - } - this->pointer = new_value; - } -}; - -struct auto_evp_pkey_ctx_t : auto_openssl_resource_t { - explicit auto_evp_pkey_ctx_t(EVP_PKEY_CTX* c) : auto_openssl_resource_t(c) {} - ~auto_evp_pkey_ctx_t() { reset(nullptr); } - void reset(EVP_PKEY_CTX* new_value) { - if (this->pointer) { - EVP_PKEY_CTX_free(this->pointer); - } - this->pointer = new_value; - } -}; - -struct auto_ec_key_t : auto_openssl_resource_t { - explicit auto_ec_key_t(EC_KEY* c) : auto_openssl_resource_t(c) {} - ~auto_ec_key_t() { reset(nullptr); } - void reset(EC_KEY* new_value) { - // TODO: EC_KEY_free is deprecated since OpenSSL 3.0 - if (this->pointer) { - EC_KEY_free(this->pointer); - } - this->pointer = new_value; - } -}; - -#ifdef HAVE_ML_KEM -struct auto_evp_kem_t : auto_openssl_resource_t { - explicit auto_evp_kem_t(EVP_KEM* kem) : auto_openssl_resource_t(kem) {} - ~auto_evp_kem_t() { reset(nullptr); } - void reset(EVP_KEM* new_value) { - if (this->pointer) { - EVP_KEM_free(this->pointer); - } - this->pointer = new_value; - } -}; -#endif - -#endif // __cplusplus diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index 6c8ed17e7d27..7668f3d0f17a 100644 --- a/lib/crypto/c_src/hash.c +++ b/lib/crypto/c_src/hash.c @@ -86,7 +86,7 @@ ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return enif_make_badarg(env); if (is_digest_forbidden_in_fips(digp)) return RAISE_NOTSUP(env); - if ((md = digest_availability_md(digp)) == NULL) + if ((md = get_digest_availability_md(digp)) == NULL) return RAISE_NOTSUP(env); } @@ -114,7 +114,7 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return EXCP_BADARG_N(env, 0, "Bad digest type"); if (is_digest_forbidden_in_fips(digp)) return EXCP_NOTSUP_N(env, 0, "Bad digest type in FIPS"); - if ((md = digest_availability_md(digp)) == NULL) + if ((md = get_digest_availability_md(digp)) == NULL) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in this cryptolib"); if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) @@ -122,7 +122,7 @@ ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,4,0) /* Set xoflen for SHAKE digests if needed */ - unsigned xof_default_length = digest_availability_xof_default_length(digp); + unsigned xof_default_length = get_digest_availability_xof_default_length(digp); if (xof_default_length) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); OSSL_PARAM params[2]; @@ -180,14 +180,14 @@ ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (is_digest_forbidden_in_fips(digp)) return EXCP_NOTSUP_N(env, 0, "Digest type not supported in FIPS"); - if (digest_availability_md(digp) == NULL) + if (get_digest_availability_md(digp) == NULL) return EXCP_NOTSUP_N(env, 0, "Unsupported digest type"); if ((ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(struct evp_md_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate nif resource"); if ((ctx->ctx = EVP_MD_CTX_new()) == NULL) assign_goto(ret, done, EXCP_ERROR(env, "Low-level call EVP_MD_CTX_new failed")); - if (EVP_DigestInit(ctx->ctx, digest_availability_md(digp)) != 1) + if (EVP_DigestInit(ctx->ctx, get_digest_availability_md(digp)) != 1) assign_goto(ret, done, EXCP_ERROR(env, "Low-level call EVP_DigestInit failed")); #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(3,4,0) @@ -195,7 +195,7 @@ ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * The default digest length for shake128 and shake256 was removed * in OpenSSL 3.4, so we set them to be backward compatible with ourself. */ - unsigned xof_default_length = digest_availability_xof_default_length(digp); + unsigned xof_default_length = get_digest_availability_xof_default_length(digp); if (xof_default_length) { OSSL_PARAM params[2]; params[0] = OSSL_PARAM_construct_uint("xoflen", &xof_default_length); diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 462a6ad3f63e..c2dc4a21b95d 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -21,214 +21,23 @@ */ #include "mac.h" -#include "algorithms_digest.h" #include "cipher.h" #include "cmac.h" #include "common.h" #include "hmac.h" #include "info.h" -/*************************** - MAC type declaration -***************************/ - -struct mac_type_t { - union { - const char* str; /* before init, NULL for end-of-table */ - ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ - } name; - unsigned unavail_flags; /* MAC_UNAVAIL_FLAGS: reasons why not available */ - union { - const int pkey_type; - } alg; - int type; /* MAC_TYPE */ - size_t key_len; /* != 0 to also match on key_len */ -#if defined(HAS_3_0_API) - const char* fetch_name; - EVP_MAC *evp_mac; -#endif -}; - -/* masks in the flags field if mac_type_t */ -enum MAC_UNAVAIL_FLAGS { - FIPS_MAC_NOT_AVAIL = 1, - FIPS_FORBIDDEN_MAC = 2, -}; - -enum MAC_TYPE { - NO_mac = 0, - HMAC_mac = 1, - CMAC_mac = 2, - POLY1305_mac = 3, -}; - -static struct mac_type_t mac_types[] = -{ - {{"poly1305"}, FIPS_FORBIDDEN_MAC, -#ifdef HAVE_POLY1305 - /* If we have POLY then we have EVP_PKEY */ - {EVP_PKEY_POLY1305}, POLY1305_mac, 32 -#else - {EVP_PKEY_NONE}, NO_mac, 0 -#endif -#if defined(HAS_3_0_API) - ,"POLY1305" -#endif - }, - - {{"hmac"}, 0, -#if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_HMAC) - {EVP_PKEY_HMAC}, HMAC_mac, 0 -#else - /* HMAC is always supported, but possibly with low-level routines */ - {EVP_PKEY_NONE}, HMAC_mac, 0 -#endif -#if defined(HAS_3_0_API) - ,"HMAC" -#endif - }, - - {{"cmac"}, 0, -#ifdef HAVE_CMAC - /* If we have CMAC then we have EVP_PKEY */ - {EVP_PKEY_CMAC}, CMAC_mac, 0 -#else - {EVP_PKEY_NONE}, NO_mac, 0 -#endif -#if defined(HAS_3_0_API) - ,"CMAC" -#endif - }, - - /*==== End of list ==== */ - {{NULL}, 0, - {0}, NO_mac, 0 - } -}; - -#ifdef FIPS_SUPPORT -# define IS_MAC_FORBIDDEN_IN_FIPS(p) (((p)->unavail_flags & FIPS_FORBIDDEN_MAC) && FIPS_MODE()) -#else -# define IS_MAC_FORBIDDEN_IN_FIPS(P) 0 -#endif +#include "algorithms_digest.h" +#include "algorithms_mac.h" /*************************** Mandatory prototypes ***************************/ -struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len); -struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type); - ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -/******************************** - Support functions for type array -*********************************/ - -#ifdef HAS_3_0_API -#ifdef FIPS_SUPPORT -/* Initialize an algorithm to check that all its dependencies are valid in FIPS */ -static int is_valid_in_fips(EVP_MAC* mac) -{ - int usable = 0; - if (mac) { - EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac); - - /* Dummy key and parameters. */ - unsigned char key[64] = {0}; - OSSL_PARAM params[2]; - params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA256", 0); - params[1] = OSSL_PARAM_construct_end(); - - /* Try to initialize the digest algorithm for use, this will check the dependencies */ - if (EVP_MAC_init(ctx, key, sizeof(key), params) == 1) { - usable |= FIPS_FORBIDDEN_MAC; - } - - EVP_MAC_CTX_free(ctx); - } else { - return FIPS_MAC_NOT_AVAIL; - } - return usable; -} -#endif /* FIPS_SUPPORT */ -#endif /* HAS_3_0_API */ - -static void update_mac_type_fips_flags(struct mac_type_t* p) { -#if defined(HAS_3_0_API) -# ifdef FIPS_SUPPORT - { - EVP_MAC* fetched_mac = EVP_MAC_fetch(NULL, p->fetch_name, "fips=yes"); - const int unavail_flags = is_valid_in_fips(fetched_mac); /* Also tests for NULL */ - if (unavail_flags == 0) { - p->unavail_flags = 0; /* mark available */ - p->evp_mac = fetched_mac; - } else { - p->unavail_flags |= unavail_flags; - EVP_MAC_free(fetched_mac); - } - } -# else - p->evp_mac = EVP_MAC_fetch(NULL, p->fetch_name, NULL); -# endif -#endif -} - -void init_mac_types(ErlNifEnv* env) -{ - struct mac_type_t* p = mac_types; - - for (/* p = mac_types */; p->name.str; p++) { - p->name.atom = enif_make_atom(env, p->name.str); - update_mac_type_fips_flags(p); - } - p->name.atom = atom_false; /* end marker */ -} - -ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env, const bool fips_forbidden) -{ - ERL_NIF_TERM prev = atom_undefined; - ERL_NIF_TERM hd = enif_make_list(env, 0); - - for (struct mac_type_t *p = mac_types; p->name.atom != atom_false; p++) - { - if (prev == p->name.atom || IS_MAC_FORBIDDEN_IN_FIPS(p) != fips_forbidden) { - continue; - } - if (p->type != NO_mac) { - hd = enif_make_list_cell(env, p->name.atom, hd); - } - } - - return hd; -} - -struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len) -{ - struct mac_type_t* p = NULL; - for (p = mac_types; p->name.atom != atom_false; p++) { - if (type == p->name.atom) { - if (p->key_len == 0 || p->key_len == key_len) - return p; - } - } - return NULL; -} - -struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type) -{ - struct mac_type_t* p = NULL; - for (p = mac_types; p->name.atom != atom_false; p++) { - if (type == p->name.atom) { - return p; - } - } - return NULL; -} - /******************************************************************* * * Mac nif @@ -259,7 +68,7 @@ ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (MacType, SubType, Key, Text) */ - struct mac_type_t *macp; + struct mac_availability_Cptr macp; ErlNifBinary key_bin, text; int ret_bin_alloc = 0; ERL_NIF_TERM return_term; @@ -282,33 +91,30 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) /*--------------------------------- Get common indata and validate it */ - if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) - { - return_term = EXCP_BADARG_N(env, 2, "Bad key"); - goto err; - } + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) { + return_term = EXCP_BADARG_N(env, 2, "Bad key"); + goto err; + } - if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) - { - return_term = EXCP_BADARG_N(env, 3, "Bad text"); - goto err; - } + if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) { + return_term = EXCP_BADARG_N(env, 3, "Bad text"); + goto err; + } macp = get_mac_type(argv[0], key_bin.size); - if (!macp) - { - if (!get_mac_type_no_key(argv[0])) - return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); - else - return_term = EXCP_BADARG_N(env, 2, "Bad key length"); - goto err; - } + if (!macp.ptr) { + struct mac_availability_Cptr macp2 = get_mac_type_no_key(argv[0]); + if (!macp2.ptr) + return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG_N(env, 2, "Bad key length"); + goto err; + } - if (IS_MAC_FORBIDDEN_IN_FIPS(macp)) - { - return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); - goto err; - } + if (is_mac_forbidden_in_fips(macp)) { + return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); + goto err; + } /*-------------------------------------------------- Algorithm dependent indata checking and computation. @@ -317,8 +123,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) If not available, do the low-level calls in the corresponding case part */ - switch (macp->type) { - + switch (get_mac_availability_type(macp)) { /******** * HMAC * ********/ @@ -339,15 +144,15 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if defined(HAS_3_0_API) name = "HMAC"; - subalg = digest_availability_str_v3(digp); + subalg = get_digest_availability_str_v3(digp); #else /* Old style */ - if (digest_availability_md(digp) == NULL) + if (get_digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digest_availability_md(digp); + md = get_digest_availability_md(digp); # if defined(HAS_EVP_PKEY_CTX) && (! DISABLE_EVP_HMAC) # ifdef HAVE_PKEY_new_raw_private_key /* Preferred for new applications according to EVP_PKEY_new_mac_key(3) */ @@ -624,7 +429,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #else /* EVP_PKEY_CTX is available or even the 3.0 API */ struct mac_context *obj = NULL; - struct mac_type_t *macp; + struct mac_availability_Cptr macp; ErlNifBinary key_bin; ERL_NIF_TERM return_term; # if defined(HAS_3_0_API) @@ -641,26 +446,24 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) /*--------------------------------- Get common indata and validate it */ - if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) - { - return_term = EXCP_BADARG_N(env, 2, "Bad key"); - goto err; - } + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) { + return_term = EXCP_BADARG_N(env, 2, "Bad key"); + goto err; + } - if (!(macp = get_mac_type(argv[0], key_bin.size))) - { - if (!get_mac_type_no_key(argv[0])) - return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); - else - return_term = EXCP_BADARG_N(env, 2, "Bad key length"); - goto err; - } + macp = get_mac_type(argv[0], key_bin.size); + if (!macp.ptr) { + if (get_mac_type_no_key(argv[0]).ptr == NULL) + return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG_N(env, 2, "Bad key length"); + goto err; + } - if (IS_MAC_FORBIDDEN_IN_FIPS(macp)) - { - return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); - goto err; - } + if (is_mac_forbidden_in_fips(macp)) { + return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS"); + goto err; + } /*-------------------------------------------------- Algorithm dependent indata checking and computation. @@ -669,8 +472,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) If not available, do the low-level calls in the corresponding case part */ - switch (macp->type) { - + switch (get_mac_availability_type(macp)) { /******** * HMAC * ********/ @@ -688,14 +490,14 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } # if defined(HAS_3_0_API) - digest = digest_availability_str_v3(digp); + digest = get_digest_availability_str_v3(digp); # else - if (digest_availability_md(digp) == NULL) + if (get_digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digest_availability_md(digp); + md = get_digest_availability_md(digp); # ifdef HAVE_PKEY_new_raw_private_key /* Preferred for new applications according to EVP_PKEY_new_mac_key(3) */ @@ -777,7 +579,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) /*----------------------------------------- Common computations when we have 3.0 API */ - if (!macp->evp_mac) { + if (!get_mac_availability_resource(macp)) { assign_goto(return_term, err, EXCP_NOTSUP_N(env, 0, "Unsupported mac algorithm")); } @@ -792,7 +594,8 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if ((obj = enif_alloc_resource(mac_context_rtype, sizeof(struct mac_context))) == NULL) assign_goto(return_term, err, EXCP_ERROR(env, "Can't allocate mac_context_rtype")); - if (!(obj->ctx = EVP_MAC_CTX_new(macp->evp_mac))) + obj->ctx = EVP_MAC_CTX_new(get_mac_availability_resource(macp)); + if (!obj->ctx) assign_goto(return_term, err, EXCP_ERROR(env, "Can't create EVP_MAC_CTX")); if (!EVP_MAC_init(obj->ctx, key_bin.data, key_bin.size, params)) diff --git a/lib/crypto/c_src/mac.h b/lib/crypto/c_src/mac.h index 91c6ff129332..aec5edf1a68b 100644 --- a/lib/crypto/c_src/mac.h +++ b/lib/crypto/c_src/mac.h @@ -27,10 +27,6 @@ int init_mac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf); -void init_mac_types(ErlNifEnv* env); - -ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env, bool fips_forbidden); - ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); diff --git a/lib/crypto/c_src/pbkdf2_hmac.c b/lib/crypto/c_src/pbkdf2_hmac.c index 4d0e505faa84..de8df69122e0 100644 --- a/lib/crypto/c_src/pbkdf2_hmac.c +++ b/lib/crypto/c_src/pbkdf2_hmac.c @@ -34,7 +34,7 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, struct digest_availability_Cptr digp = get_digest_type(argv[0]); if (digp.ptr == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (digest_availability_md(digp) == NULL) + if (get_digest_availability_md(digp) == NULL) return EXCP_BADARG_N(env, 0, "md.p is not NULL"); if (is_digest_eligible_for_pbkdf2(digp)) return EXCP_BADARG_N(env, 0, "Not eligible digest type"); @@ -57,7 +57,7 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, if (!PKCS5_PBKDF2_HMAC((const char *)pass.data, pass.size, salt.data, salt.size, iter, - digest_availability_md(digp), + get_digest_availability_md(digp), keylen, out.data)) { enif_release_binary(&out); return EXCP_ERROR(env, "Low-level call failed"); diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index 06975771239e..4790bb395e2d 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -159,7 +159,7 @@ static int check_pkey_algorithm_type(ErlNifEnv *env, static int get_pkey_digest_type(ErlNifEnv* env, ERL_NIF_TERM algorithm, const int type_arg_num, ERL_NIF_TERM type, const EVP_MD** md, ERL_NIF_TERM* err_return) { - struct digest_availability_t *digp = NULL; + struct digest_availability_Cptr digp; *md = NULL; if (type == atom_none) { @@ -179,17 +179,18 @@ static int check_pkey_algorithm_type(ErlNifEnv *env, For eddsa the RFC 8032 mandates sha512 in the algorithm */ return 1; - - if ((digp = get_digest_type(type)) == NULL) + + digp = get_digest_type(type); + if (digp.ptr == NULL) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Bad digest type")); if (is_digest_forbidden_in_fips(digp)) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Digest type forbidden in FIPS")); - if (get_digest_availability_field_md(digp) == NULL) + if (get_digest_availability_md(digp) == NULL) assign_goto(*err_return, notsup, EXCP_BADARG_N(env, type_arg_num, "Digest type not supported")); - *md = get_digest_availability_field_md(digp); + *md = get_digest_availability_md(digp); return 1; notsup: