diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index d36e76dd7e11..a86d1d3aa8e4 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,15 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/aead$(TYPEMARKER).o \ $(OBJDIR)/aes$(TYPEMARKER).o \ $(OBJDIR)/algorithms$(TYPEMARKER).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)/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 \ @@ -98,7 +107,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 \ @@ -119,7 +127,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 +189,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) -std=c++14 ALL_STATIC_CFLAGS = @DED_STATIC_CFLAGS@ $(TYPE_EXTRA_CFLAGS) $(CONFIGURE_ARGS) $(INCLUDES) # ---------------------------------------------------- @@ -222,6 +232,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/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..2188c8b30190 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -20,729 +20,87 @@ * %CopyrightEnd% */ -#include "common.h" #include "algorithms.h" #include "cipher.h" +#include "common.h" #include "mac.h" -#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 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 */ -void init_pubkey_types(ErlNifEnv* env); - -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); - -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); - +#include "algorithms_cipher.h" +#include "algorithms_curve.h" +#include "algorithms_digest.h" +#include "algorithms_kem.h" +#include "algorithms_mac.h" +#include "algorithms_pubkey.h" +// +// Supported Algorithms (filter on fips_forbidden == false) +// -void init_algorithms_types(ErlNifEnv* env) -{ -#ifdef HAS_3_0_API -#else - init_hash_types(env); -#endif - init_pubkey_types(env); - init_rsa_opts_types(env); - /* ciphers and macs are initiated statically */ +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); } - -int create_curve_mutex(void) -{ - if (!mtx_init_curve_types) { - mtx_init_curve_types = enif_mutex_create("init_curve_types"); - } - return !!mtx_init_curve_types; +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); } -void destroy_curve_mutex(void) -{ - if (mtx_init_curve_types) { - enif_mutex_destroy(mtx_init_curve_types); - mtx_init_curve_types = NULL; - } +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); } -/*================================================================ - Hash algorithms -*/ - -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); -#else - unsigned int cnt = - FIPS_MODE() ? algo_hash_fips_cnt : algo_hash_cnt; - - return enif_make_list_from_array(env, algo_hash, cnt); -#endif +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); } -#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(ERL_NIF_TERM)); +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); } -#endif - -/*================================================================ - Public key algorithms -*/ -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; - - return enif_make_list_from_array(env, algo_pubkey, cnt); -} - -void init_pubkey_types(ErlNifEnv* env) { - // Validated algorithms first - algo_pubkey_cnt = 0; - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "rsa"); -#ifdef HAVE_DSA - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dss"); -#endif -#ifdef HAVE_DH - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "dh"); -#endif -#if defined(HAVE_EC) -#if !defined(OPENSSL_NO_EC2M) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ec_gf2m"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdsa"); - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "ecdh"); -#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"); -#endif -#if defined(HAVE_EDDH) - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "eddh"); -#endif - algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); -#ifdef HAVE_ML_DSA - algo_pubkey[algo_pubkey_cnt++] = atom_mldsa44; - algo_pubkey[algo_pubkey_cnt++] = atom_mldsa65; - algo_pubkey[algo_pubkey_cnt++] = atom_mldsa87; -#endif - ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); -} - -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); -#else - return enif_make_list(env, 0); -#endif -} - - -/*================================================================ - Cipher key algorithms -*/ - -ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - return cipher_types_as_list(env); /* Exclude old api ciphers */ +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); } - -/*================================================================ - MAC key algorithms -*/ - -ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - return mac_types_as_list(env); +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) +// -/*================================================================ - Curves -*/ - -ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - int fips_mode; - int algo_curve_cnt; - - fips_mode = FIPS_MODE(); - algo_curve_cnt = get_curve_cnt(env, fips_mode); - - return enif_make_list_from_array(env, algo_curve[fips_mode], algo_curve_cnt); +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); } -static int init_curves(ErlNifEnv* env, int fips); -#if defined(HAVE_EC) -static int valid_curve(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; - } - - if (1 == fips && algo_curve_fips_cnt >= 0) { - return algo_curve_fips_cnt; - } - - 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); - } - cnt = algo_curve_cnt; - } - enif_mutex_unlock(mtx_init_curve_types); - - return cnt; +ERL_NIF_TERM fips_forbidden_pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + pubkey_algorithms_lazy_init(env, 1); + return pubkey_algorithms_as_list(env, true); } -int init_curves(ErlNifEnv* env, int fips) { -#if defined(HAVE_EC) - int cnt = 0; - -#ifdef NID_secp160k1 - if (valid_curve(NID_secp160k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160k1"); -#else -#endif -#ifdef NID_secp160r1 - if (valid_curve(NID_secp160r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160r1"); -#else -#endif -#ifdef NID_secp160r2 - if (valid_curve(NID_secp160r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp160r2"); -#else -#endif -#ifdef NID_secp192k1 - if (valid_curve(NID_secp192k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp192k1"); -#else -#endif -#ifdef NID_secp224k1 - if (valid_curve(NID_secp224k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp224k1"); -#else -#endif -#ifdef NID_secp224r1 - if (valid_curve(NID_secp224r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp224r1"); -#else -#endif -#ifdef NID_secp256k1 - if (valid_curve(NID_secp256k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp256k1"); -#else -#endif -#ifdef NID_secp384r1 - if (valid_curve(NID_secp384r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp384r1"); -#else -#endif -#ifdef NID_secp521r1 - if (valid_curve(NID_secp521r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"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"); - } -#else -#endif -#ifdef NID_X9_62_prime192v2 - if (valid_curve(NID_X9_62_prime192v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime192v2"); -#else -#endif -#ifdef NID_X9_62_prime192v3 - if (valid_curve(NID_X9_62_prime192v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime192v3"); -#else -#endif -#ifdef NID_X9_62_prime239v1 - if (valid_curve(NID_X9_62_prime239v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime239v1"); -#else -#endif -#ifdef NID_X9_62_prime239v2 - if (valid_curve(NID_X9_62_prime239v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"prime239v2"); -#else -#endif -#ifdef NID_X9_62_prime239v3 - if (valid_curve(NID_X9_62_prime239v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"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"); - } -#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"); -#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"); -#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"); -#else -#endif -#ifdef NID_brainpoolP160r1 - if (valid_curve(NID_brainpoolP160r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP160r1"); -#else -#endif -#ifdef NID_brainpoolP160t1 - if (valid_curve(NID_brainpoolP160t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP160t1"); -#else -#endif -#ifdef NID_brainpoolP192r1 - if (valid_curve(NID_brainpoolP192r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP192r1"); -#else -#endif -#ifdef NID_brainpoolP192t1 - if (valid_curve(NID_brainpoolP192t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP192t1"); -#else -#endif -#ifdef NID_brainpoolP224r1 - if (valid_curve(NID_brainpoolP224r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP224r1"); -#else -#endif -#ifdef NID_brainpoolP224t1 - if (valid_curve(NID_brainpoolP224t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP224t1"); -#else -#endif -#ifdef NID_brainpoolP256r1 - if (valid_curve(NID_brainpoolP256r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP256r1"); -#else -#endif -#ifdef NID_brainpoolP256t1 - if (valid_curve(NID_brainpoolP256t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP256t1"); -#else -#endif -#ifdef NID_brainpoolP320r1 - if (valid_curve(NID_brainpoolP320r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP320r1"); -#else -#endif -#ifdef NID_brainpoolP320t1 - if (valid_curve(NID_brainpoolP320t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP320t1"); -#else -#endif -#ifdef NID_brainpoolP384r1 - if (valid_curve(NID_brainpoolP384r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP384r1"); -#else -#endif -#ifdef NID_brainpoolP384t1 - if (valid_curve(NID_brainpoolP384t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP384t1"); -#else -#endif -#ifdef NID_brainpoolP512r1 - if (valid_curve(NID_brainpoolP512r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP512r1"); -#else -#endif -#ifdef NID_brainpoolP512t1 - if (valid_curve(NID_brainpoolP512t1)) algo_curve[fips][cnt++] = enif_make_atom(env,"brainpoolP512t1"); -#else -#endif - //#if !defined(OPENSSL_NO_EC2M) -#ifdef NID_sect163k1 - if (valid_curve(NID_sect163k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163k1"); -#else -#endif -#ifdef NID_sect163r1 - if (valid_curve(NID_sect163r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163r1"); -#else -#endif -#ifdef NID_sect163r2 - if (valid_curve(NID_sect163r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect163r2"); -#else -#endif -#ifdef NID_sect193r1 - if (valid_curve(NID_sect193r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect193r1"); -#else -#endif -#ifdef NID_sect193r2 - if (valid_curve(NID_sect193r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect193r2"); -#else -#endif -#ifdef NID_sect233k1 - if (valid_curve(NID_sect233k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect233k1"); -#else -#endif -#ifdef NID_sect233r1 - if (valid_curve(NID_sect233r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect233r1"); -#else -#endif -#ifdef NID_sect239k1 - if (valid_curve(NID_sect239k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect239k1"); -#else -#endif -#ifdef NID_sect283k1 - if (valid_curve(NID_sect283k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect283k1"); -#else -#endif -#ifdef NID_sect283r1 - if (valid_curve(NID_sect283r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect283r1"); -#else -#endif -#ifdef NID_sect409k1 - if (valid_curve(NID_sect409k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect409k1"); -#else -#endif -#ifdef NID_sect409r1 - if (valid_curve(NID_sect409r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect409r1"); -#else -#endif -#ifdef NID_sect571k1 - if (valid_curve(NID_sect571k1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect571k1"); -#else -#endif -#ifdef NID_sect571r1 - if (valid_curve(NID_sect571r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect571r1"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v1 - if (valid_curve(NID_X9_62_c2pnb163v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v1"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v2 - if (valid_curve(NID_X9_62_c2pnb163v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v2"); -#else -#endif -#ifdef NID_X9_62_c2pnb163v3 - if (valid_curve(NID_X9_62_c2pnb163v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb163v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb176v1 - if (valid_curve(NID_X9_62_c2pnb176v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb176v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v1 - if (valid_curve(NID_X9_62_c2tnb191v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v2 - if (valid_curve(NID_X9_62_c2tnb191v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v2"); -#else -#endif -#ifdef NID_X9_62_c2tnb191v3 - if (valid_curve(NID_X9_62_c2tnb191v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb191v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb208w1 - if (valid_curve(NID_X9_62_c2pnb208w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb208w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v1 - if (valid_curve(NID_X9_62_c2tnb239v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v1"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v2 - if (valid_curve(NID_X9_62_c2tnb239v2)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v2"); -#else -#endif -#ifdef NID_X9_62_c2tnb239v3 - if (valid_curve(NID_X9_62_c2tnb239v3)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb239v3"); -#else -#endif -#ifdef NID_X9_62_c2pnb272w1 - if (valid_curve(NID_X9_62_c2pnb272w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb272w1"); -#else -#endif -#ifdef NID_X9_62_c2pnb304w1 - if (valid_curve(NID_X9_62_c2pnb304w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb304w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb359v1 - if (valid_curve(NID_X9_62_c2tnb359v1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2tnb359v1"); -#else -#endif -#ifdef NID_X9_62_c2pnb368w1 - if (valid_curve(NID_X9_62_c2pnb368w1)) algo_curve[fips][cnt++] = enif_make_atom(env,"c2pnb368w1"); -#else -#endif -#ifdef NID_X9_62_c2tnb431r1 - if (valid_curve(NID_X9_62_c2tnb431r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"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"); -#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"); -#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"); -#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"); -#else -#endif - // Non-validated algorithms follow -#ifdef NID_secp112r1 - if (valid_curve(NID_secp112r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp112r1"); -#else -#endif -#ifdef NID_secp112r2 - if (valid_curve(NID_secp112r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp112r2"); -#else -#endif -#ifdef NID_secp128r1 - if (valid_curve(NID_secp128r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"secp128r1"); -#else -#endif -#ifdef NID_secp128r2 - if (valid_curve(NID_secp128r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"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"); -#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"); -#else -#endif - //#if !defined(OPENSSL_NO_EC2M) -#ifdef NID_sect113r1 - if (valid_curve(NID_sect113r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect113r1"); -#else -#endif -#ifdef NID_sect113r2 - if (valid_curve(NID_sect113r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect113r2"); -#else -#endif -#ifdef NID_sect131r1 - if (valid_curve(NID_sect131r1)) algo_curve[fips][cnt++] = enif_make_atom(env,"sect131r1"); -#else -#endif -#ifdef NID_sect131r2 - if (valid_curve(NID_sect131r2)) algo_curve[fips][cnt++] = enif_make_atom(env,"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"); -#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"); -#else -#endif -#ifdef NID_ipsec3 - if (valid_curve(NID_ipsec3)) algo_curve[fips][cnt++] = enif_make_atom(env,"ipsec3"); -#else -#endif -#ifdef NID_ipsec4 - if (valid_curve(NID_ipsec4)) algo_curve[fips][cnt++] = enif_make_atom(env,"ipsec4"); -#else -#endif - - if (!fips) { -#ifdef HAVE_ED25519 - algo_curve[fips][cnt++] = enif_make_atom(env,"ed25519"); -#endif -#ifdef HAVE_ED448 - algo_curve[fips][cnt++] = enif_make_atom(env,"ed448"); -#endif -#ifdef HAVE_X25519 - algo_curve[fips][cnt++] = enif_make_atom(env,"x25519"); -#endif -#ifdef HAVE_X448 - algo_curve[fips][cnt++] = enif_make_atom(env,"x448"); -#endif - } - - ASSERT(cnt <= sizeof(algo_curve[0])/sizeof(ERL_NIF_TERM)); - - return cnt; -#else /* if not HAVE_EC */ - return 0; -#endif +ERL_NIF_TERM fips_forbidden_cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + cipher_algorithms_lazy_init(env, FIPS_MODE()); + return cipher_types_as_list(env, true); } -#if defined(HAVE_EC) - -/* Check if the curve in nid is supported by the - current cryptolib and current FIPS state. -*/ - -int valid_curve(int nid) { - int ret = 0; - -#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 = 1; - 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 = 1; - out: - if (key) EC_KEY_free(key); -# endif -#endif /* HAVE_DH etc */ - - return ret; +ERL_NIF_TERM fips_forbidden_kem_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + kem_algorithms_lazy_init(env, FIPS_MODE()); + return kem_algorithms_as_list(env, true); } -#endif /* HAVE_EC */ - -/*================================================================ - RSA Options -*/ -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; - - return enif_make_list_from_array(env, algo_rsa_opts, cnt); +ERL_NIF_TERM fips_forbidden_mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + mac_algorithms_lazy_init(env, FIPS_MODE()); + return mac_algorithms_as_list(env, true); } -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(ERL_NIF_TERM)); +ERL_NIF_TERM fips_forbidden_curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + curve_algorithms_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..5c296a5470c1 100644 --- a/lib/crypto/c_src/algorithms.h +++ b/lib/crypto/c_src/algorithms.h @@ -25,16 +25,24 @@ #include "common.h" -int create_curve_mutex(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[]); +ERL_NIF_TERM fips_forbidden_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[]); +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/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 new file mode 100644 index 000000000000..6d8be0e1470c --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.cpp @@ -0,0 +1,67 @@ +/* + * %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_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() && + 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) { + 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..74733a47bfb2 --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.h @@ -0,0 +1,153 @@ +/* + * %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 affects 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 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 +// 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(); } + + // 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; + } + + 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 (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); + } + 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_available() && algo.is_forbidden_in_fips() == fips_forbidden) { + hd = enif_make_list_cell(env, algo.get_atom(), hd); + } + } + return hd; + } +}; + +// 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 new file mode 100644 index 000000000000..d64ee14a502f --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -0,0 +1,489 @@ +/* + * %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 +} + +#include "algorithms_curve.h" +#include "auto_openssl_resource.h" + +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 + +#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 +// + +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) { + 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; + auto_evp_pkey_t params; + + 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)) + 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; + + const 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 + auto_ec_key_t key(EC_KEY_new_by_curve_name(nid)); + + if (!key) + return false; + if (1 != EC_KEY_generate_key(key.pointer)) + return false; + return true; +#endif +#endif /* HAVE_DH etc */ + + return false; +#else + return false; +#endif // HAVE_EC +} + +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}; + + // 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 + } + + algo.probe_under_fips(fips_mode); + output.push_back(algo); +} + +ERL_NIF_TERM curve_availability_t::get_atom() const { return this->init->atom; } + +void curve_availability_t::probe_under_fips(bool fips_mode) { +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + // This checking code only runs under FIPS and OpenSSL 3+, other cases algorithm is always added + if (!fips_mode) + return; + + OSSL_PARAM params[2]; + + const auto_evp_pkey_ctx_t pctx(EVP_PKEY_CTX_new_from_name(nullptr, "EC", "fips=yes")); + if (!pctx) { + this->flags.algorithm_init_failed = true; + return; // EC keygen context not available + } + if (EVP_PKEY_keygen_init(pctx.pointer) <= 0) { + 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.algorithm_init_failed = true; + return; + } + auto_evp_pkey_t pkey(nullptr); + if (EVP_PKEY_generate(pctx.pointer, &pkey.pointer) <= 0) { + 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 new file mode 100644 index 000000000000..46c61a43ec5f --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.h @@ -0,0 +1,89 @@ +/* + * %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" + +// +// Curve Algorithms storage API +// +size_t curve_algorithms_lazy_init(ErlNifEnv *env, bool fips_enabled); +ERL_NIF_TERM curve_algorithms_as_list(ErlNifEnv *env, bool fips_enabled); + +#ifdef __cplusplus +} +#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 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.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 + 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 { + 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 + + // 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: + bool is_curve_valid_by_nid(); // used by the probe() to check this->nid +}; + +// 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..6f76edaf1b31 --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -0,0 +1,194 @@ +/* + * %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" + +static digest_probe_t digest_probes[] = { +#ifdef HAVE_MD4 + {.str = "md4", .str_v3 = "MD4", .v1_ctor = &EVP_md4}, +#endif +#ifdef HAVE_MD5 + {.str = "md5", .str_v3 = "MD5", .v1_ctor = &EVP_md5}, +#endif +#ifdef HAVE_RIPEMD160 + {.str = "ripemd160", .str_v3 = "RIPEMD160", .v1_ctor = &EVP_ripemd160}, +#endif + {.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}, +#endif +#ifdef HAVE_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}, +#endif +#ifdef HAVE_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}, +#endif +#ifdef HAVE_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}, +#endif +#ifdef HAVE_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}, +#endif +#ifdef HAVE_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}, +#endif +#ifdef HAVE_SHAKE256 + {.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}, +#endif +#ifdef HAVE_BLAKE2 + {.str = "blake2b", .str_v3 = "BLAKE2b512", .v1_ctor = &EVP_blake2b512}, +#endif +#ifdef HAVE_BLAKE2 + {.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])); + +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(); + int usable = 0; + + if (md) { + // Try to initialize the digest algorithm for use, this will check the dependencies + if (EVP_DigestInit_ex(ctx, md, nullptr) == 1) { + usable = 1; + } + } + + EVP_MD_CTX_free(ctx); + return usable; +} +#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(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 { + this->flags.fips_forbidden = false; + this->md = fetched_md; + } +#else + // construct from the old API, each probe has a constructor function + this->md = this->init->v1_ctor(); +#endif // HAS_3_0_API +} + +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. + algo.create_md_resource(fips_mode); + if (algo.md) { + output.push_back(std::move(algo)); + } +} + +// Array lookup +extern "C" digest_availability_Cptr get_digest_type(ERL_NIF_TERM type) { + for (auto& p : digest_collection) { + if (type == p.get_atom()) { + return digest_availability_Cptr{.ptr = &p}; + } + } + return digest_availability_Cptr{}; // sorry, nullptr +} + +// Free the OpenSSL resource +digest_availability_t::~digest_availability_t() { + if (this->md) { +#if defined(HAS_3_0_API) + EVP_MD_free(const_cast(this->md)); +#else + 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 + } + const auto algo = static_cast(p.ptr); + return algo->is_forbidden_in_fips(); +} + +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 + } + const auto algo = static_cast(p.ptr); + return algo->init->str_v3; +} + +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 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 + } + const auto algo = static_cast(p.ptr); + return algo->xof_default_length; +} + +extern "C" bool is_digest_eligible_for_pbkdf2(struct digest_availability_Cptr p) { + if (p.ptr == nullptr) { + return false; // "no digest" is not eligible + } + const auto algo = static_cast(p.ptr); + 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 new file mode 100644 index 000000000000..1b0bd00fb686 --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.h @@ -0,0 +1,119 @@ +/* + * %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 + +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); +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* 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 +// created again. +struct digest_availability_t { + // The definition used to create this record + const struct digest_probe_t* init = nullptr; + 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 + size_t xof_default_length = 0; + + ~digest_availability_t(); + + 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; + + // Fetches the algorithm and sets the initial flags + 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); +#endif +}; + +// 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; + // 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; + // 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; + + // 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; +extern digest_collection_t digest_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_kem.cpp b/lib/crypto/c_src/algorithms_kem.cpp new file mode 100644 index 000000000000..8e06c31d2e0c --- /dev/null +++ b/lib/crypto/c_src/algorithms_kem.cpp @@ -0,0 +1,92 @@ +/* + * %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 "auto_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..5d3be77264c4 --- /dev/null +++ b/lib/crypto/c_src/algorithms_kem.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" + +// +// 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); + +#ifdef __cplusplus +} +#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 + + 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; + 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; + 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_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 new file mode 100644 index 000000000000..b0889b164f00 --- /dev/null +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -0,0 +1,122 @@ +/* + * %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 "auto_openssl_resource.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; } + +// Result: flags set if FIPS is not supported +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) +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) { + this->flags.algorithm_init_failed = true; + return; + } + if (EVP_PKEY_keygen_init(ctx.pointer) <= 0) { // can't generate keys? + this->flags.fips_forbidden_keygen = true; + } + + // 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; + } + + // 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.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.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) { + 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.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 new file mode 100644 index 000000000000..aa2d2839ef11 --- /dev/null +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -0,0 +1,96 @@ +/* + * %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" + +// +// 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 + +#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 algorithm_init_failed : 1; // algorithm init failed + 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 + // Forbidden in FIPS if all operations are forbidden, or if algorithm is not available at all + 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) && + 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; +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + void check_against_fips(); // Result: flags set if FIPS is not supported +#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 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 pubkey_collection_t = algorithm_collection_t; +extern pubkey_collection_t pubkey_collection; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/algorithms_rsaopt.cpp b/lib/crypto/c_src/algorithms_rsaopt.cpp new file mode 100644 index 000000000000..952d6c2ed8e3 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.cpp @@ -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% + */ + +#include "algorithms_rsaopt.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..43d6745bb8b0 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.h @@ -0,0 +1,81 @@ +/* + * %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 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 +#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 + + 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 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/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/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/cipher.c b/lib/crypto/c_src/cipher.c index b57563b3a712..19d1393920d3 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,34 +205,63 @@ int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) { return 0; } -void init_cipher_types(ErlNifEnv* env) +#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) { - struct cipher_type_t* p = cipher_types; + 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; + } + } - 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); + EVP_CIPHER_CTX_free(ctx); + return usable; +} +#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) { - p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); + if (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; - } -# endif /* FIPS_SUPPORT and >=3.0.0 */ + 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(); + 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++) { + num_cipher_types++; + p->type.atom = enif_make_atom(env, p->type.str); + update_cipher_type_availability(p); } p->type.atom = atom_false; /* end marker */ @@ -277,7 +306,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 +420,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..52d4ef3cee63 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,6 +54,8 @@ #include "rsa.h" #include "srp.h" +#include "algorithms_collection.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); @@ -70,12 +72,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 +116,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}, @@ -244,7 +260,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; } @@ -337,11 +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; ret = 0; @@ -396,7 +407,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); /* @@ -411,4 +422,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 deleted file mode 100644 index 863490cf2b15..000000000000 --- a/lib/crypto/c_src/digest.c +++ /dev/null @@ -1,243 +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, NO_FIPS_DIGEST, -#ifdef HAVE_MD4 - {&EVP_md4,NULL} -#else - {NULL,NULL} -#endif - }, - - {"md5", "MD5", 0, NO_FIPS_DIGEST, -#ifdef HAVE_MD5 - {&EVP_md5,NULL} -#else - {NULL,NULL} -#endif - }, - - {"ripemd160", "RIPEMD160", 0, NO_FIPS_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}} -}; - -void init_digest_types(ErlNifEnv* env) -{ - struct digest_type_t* p = digest_types; - - 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; - } -# endif /* FIPS_SUPPORT and >=3.0.0 */ - } -#else - if (p->md.funcp) - p->md.p = p->md.funcp(); -#endif - 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; -} - - -#ifdef HAS_3_0_API -ERL_NIF_TERM digest_types_as_list(ErlNifEnv* env) -{ - 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; - - if (p->md.p != NULL) - 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 deleted file mode 100644 index c211a8ca96ea..000000000000 --- a/lib/crypto/c_src/digest.h +++ /dev/null @@ -1,58 +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; - 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 if digest_type_t */ -#define NO_FIPS_DIGEST 1 -#define PBKDF2_ELIGIBLE_DIGEST 2 - -#ifdef FIPS_SUPPORT -# define DIGEST_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_DIGEST) && FIPS_MODE()) -#else -# define DIGEST_FORBIDDEN_IN_FIPS(P) 0 -#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 - -#endif /* E_DIGEST_H__ */ diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index 88271ebc9a6e..558112656c79 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_collection.h" +#include "algorithms_digest.h" ERL_NIF_TERM info_fips(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { @@ -33,32 +33,34 @@ 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 { - 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) { - if (!FIPS_mode_set(0)) return atom_false; - else return atom_true; + bool previous_setting = FIPS_MODE(); + bool fips_mode = fips_mode_to_set == atom_true; - } else + /* 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); + } + + 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; } #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 diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c index 5757c30d24e5..7668f3d0f17a 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 @@ -72,46 +72,49 @@ 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; - 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) - return enif_make_badarg(env); - if (DIGEST_FORBIDDEN_IN_FIPS(digp)) - return RAISE_NOTSUP(env); - - if ((md = digp->md.p) == 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 = get_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); - ASSERT(ok); (void)ok; + + { + 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_type_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 (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 = 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)) @@ -119,19 +122,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 = get_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 +171,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_type_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 (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 (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, digp->md.p) != 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) @@ -191,9 +195,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 = get_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")); @@ -277,7 +282,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; @@ -288,7 +293,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"); @@ -363,7 +368,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; @@ -374,7 +379,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"); @@ -460,7 +465,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; @@ -473,7 +478,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/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 3db57da49133..c2dc4a21b95d 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -20,166 +20,24 @@ * %CopyrightEnd% */ -#include "common.h" +#include "mac.h" #include "cipher.h" -#include "digest.h" #include "cmac.h" +#include "common.h" #include "hmac.h" -#include "mac.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 flags; - union { - const int pkey_type; - }alg; - int 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 */ -#define NO_FIPS_MAC 1 - -#define NO_mac 0 -#define HMAC_mac 1 -#define CMAC_mac 2 -#define POLY1305_mac 3 - -static struct mac_type_t mac_types[] = -{ - {{"poly1305"}, NO_FIPS_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 MAC_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_MAC) && FIPS_MODE()) -#else -# define 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 -*********************************/ - -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); -#if defined(HAS_3_0_API) - p->evp_mac = EVP_MAC_fetch(NULL, p->fetch_name, NULL); -#endif - } - p->name.atom = atom_false; /* end marker */ -} - -ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env) -{ - struct mac_type_t* p; - ERL_NIF_TERM prev, hd; - - hd = enif_make_list(env, 0); - prev = atom_undefined; - - for (p = mac_types; p->name.atom != atom_false; p++) { - if (prev == p->name.atom) - 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 @@ -210,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; @@ -233,32 +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; + } - 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) { + 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 (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. @@ -267,21 +123,20 @@ 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 * ********/ case HMAC_mac: { - struct digest_type_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 (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; @@ -289,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 = digp->str_v3; + subalg = get_digest_availability_str_v3(digp); #else /* Old style */ - if (digp->md.p == NULL) + if (get_digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + 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) */ @@ -335,7 +190,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; @@ -574,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) @@ -591,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 (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. @@ -619,34 +472,32 @@ 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 * ********/ case HMAC_mac: { - struct digest_type_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 (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 = get_digest_availability_str_v3(digp); # else - if (digp->md.p == NULL) + if (get_digest_availability_md(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + 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) */ @@ -677,7 +528,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; @@ -728,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")); } @@ -743,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 60cb4ff1d925..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); - 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 6d450cb4fc15..de8df69122e0 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,13 +30,13 @@ static ERL_NIF_TERM pbkdf2_hmac(ErlNifEnv* env, int argc, { ErlNifBinary pass, salt, out; ErlNifUInt64 iter, keylen; - struct digest_type_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 (get_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, + 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 f10998bafff4..4790bb395e2d 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -20,9 +20,10 @@ * %CopyrightEnd% */ +#include "algorithms_digest.h" + #include "pkey.h" #include "bn.h" -#include "digest.h" #include "dss.h" #include "ec.h" #include "eddsa.h" @@ -156,12 +157,9 @@ 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) -{ - struct digest_type_t *digp = NULL; + 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_Cptr digp; *md = NULL; if (type == atom_none) { @@ -181,17 +179,18 @@ static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, 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 (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_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_md(digp); return 1; notsup: @@ -203,7 +202,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; 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..a28067d6a0d4 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. @@ -969,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. @@ -3906,14 +3966,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, []).