diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index d36e76dd7e11..d39e3355402a 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,16 @@ 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)/evp_compat$(TYPEMARKER).cpp.o \ $(OBJDIR)/api_ng$(TYPEMARKER).o \ $(OBJDIR)/atoms$(TYPEMARKER).o \ $(OBJDIR)/bn$(TYPEMARKER).o \ @@ -98,7 +108,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 +128,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 @@ -179,8 +189,12 @@ 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_CFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) -std=gnu99 +ALL_CXXFLAGS = $(TYPE_FLAGS) $(EXTRA_FLAGS) $(CONFIGURE_ARGS) $(INCLUDES) -std=c++14 + +# Removed: -Wdeclaration-after-statement, allowed in C99 and C++ ALL_STATIC_CFLAGS = @DED_STATIC_CFLAGS@ $(TYPE_EXTRA_CFLAGS) $(CONFIGURE_ARGS) $(INCLUDES) +ALL_STATIC_CFLAGS := $(filter-out -Wdeclaration-after-statement,$(ALL_STATIC_CFLAGS)) # ---------------------------------------------------- # Targets @@ -222,6 +236,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..0be6d0e185cf 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -24,12 +24,12 @@ #include "aes.h" #include "cipher.h" #include "info.h" - +#include "algorithms_cipher.h" ErlNifResourceType* aead_cipher_ctx_rtype; struct aead_cipher_ctx { - const struct cipher_type_t *cipherp; + const cipher_type_C *cipherp; EVP_CIPHER_CTX *ctx; ERL_NIF_TERM key; @@ -71,6 +71,7 @@ ERL_NIF_TERM aead_cipher_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a struct aead_cipher_ctx *ctx_res = NULL; ERL_NIF_TERM ret, encflg_arg, type; ErlNifBinary key; + struct cipher_type_flags_t flags; if ((ctx_res = enif_alloc_resource(aead_cipher_ctx_rtype, sizeof(struct aead_cipher_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate resource"); @@ -107,11 +108,13 @@ ERL_NIF_TERM aead_cipher_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a if ((ctx_res->cipherp = get_cipher_type(type, key.size)) == NULL) {ret = EXCP_BADARG_N(env, 0, "Unknown cipher or invalid key size"); goto done;} - if (ctx_res->cipherp->flags & NON_EVP_CIPHER) + + flags = get_cipher_type_flags(ctx_res->cipherp); + if (flags.non_evp_cipher) {ret = EXCP_BADARG_N(env, 0, "Bad cipher"); goto done;} - if (! (ctx_res->cipherp->flags & AEAD_CIPHER) ) + if (!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) @@ -120,13 +123,19 @@ ERL_NIF_TERM aead_cipher_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a } #endif - if (ctx_res->cipherp->cipher.p == NULL) - {ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); goto done;} - - if ((ctx_res->ctx = EVP_CIPHER_CTX_new()) == NULL) - {ret = EXCP_ERROR(env, "Can't allocate ctx"); goto done;} - if (EVP_CipherInit_ex(ctx_res->ctx, ctx_res->cipherp->cipher.p, NULL, NULL, NULL, ctx_res->encflg) != 1) - {ret = EXCP_ERROR(env, "CipherInit failed"); goto done;} + if (get_cipher_type_resource(ctx_res->cipherp) == NULL) { + ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); + goto done; + } + if ((ctx_res->ctx = EVP_CIPHER_CTX_new()) == NULL) { + ret = EXCP_ERROR(env, "Can't allocate ctx"); + goto done; + } + if (EVP_CipherInit_ex(ctx_res->ctx, get_cipher_type_resource(ctx_res->cipherp), + NULL, NULL, NULL, ctx_res->encflg) != 1) { + ret = EXCP_ERROR(env, "CipherInit failed"); + goto done; + } ret = enif_make_resource(env, ctx_res); @@ -146,9 +155,11 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] const EVP_CIPHER *cipher = NULL; ErlNifBinary key, iv, aad, in, tag; unsigned int tag_len; - unsigned char *outp, *tagp, *tag_data, *in_data; + unsigned char *outp, *tag_data, *in_data; ERL_NIF_TERM type, out, out_tag, ret, encflg_arg; int len, encflg, in_len; + struct cipher_type_flags_t flags; + struct cipher_type_aead_t aead; if(argc == 7) { /* @@ -202,29 +213,45 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] || iv.size > INT_MAX || in.size > INT_MAX || aad.size > INT_MAX) - {ret = EXCP_BADARG_N(env, 5, "binary too long"); goto done;} + {ret = EXCP_BADARG_N(env, 5, "binary too long"); goto done; + } + + if ((cipherp = get_cipher_type(type, key.size)) == NULL) { + ret = EXCP_BADARG_N(env, 0, "Unknown cipher or invalid key size"); + goto done; + } - if ((cipherp = get_cipher_type(type, key.size)) == NULL) - {ret = EXCP_BADARG_N(env, 0, "Unknown cipher or invalid key size"); goto done;} - if (cipherp->flags & NON_EVP_CIPHER) - {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)) - {ret = EXCP_NOTSUP_N(env, 0, "Forbidden in FIPS"); goto done;} + flags = get_cipher_type_flags(cipherp); + if (flags.non_evp_cipher) { + ret = EXCP_BADARG_N(env, 0, "Bad cipher"); + goto done; + } + if (!flags.aead_cipher) { + ret = EXCP_BADARG_N(env, 0, "Not aead cipher"); + goto done; + } + 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) if ( !encflg && (cipherp->flags & GCM_MODE)) { return aes_gcm_decrypt_NO_EVP(env, argc, argv); } #endif - if ((cipher = cipherp->cipher.p) == NULL) - {ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); goto done;} - - if ((ctx = EVP_CIPHER_CTX_new()) == NULL) - {ret = EXCP_ERROR(env, "Can't allocate ctx"); goto done;} - if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encflg) != 1) - {ret = EXCP_ERROR(env, "CipherInit failed"); goto done;} + if ((cipher = get_cipher_type_resource(cipherp)) == NULL) { + ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); + goto done; + } + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) { + ret = EXCP_ERROR(env, "Can't allocate ctx"); + goto done; + } + if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encflg) != 1) { + ret = EXCP_ERROR(env, "CipherInit failed"); + goto done; + } } else { /* argc = 4 {state, IV, InData, AAD } */ @@ -258,17 +285,20 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] } cipherp = ctx_res->cipherp; - cipher = cipherp->cipher.p; + cipher = get_cipher_type_resource(cipherp); ctx = ctx_res->ctx; } /* Init done */ - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) + aead = get_cipher_type_aead(cipherp); + flags = get_cipher_type_flags(cipherp); + + if (EVP_CIPHER_CTX_ctrl(ctx, aead.ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1) {ret = EXCP_BADARG_N(env, 2, "Bad IV length"); goto done;} #if defined(HAVE_CCM) - if (cipherp->flags & CCM_MODE) { - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1) + if (flags.ccm_mode) { + if (EVP_CIPHER_CTX_ctrl(ctx, aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1) {ret = EXCP_BADARG_N(env, 5, "Can't set tag"); goto done;} if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1) {ret = EXCP_ERROR(env, "Can't set key or iv"); goto done;} @@ -304,8 +334,8 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ret = atom_error; goto done; } - if (encflg) - { + if (encflg) { + unsigned char *tagp; if (argc == 7) { /* Finalize the encrypted text */ if (EVP_CipherFinal_ex(ctx, outp, &len) != 1) @@ -314,7 +344,7 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] /* Get the tag */ if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL) {ret = EXCP_ERROR(env, "Can't make 'Out' binary"); goto done;} - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) + if (EVP_CIPHER_CTX_ctrl(ctx, aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) {ret = EXCP_ERROR(env, "Can't get Tag"); goto done;} /* Make the return value (the tuple with binary crypto text and the tag) */ @@ -324,7 +354,7 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] {ret = EXCP_ERROR(env, "Encrypt error"); goto done;} /* Add tag to output end */ tagp = outp + in_len; - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) + if (EVP_CIPHER_CTX_ctrl(ctx, aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) {ret = EXCP_ERROR(env, "Can't get Tag"); goto done;} ret = out; } @@ -333,8 +363,8 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] { #if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) /* Check the Tag before returning. CCM_MODE does this previously. */ - if (!(cipherp->flags & CCM_MODE)) { /* That is, CHACHA20_POLY1305 or GCM_MODE */ - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1) + if (!flags.ccm_mode) { /* That is, CHACHA20_POLY1305 or GCM_MODE */ + if (EVP_CIPHER_CTX_ctrl(ctx, aead.ctx_ctrl_set_tag, (int)tag_len, tag_data) != 1) /* Decrypt error */ {ret = atom_error; goto done;} /* CCM dislikes EVP_DecryptFinal_ex on decrypting for pre 1.1.1, so we do it only here */ diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 2e1465e15e43..109dae3a800b 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -20,729 +20,88 @@ * %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" +#include "algorithms_rsaopt.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_algorithms_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[]) { + rsaopts_lazy_init(env, FIPS_MODE()); + return rsaopts_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_algorithms_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..871f31241919 --- /dev/null +++ b/lib/crypto/c_src/algorithms_cipher.cpp @@ -0,0 +1,504 @@ +/* + * %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" + +#include + +cipher_probe_t cipher_probes[] = { +#ifdef HAVE_RC2 +# define CIPHER_RC2_CTOR &EVP_rc2_cbc +#else +# define CIPHER_RC2_CTOR nullptr +#endif + {.str = "rc2_cbc", .str_v3 = "rc2-cbc", .ctor_v1 = CIPHER_RC2_CTOR, .flags = {.fips_forbidden = true}}, +#undef CIPHER_RC2_CTOR +// -------------------------- +#ifdef HAVE_RC4 +# define CIPHER_RC4_CTOR &EVP_rc4 +#else +# define CIPHER_RC4_CTOR nullptr +#endif + {.str = "rc4", .str_v3 = "rc4", .ctor_v1 = CIPHER_RC4_CTOR, .flags = {.fips_forbidden = true}}, +#undef CIPHER_RC4_CTOR +// -------------------------- +#ifdef HAVE_DES +# define CIPHER_DES_CBC_CTOR &EVP_des_cbc +# define CIPHER_DES_CFB8_CTOR &EVP_des_cfb8 +# define CIPHER_DES_ECB_CTOR &EVP_des_ecb +#else +# define CIPHER_DES_CBC_CTOR nullptr +# define CIPHER_DES_CFB8_CTOR nullptr +# define CIPHER_DES_ECB_CTOR nullptr +#endif + {.str = "des_cbc", .str_v3 = "des-cbc", .ctor_v1 = CIPHER_DES_CBC_CTOR, .flags = {.fips_forbidden = true}}, + {.str = "des_cfb", .str_v3 = "des-cfb", .ctor_v1 = CIPHER_DES_CFB8_CTOR, .flags = {.fips_forbidden = true}}, + {.str = "des_ecb", + .str_v3 = "des-ecb", + .ctor_v1 = CIPHER_DES_ECB_CTOR, + .flags = {.fips_forbidden = true, .ecb_bug_0_9_8l = true}}, +#undef CIPHER_DES_CBC_CTOR +#undef CIPHER_DES_CFB8_CTOR +#undef CIPHER_DES_ECB_CTOR +// -------------------------- +#ifdef HAVE_DES_ede3_cbc +# define CIPHER_DES_EDE3_CBC_CTOR &EVP_des_ede3_cbc +#else +# define CIPHER_DES_EDE3_CBC_CTOR nullptr +#endif + {.str = "des_ede3_cbc", .str_v3 = "des-ede3-cbc", .ctor_v1 = CIPHER_DES_EDE3_CBC_CTOR}, +#undef CIPHER_DES_EDE3_CBC_CTOR +// -------------------------- +#ifdef HAVE_DES_ede3_cfb +# define CIPHER_DES_EDE3_CFB_CTOR &EVP_des_ede3_cfb8 +#else +# define CIPHER_DES_EDE3_CFB_CTOR nullptr +#endif + {.str = "des_ede3_cfb", .str_v3 = "des-ede3-cfb", .ctor_v1 = CIPHER_DES_EDE3_CFB_CTOR}, +#undef CIPHER_DES_EDE3_CFB_CTOR +// -------------------------- +#ifdef HAVE_BF +# define CIPHER_BF_CBC_CTOR &EVP_bf_cbc +# define CIPHER_BF_CFB64_CTOR &EVP_bf_cfb64 +# define CIPHER_BF_OFB_CTOR &EVP_bf_ofb +# define CIPHER_BF_ECB_CTOR &EVP_bf_ecb +#else +# define CIPHER_BF_CBC_CTOR nullptr +# define CIPHER_BF_CFB64_CTOR nullptr +# define CIPHER_BF_OFB_CTOR nullptr +# define CIPHER_BF_ECB_CTOR nullptr +#endif + {.str = "blowfish_cbc", .str_v3 = "BF-CBC", .ctor_v1 = CIPHER_BF_CBC_CTOR, .flags = {.fips_forbidden = true}}, + {.str = "blowfish_cfb64", + .str_v3 = "BF-CFB", + .ctor_v1 = CIPHER_BF_CFB64_CTOR, + .flags = {.fips_forbidden = true}}, + {.str = "blowfish_ofb64", .str_v3 = "BF-OFB", .ctor_v1 = CIPHER_BF_OFB_CTOR, .flags = {.fips_forbidden = true}}, + {.str = "blowfish_ecb", + .str_v3 = "BF-ECB", + .ctor_v1 = CIPHER_BF_ECB_CTOR, + .flags = {.fips_forbidden = true, .ecb_bug_0_9_8l = true}}, +#undef CIPHER_BF_CBC_CTOR +#undef CIPHER_BF_CFB64_CTOR +#undef CIPHER_BF_OFB_CTOR +#undef CIPHER_BF_ECB_CTOR +// -------------------------- +#ifdef HAVE_SM4 +# define CIPHER_SM4_CBC_CTOR &EVP_sm4_cbc +# define CIPHER_SM4_ECB_CTOR &EVP_sm4_ecb +# define CIPHER_SM4_CFB_CTOR &EVP_sm4_cfb +# define CIPHER_SM4_OFB_CTOR &EVP_sm4_ofb +# define CIPHER_SM4_CTR_CTOR &EVP_sm4_ctr +#else +# define CIPHER_SM4_CBC_CTOR nullptr +# define CIPHER_SM4_ECB_CTOR nullptr +# define CIPHER_SM4_CFB_CTOR nullptr +# define CIPHER_SM4_OFB_CTOR nullptr +# define CIPHER_SM4_CTR_CTOR nullptr +#endif + {.str = "sm4_cbc", + .str_v3 = "sm4-cbc", + .ctor_v1 = CIPHER_SM4_CBC_CTOR, + .key_len = 16, + .flags = {.fips_forbidden = true}}, + {.str = "sm4_ecb", + .str_v3 = "sm4-ecb", + .ctor_v1 = CIPHER_SM4_ECB_CTOR, + .key_len = 16, + .flags = {.fips_forbidden = true}}, + {.str = "sm4_cfb", + .str_v3 = "sm4-cfb", + .ctor_v1 = CIPHER_SM4_CFB_CTOR, + .key_len = 16, + .flags = {.fips_forbidden = true}}, + {.str = "sm4_ofb", + .str_v3 = "sm4-ofb", + .ctor_v1 = CIPHER_SM4_OFB_CTOR, + .key_len = 16, + .flags = {.fips_forbidden = true}}, + {.str = "sm4_ctr", + .str_v3 = "sm4-ctr", + .ctor_v1 = CIPHER_SM4_CTR_CTOR, + .key_len = 16, + .flags = {.fips_forbidden = true}}, +#undef CIPHER_SM4_CBC_CTOR +#undef CIPHER_SM4_ECB_CTOR +#undef CIPHER_SM4_CFB_CTOR +#undef CIPHER_SM4_OFB_CTOR +#undef CIPHER_SM4_CTR_CTOR + // -------------------------- + {.str = "aes_128_cbc", .str_v3 = "aes-128-cbc", .ctor_v1 = &EVP_aes_128_cbc, .key_len = 16}, + {.str = "aes_192_cbc", .str_v3 = "aes-192-cbc", .ctor_v1 = &EVP_aes_192_cbc, .key_len = 24}, + {.str = "aes_256_cbc", .str_v3 = "aes-256-cbc", .ctor_v1 = &EVP_aes_256_cbc, .key_len = 32}, + + {.str = "aes_128_ofb", .str_v3 = "aes-128-ofb", .ctor_v1 = &EVP_aes_128_ofb, .key_len = 16}, + {.str = "aes_192_ofb", .str_v3 = "aes-192-ofb", .ctor_v1 = &EVP_aes_192_ofb, .key_len = 24}, + {.str = "aes_256_ofb", .str_v3 = "aes-256-ofb", .ctor_v1 = &EVP_aes_256_ofb, .key_len = 32}, + + {.str = "aes_128_cfb8", + .str_v3 = "aes-128-cfb8", + .ctor_v1 = &EVP_aes_128_cfb8, + .key_len = 16, + .flags = {.aes_cfbx = true}}, + {.str = "aes_192_cfb8", + .str_v3 = "aes-192-cfb8", + .ctor_v1 = &EVP_aes_192_cfb8, + .key_len = 24, + .flags = {.aes_cfbx = true}}, + {.str = "aes_256_cfb8", + .str_v3 = "aes-256-cfb8", + .ctor_v1 = &EVP_aes_256_cfb8, + .key_len = 32, + .flags = {.aes_cfbx = true}}, + + {.str = "aes_128_cfb128", + .str_v3 = "aes-128-cfb", + .ctor_v1 = &EVP_aes_128_cfb128, + .key_len = 16, + .flags = {.aes_cfbx = true}}, + {.str = "aes_192_cfb128", + .str_v3 = "aes-192-cfb", + .ctor_v1 = &EVP_aes_192_cfb128, + .key_len = 24, + .flags = {.aes_cfbx = true}}, + {.str = "aes_256_cfb128", + .str_v3 = "aes-256-cfb", + .ctor_v1 = &EVP_aes_256_cfb128, + .key_len = 32, + .flags = {.aes_cfbx = true}}, + + {.str = "aes_128_ecb", + .str_v3 = "aes-128-ecb", + .ctor_v1 = &EVP_aes_128_ecb, + .key_len = 16, + .flags = {.ecb_bug_0_9_8l = true}}, + {.str = "aes_192_ecb", + .str_v3 = "aes-192-ecb", + .ctor_v1 = &EVP_aes_192_ecb, + .key_len = 24, + .flags = {.ecb_bug_0_9_8l = true}}, + {.str = "aes_256_ecb", + .str_v3 = "aes-256-ecb", + .ctor_v1 = &EVP_aes_256_ecb, + .key_len = 32, + .flags = {.ecb_bug_0_9_8l = true}}, +// -------------------------- +#if defined(HAVE_EVP_AES_CTR) +# define CIPHER_AES128_CTR_CTOR &EVP_aes_128_ctr +# define CIPHER_AES192_CTR_CTOR &EVP_aes_192_ctr +# define CIPHER_AES256_CTR_CTOR &EVP_aes_256_ctr +#else +# define CIPHER_AES128_CTR_CTOR nullptr +# define CIPHER_AES192_CTR_CTOR nullptr +# define CIPHER_AES256_CTR_CTOR nullptr +#endif + {.str = "aes_128_ctr", .str_v3 = "aes-128-ctr", .ctor_v1 = CIPHER_AES128_CTR_CTOR, .key_len = 16}, + {.str = "aes_192_ctr", .str_v3 = "aes-192-ctr", .ctor_v1 = CIPHER_AES192_CTR_CTOR, .key_len = 24}, + {.str = "aes_256_ctr", .str_v3 = "aes-256-ctr", .ctor_v1 = CIPHER_AES256_CTR_CTOR, .key_len = 32}, +#undef CIPHER_AES128_CTR_CTOR +#undef CIPHER_AES192_CTR_CTOR +#undef CIPHER_AES256_CTR_CTOR +// -------------------------- +#if defined(HAVE_CHACHA20) +# define CIPHER_CHACHA20_CTOR &EVP_chacha20 +#else +# define CIPHER_CHACHA20_CTOR nullptr +#endif + {.str = "chacha20", .ctor_v1 = CIPHER_CHACHA20_CTOR, .key_len = 32, .flags = {.fips_forbidden = true}}, +#undef CIPHER_CHACHA20_CTOR + +// -------------------------- +// AEAD ciphers +// -------------------------- + +#if defined(HAVE_CHACHA20_POLY1305) +# define CIPHER_CHACHA20_POLY1305_CTOR &EVP_chacha20_poly1305 +# define CIPHER_CHACHA20_POLY1305_AEADCTRL AEAD_CTRL +#else +# define CIPHER_CHACHA20_POLY1305_CTOR nullptr +# define CIPHER_CHACHA20_POLY1305_AEADCTRL NOT_AEAD +#endif +#ifdef HAVE_AEAD + {.str = "chacha20_poly1305", + .str_v3 = "chacha20-poly1305", + .ctor_v1 = CIPHER_CHACHA20_POLY1305_CTOR, + .flags = {.fips_forbidden = true, .aead_cipher = true}, + .aead_ctrl_type = CIPHER_CHACHA20_POLY1305_AEADCTRL}, +#endif // HAVE_AEAD +#undef CIPHER_CHACHA20_POLY1305_CTOR +#undef CIPHER_CHACHA20_POLY1305_AEADCTRL +// -------------------------- +#if defined(HAVE_SM4_GCM) + {.str = "sm4_gcm", + .str_v3 = "sm4-gcm", + .key_len = 16, + .flags = {.fips_forbidden = true, .aead_cipher = true, .gcm_mode = true}, + .aead_ctrl_type = AEAD_CTRL}, +#endif +// -------------------------- +#if defined(HAVE_SM4_CCM) + {.str = "sm4_ccm", + .str_v3 = "sm4-ccm", + .key_len = 16, + .flags = {.fips_forbidden = true, .aead_cipher = true, .ccm_mode = true}, + .aead_ctrl_type = AEAD_CTRL}, +#endif +// -------------------------- +#if defined(HAVE_GCM) +# define CIPHER_AES128_GCM_CTOR &EVP_aes_128_gcm +# define CIPHER_AES192_GCM_CTOR &EVP_aes_192_gcm +# define CIPHER_AES256_GCM_CTOR &EVP_aes_256_gcm +# if defined(HAS_3_0_API) +# define CIPHER_AES_GCM_AEADCTRL AEAD_CTRL +# else +# define CIPHER_AES_GCM_AEADCTRL AEAD_CTRL_GCM +# endif +#else // not HAVE_GCM +# define CIPHER_AES128_GCM_CTOR nullptr +# define CIPHER_AES192_GCM_CTOR nullptr +# define CIPHER_AES256_GCM_CTOR nullptr +# define CIPHER_AES_GCM_AEADCTRL NOT_AEAD +#endif +#ifdef HAVE_AEAD + {.str = "aes_128_gcm", + .str_v3 = "aes-128-gcm", + .ctor_v1 = CIPHER_AES128_GCM_CTOR, + .key_len = 16, + .flags = {.aead_cipher = true, .gcm_mode = true}, + .aead_ctrl_type = CIPHER_AES_GCM_AEADCTRL}, + {.str = "aes_192_gcm", + .str_v3 = "aes-192-gcm", + .ctor_v1 = CIPHER_AES192_GCM_CTOR, + .key_len = 24, + .flags = {.aead_cipher = true, .gcm_mode = true}, + .aead_ctrl_type = CIPHER_AES_GCM_AEADCTRL}, + {.str = "aes_256_gcm", + .str_v3 = "aes-256-gcm", + .ctor_v1 = CIPHER_AES256_GCM_CTOR, + .key_len = 32, + .flags = {.aead_cipher = true, .gcm_mode = true}, + .aead_ctrl_type = CIPHER_AES_GCM_AEADCTRL}, +#endif // HAVE_AEAD +#undef CIPHER_AES128_GCM_CTOR +#undef CIPHER_AES192_GCM_CTOR +#undef CIPHER_AES256_GCM_CTOR +#undef CIPHER_AES_GCM_AEADCTRL +// -------------------------- +#if defined(HAVE_CCM) +# define CIPHER_AES128_CCM_CTOR &EVP_aes_128_ccm +# define CIPHER_AES192_CCM_CTOR &EVP_aes_192_ccm +# define CIPHER_AES256_CCM_CTOR &EVP_aes_256_ccm +# if defined(HAS_3_0_API) +# define CIPHER_AES_CCM_AEADCTRL AEAD_CTRL +# else +# define CIPHER_AES_CCM_AEADCTRL AEAD_CTRL_CCM +# endif +#else // not HAVE_GCM +# define CIPHER_AES128_CCM_CTOR nullptr +# define CIPHER_AES192_CCM_CTOR nullptr +# define CIPHER_AES256_CCM_CTOR nullptr +# define CIPHER_AES_CCM_AEADCTRL NOT_AEAD +#endif +#ifdef HAVE_AEAD + {.str = "aes_128_ccm", + .str_v3 = "aes-128-ccm", + .ctor_v1 = CIPHER_AES128_CCM_CTOR, + .key_len = 16, + .flags = {.aead_cipher = true, .ccm_mode = true}, + .aead_ctrl_type = CIPHER_AES_CCM_AEADCTRL}, + {.str = "aes_192_ccm", + .str_v3 = "aes-192-ccm", + .ctor_v1 = CIPHER_AES192_CCM_CTOR, + .key_len = 24, + .flags = {.aead_cipher = true, .ccm_mode = true}, + .aead_ctrl_type = CIPHER_AES_CCM_AEADCTRL}, + {.str = "aes_256_ccm", + .str_v3 = "aes-256-ccm", + .ctor_v1 = CIPHER_AES256_CCM_CTOR, + .key_len = 32, + .flags = {.aead_cipher = true, .ccm_mode = true}, + .aead_ctrl_type = CIPHER_AES_CCM_AEADCTRL}, +#endif // HAVE_AEAD +#undef CIPHER_AES128_CCM_CTOR +#undef CIPHER_AES192_CCM_CTOR +#undef CIPHER_AES256_CCM_CTOR +#undef CIPHER_AES_CCM_AEADCTRL +}; + +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) { + const auto result = cipher_collection.lazy_init(env, fips_enabled); + std::sort(cipher_collection.begin(), cipher_collection.end(), cipher_type_t::compare_function); + return result; +} + +// Partial order compare, returns a.atom < b.atom && a.key < b.key +bool cipher_type_t::compare_function(const cipher_type_t &a, const cipher_type_t &b) { + return (a.atom < b.atom) || (a.atom == b.atom && a.key_len < b.key_len); +} + +// Partial order compare, returns a.atom < b.atom +bool cipher_type_t::compare_function_no_key(const cipher_type_t &a, const cipher_type_t &b) { + return a.atom < b.atom; +} + +// 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_type_t::get_atom() const { + return this->init->atom; +} + +#if defined(HAS_3_0_API) +// Initialize an algorithm to check that all its dependencies are valid +bool cipher_type_t::can_cipher_be_instantiated() const { + const auto_cipher_ctx_t ctx(EVP_CIPHER_CTX_new()); + + if (this->resource) { + constexpr uint8_t key[64] = {}; + constexpr uint8_t iv[32] = {}; + // Try to initialize the cipher in encryption mode + if (EVP_CipherInit_ex(ctx.pointer, this->resource.pointer, nullptr, key, iv, 1) == 1) { + return true; + } + } + return false; +} +#endif // HAS_3_0_API + +void cipher_type_t::check_fips_availability(const bool fips_enabled) { +#ifdef HAS_3_0_API + const auto name = this->init->get_v3_name(); + if (name) { +# ifdef FIPS_SUPPORT + if (fips_enabled) { + if (this->flags.fips_forbidden) { + // Shortcut when the fips_forbidden flag is already set from the probe data + return; + } + this->resource.reset(EVP_CIPHER_fetch(nullptr, name, "fips=yes")); + if (!this->can_cipher_be_instantiated()) { + this->flags.fips_forbidden = true; + this->resource.reset(nullptr); // free the resource + } + } +# else + this->resource.reset(EVP_CIPHER_fetch(nullptr, name, "")); + if (!this->can_cipher_be_instantiated()) { + this->flags.algorithm_init_failed = true; + this->resource.reset(nullptr); // free the resource + } +# endif // FIPS_SUPPORT and >=3.0.0 + } +#else + if (this->init->ctor_v1) { + // Construct from the provided function + this->resource.reset(this->init->ctor_v1()); + } +#endif +} + +void cipher_type_t::check_availability(bool fips_enabled) { + if (this->init->ctor_v1) { + this->resource.reset(this->init->ctor_v1()); // take ownership on the cipher + } +#ifdef HAVE_AEAD + switch (this->init->aead_ctrl_type) { + case NOT_AEAD: + this->aead = {0, 0, 0}; + break; + case AEAD_CTRL: +#if defined(HAVE_3_0_API) + // This is defined in a much later version of OpenSSL like 3.x, AEAD was available from 1.0.1 + this->aead = {EVP_CTRL_AEAD_SET_IVLEN, EVP_CTRL_AEAD_GET_TAG, EVP_CTRL_AEAD_SET_TAG}; +#endif + break; + case AEAD_CTRL_GCM: + this->aead = {EVP_CTRL_GCM_SET_IVLEN, EVP_CTRL_GCM_GET_TAG, EVP_CTRL_GCM_SET_TAG}; + break; + case AEAD_CTRL_CCM: + this->aead = {EVP_CTRL_CCM_SET_IVLEN, EVP_CTRL_CCM_GET_TAG, EVP_CTRL_CCM_SET_TAG}; + break; + } +#endif // HAVE_AEAD + // We do not know for sure that the algorithm is unavailable (normal or FIPS) + this->check_fips_availability(fips_enabled); +} + +// 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, this->atom); + output.emplace_back(this, this->atom, this->key_len, this->flags); // construct in place + auto &algo = output.back(); + algo.check_availability(fips_enabled); +} + +extern "C" const cipher_type_C *get_cipher_type(ERL_NIF_TERM type, size_t key_len) { + cipher_type_t sample(type, key_len); + auto result = std::lower_bound(cipher_collection.cbegin(), + cipher_collection.cend(), + sample, + cipher_type_t::compare_function); + if (result != cipher_collection.cend() && result->eq(sample)) { + return &*result; + } + return nullptr; +} + +extern "C" const cipher_type_C *get_cipher_type_no_key(ERL_NIF_TERM type) { + cipher_type_t sample(type); + auto result = std::lower_bound(cipher_collection.cbegin(), + cipher_collection.cend(), + sample, + cipher_type_t::compare_function_no_key); + if (result != cipher_collection.cend() && result->eq_no_key(sample)) { + return &*result; + } + return nullptr; +} + +extern "C" cipher_type_flags_t get_cipher_type_flags(const cipher_type_C *p) { + return p->flags; +} +extern "C" cipher_type_aead_t get_cipher_type_aead(const cipher_type_C *p) { + return p->aead; +} +extern "C" bool is_cipher_forbidden_in_fips(const cipher_type_C *p) { + return p->is_forbidden_in_fips(); +} +extern "C" const EVP_CIPHER *get_cipher_type_resource(const cipher_type_C *p) { + return p->resource.pointer; +} +extern "C" const char *get_cipher_type_str_v3(const cipher_type_C *p) { + return p->init->get_v3_name(); +} diff --git a/lib/crypto/c_src/algorithms_cipher.h b/lib/crypto/c_src/algorithms_cipher.h new file mode 100644 index 000000000000..485c1704af7f --- /dev/null +++ b/lib/crypto/c_src/algorithms_cipher.h @@ -0,0 +1,158 @@ +/* + * %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" + +typedef struct cipher_type_t cipher_type_C; +struct cipher_type_flags_t { + bool fips_forbidden : 1; + bool algorithm_init_failed : 1; + bool aes_cfbx : 1; + bool ecb_bug_0_9_8l : 1; + bool aead_cipher : 1; + bool non_evp_cipher : 1; + bool aes_ctr_compat : 1; + bool ccm_mode : 1; + bool gcm_mode : 1; +}; +struct cipher_type_aead_t { + int ctx_ctrl_set_ivlen, ctx_ctrl_get_tag, ctx_ctrl_set_tag; +}; + +// +// 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); +const cipher_type_C *get_cipher_type(ERL_NIF_TERM type, size_t key_len); +const cipher_type_C *get_cipher_type_no_key(ERL_NIF_TERM type); +struct cipher_type_flags_t get_cipher_type_flags(const cipher_type_C *p); +struct cipher_type_aead_t get_cipher_type_aead(const cipher_type_C *p); +bool is_cipher_forbidden_in_fips(const cipher_type_C *p); +const EVP_CIPHER *get_cipher_type_resource(const cipher_type_C *p); +const char *get_cipher_type_str_v3(const cipher_type_C *p); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +#include "algorithms_collection.h" +#include "auto_openssl_resource.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_type_t { + const cipher_probe_t *init = nullptr; // the cipher_probe_t used to create this record + auto_cipher_t resource{}; + cipher_type_aead_t aead{}; + ERL_NIF_TERM atom = 0; // copy of init->atom + size_t key_len = 0; // copy of init->key_len + + cipher_type_flags_t flags{}; + + cipher_type_t(const cipher_probe_t *init_, ERL_NIF_TERM atom, const size_t key_len, cipher_type_flags_t flags_) : + init(init_), atom(atom), key_len(key_len), flags(flags_) {} + explicit cipher_type_t(ERL_NIF_TERM atom, const size_t key_len=0) : + atom(atom), key_len(key_len) {} + + bool is_forbidden_in_fips() const { +#ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +#else + return false; +#endif + } + bool is_available() const { + // Available if cipher could be initialized, and (has a EVP_CIPHER resource, or is AES_CTR compatible) + return !this->flags.algorithm_init_failed; + } + // Return the atom which goes to the Erlang caller + ERL_NIF_TERM get_atom() const; + void check_availability(bool fips_enabled); + // Partial order compare, returns a.atom < b.atom && a.key < b.key + static bool compare_function(const cipher_type_t &a, const cipher_type_t &b); + // Partial order compare, returns a.atom < b.atom + static bool compare_function_no_key(const cipher_type_t &a, const cipher_type_t &b); + bool eq(const cipher_type_t &other) const { return this->atom == other.atom && this->key_len == other.key_len; } + bool eq_no_key(const cipher_type_t &other) const { return this->atom == other.atom; } + +private: + void check_fips_availability(bool fips_enabled); + bool can_cipher_be_instantiated() const; +}; + +#ifdef HAVE_AEAD +enum AEAD_CTRL_TYPE { + // Writes {{0,0,0}} into cipher_availability_t::aead + NOT_AEAD, + // Writes {{EVP_CTRL_AEAD_SET_IVLEN, EVP_CTRL_AEAD_GET_TAG, EVP_CTRL_AEAD_SET_TAG}} + // into cipher_availability_t::aead + AEAD_CTRL, + // Writes {{EVP_CTRL_GCM_SET_IVLEN, EVP_CTRL_GCM_GET_TAG, EVP_CTRL_GCM_SET_TAG}}} + // into cipher_availability_t::aead + AEAD_CTRL_GCM, + // Writes {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}} + // into cipher_availability_t::aead + AEAD_CTRL_CCM, +}; +#endif // HAVE_AEAD + +// A 0-argument function returning EVP_CIPHER used to construct supported ciphers +using cipher_constructor_fn_t = const EVP_CIPHER *(*) (); + +// 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; + // can be null, then str is used + const char *str_v3; + // constructor for OpenSSL < 3.0 (can be null) + cipher_constructor_fn_t ctor_v1; + ERL_NIF_TERM atom; + size_t key_len; + // initial value for the flags + cipher_type_flags_t flags; +#ifdef HAVE_AEAD + // determines which value goes into cipher_availability_t::aead + AEAD_CTRL_TYPE aead_ctrl_type; +#endif // HAVE_AEAD + + const char *get_v3_name() const { return this->str_v3 ? this->str_v3 : this->str; } + // 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..132d9ce48a8f --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.cpp @@ -0,0 +1,71 @@ +/* + * %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" + +#include + +extern "C" bool create_algorithm_mutexes() { + return cipher_collection.create_mutex() && curve_collection.create_mutex() && digest_collection.create_mutex() && + kem_collection.create_mutex() && mac_collection.create_mutex() && pubkey_collection.create_mutex() && + rsaopt_collection.create_mutex(); +} + +extern "C" void free_algorithm_mutexes(void) { + cipher_collection.destroy_mutex(); + curve_collection.destroy_mutex(); + digest_collection.destroy_mutex(); + kem_collection.destroy_mutex(); + mac_collection.destroy_mutex(); + pubkey_collection.destroy_mutex(); + rsaopt_collection.destroy_mutex(); +} + +extern "C" void algorithms_reset_cache() { + cipher_collection.reset(); + curve_collection.reset(); + digest_collection.reset(); + kem_collection.reset(); + mac_collection.reset(); + pubkey_collection.reset(); + rsaopt_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) { + ASSERT(atom_name != nullptr); + ASSERT(std::strlen(atom_name) > 0); + if (!atom) { + enif_make_existing_atom(env, atom_name, &atom, ERL_NIF_UTF8); + if (!atom) { + atom = enif_make_atom(env, atom_name); + } + } + return atom; +} diff --git a/lib/crypto/c_src/algorithms_collection.h b/lib/crypto/c_src/algorithms_collection.h new file mode 100644 index 000000000000..e8fb19a39ba7 --- /dev/null +++ b/lib/crypto/c_src/algorithms_collection.h @@ -0,0 +1,184 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#pragma once +#include + +#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; + size_t probes_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 probes_count_) + : lazy_init_done(false), probes(probes_), probes_count(probes_count_), mutex(nullptr), + debug_name(debug_name) { + } + + ~algorithm_collection_t() { + destroy_mutex(); + } + + // Const pointer to start of algorithms + auto cbegin() const -> decltype(this->algorithms.cbegin()) { + return this->algorithms.cbegin(); + } + // Const pointer to one after last of the algorithms + auto cend() const -> decltype(this->algorithms.cend()) { + return this->algorithms.cend(); + } + + // Mutable pointer to start of algorithms + auto begin() -> decltype(this->algorithms.begin()) { + return this->algorithms.begin(); + } + // Mutable pointer to one after last of the algorithms + auto end() -> decltype(this->algorithms.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 (auto i = 0; i < probes_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; + } + + // Search for algorithms which have is_forbidden_in_fips() return true for them. + ERL_NIF_TERM to_list(ErlNifEnv *env, const bool match_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() == match_fips_forbidden) { + const auto atom = algo.get_atom(); + ASSERT(atom != 0); + hd = enif_make_list_cell(env, 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..4aa853c47c68 --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.cpp @@ -0,0 +1,490 @@ +/* + * %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" + +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) +#include +#endif // FIPS && OPENSSL 3.0 + +#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() const { +#ifdef HAVE_AUTO_PKEY_T + auto_pkey_t pkey; + auto_pkey_t params; + + const auto_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_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 +#ifdef HAVE_AUTO_KEY_V1_T + auto_key_v1_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_AUTO_PKEY_T + return false; +} + +ERL_NIF_TERM curve_type_t::get_atom() const { return this->init->atom; } + +void curve_type_t::check_fips_availability(const 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_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_pkey_t pkey(nullptr); + if (EVP_PKEY_generate(pctx.pointer, &pkey.pointer) <= 0) { + this->flags.algorithm_init_failed = true; + } +#endif +} + +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); + + // 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 + } + + // Construct in the container directly, passing 'this' to the ctor + output.emplace_back(this); + auto &algo = output.back(); + algo.check_fips_availability(fips_mode); +} diff --git a/lib/crypto/c_src/algorithms_curve.h b/lib/crypto/c_src/algorithms_curve.h new file mode 100644 index 000000000000..b612f6e17604 --- /dev/null +++ b/lib/crypto/c_src/algorithms_curve.h @@ -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% + */ + +#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_type_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 = {}; + + explicit curve_type_t(const curve_probe_t *probe) : init(probe) {} + 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 check_fips_availability(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; // NID_xxxx value (an OpenSSL macro) + const char *sn; // serves as Erlang atom name, also equal to SN_xxxxx macro of OpenSSL + ERL_NIF_TERM atom; // 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); + +#ifdef HAVE_EC +private: + bool is_curve_valid_by_nid() const; // used by the probe() to check this->nid +#endif // HAVE_EC +}; + +// 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..ebea5f40c57f --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.cpp @@ -0,0 +1,165 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "algorithms_digest.h" +#include + +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", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha1}, +#ifdef HAVE_SHA224 + {.str = "sha224", .str_v3 = "SHA2-224", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha224}, +#endif +#ifdef HAVE_SHA256 + {.str = "sha256", .str_v3 = "SHA2-256", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha256}, +#endif +#ifdef HAVE_SHA384 + {.str = "sha384", .str_v3 = "SHA2-384", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha384}, +#endif +#ifdef HAVE_SHA512 + {.str = "sha512", .str_v3 = "SHA2-512", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha512}, +#endif +#ifdef HAVE_SHA512_224 + {.str = "sha512_224", .str_v3 = "SHA2-512/224", .flags = {.pbkdf2_eligible = true}, .v1_ctor = &EVP_sha512_224}, +#endif +#ifdef HAVE_SHA512_256 + {.str = "sha512_256", .str_v3 = "SHA2-512/256", .flags = {.pbkdf2_eligible = 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])); + +// +// Implementation of Pubkey Algorithm storage API +// + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" void digest_types_lazy_init(ErlNifEnv *env, const bool fips_enabled) { + digest_collection.lazy_init(env, fips_enabled); +} + +// C API: Proxy the call to generic algorithm_collection_t +extern "C" ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, const bool fips_forbidden) { + return digest_collection.to_list(env, fips_forbidden); +} + +ERL_NIF_TERM digest_type_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_type_t::check_valid_in_fips(const EVP_MD *md) { + if (md) { + const auto_md_ctx_t ctx(EVP_MD_CTX_new()); + // Try to initialize the digest algorithm for use, this will check the dependencies + if (EVP_DigestInit_ex(ctx.pointer, md, nullptr) == 1) { + return true; + } + } + return false; +} +#endif // FIPS_SUPPORT && HAS_3_0_API + +void digest_type_t::create_md_resource(const bool fips_mode) { +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + auto_md_t fetched_md(EVP_MD_fetch(nullptr, this->init->get_v3_name(), nullptr)); + + // Record failed algorithm instantiation for FIPS enabled & OpenSSL API 3.0 only + if (fips_mode && !check_valid_in_fips(fetched_md.pointer)) { + this->flags.fips_forbidden = true; + } else { + this->flags.fips_forbidden = false; + this->resource = std::move(fetched_md); // pass ownership and move data to this->md + } +#else + // construct from the old API, each probe has a constructor function + this->resource.reset(this->init->v1_ctor()); +#endif // HAS_3_0_API && FIPS_SUPPORT +} + +digest_type_t::digest_type_t(const digest_probe_t *init_) : + init(init_), flags(init_->flags), xof_default_length(init_->xof_default_length) {} + +void digest_probe_t::probe(ErlNifEnv *env, const bool fips_mode, std::vector &output) { + output.emplace_back(this); + // Unavailable are skipped. Available are added. Forbidden are added, but with flags.fips_forbidden=true. + auto &algo = output.back(); + algo.create_md_resource(fips_mode); + this->atom = create_or_existing_atom(env, this->str, this->atom); +} + +// Array lookup +extern "C" digest_type_C *get_digest_type(ERL_NIF_TERM type) { + for (auto &p: digest_collection) { + if (type == p.get_atom()) { + return &p; + } + } + return nullptr; +} + +extern "C" bool is_digest_forbidden_in_fips(const digest_type_C *p) { + return p ? p->is_forbidden_in_fips() : true; // forbidden if p is null +} + +extern "C" const char *get_digest_type_str_v3(const digest_type_C *p) { return p->init->get_v3_name(); } + +extern "C" const EVP_MD *get_digest_type_resource(const digest_type_C *p) { return p ? p->resource.pointer : nullptr; } + +extern "C" size_t get_digest_type_xof_default_length(const digest_type_C *p) { return p ? p->xof_default_length : 0; } + +extern "C" bool is_digest_eligible_for_pbkdf2(const digest_type_C *p) { return p ? p->flags.pbkdf2_eligible : false; } diff --git a/lib/crypto/c_src/algorithms_digest.h b/lib/crypto/c_src/algorithms_digest.h new file mode 100644 index 000000000000..55a5ca9a6dea --- /dev/null +++ b/lib/crypto/c_src/algorithms_digest.h @@ -0,0 +1,126 @@ +/* + * %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_type_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 + typedef struct digest_type_t digest_type_C; + + // + // C Digest storage API + // + void digest_types_lazy_init(ErlNifEnv *env, bool fips_enabled); + ERL_NIF_TERM digest_types_as_list(ErlNifEnv *env, bool fips_forbidden); + + // Lookup and access fields + digest_type_C *get_digest_type(ERL_NIF_TERM type); // linear lookup by atom + bool is_digest_forbidden_in_fips(const digest_type_C *p); // access C++ member from C + const EVP_MD *get_digest_type_resource(const digest_type_C *p); // access field + size_t get_digest_type_xof_default_length(const digest_type_C *p); // access field + const char *get_digest_type_str_v3(const digest_type_C *p); // access str_v3 name (field of probe) + bool is_digest_eligible_for_pbkdf2(const digest_type_C *p); // check PBKDF2 availability bit + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +# include "algorithms_collection.h" +# include "auto_openssl_resource.h" + +struct digest_type_flags_t { + bool fips_forbidden : 1; + bool pbkdf2_eligible : 1; +}; + +// 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_type_t { + // The definition used to create this record + const struct digest_probe_t *init = nullptr; + digest_type_flags_t flags = {}; + // after init will contain the algorithm pointer, NULL if not supported. Frees automatically. + auto_md_t resource; + // 0 or default digest length for XOF digests + size_t xof_default_length = 0; + + explicit digest_type_t(const digest_probe_t *init_); + + bool is_forbidden_in_fips() const { +# ifdef FIPS_SUPPORT + return this->flags.fips_forbidden && FIPS_MODE(); +# else + return false; +# endif + } + static bool is_available() { + 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 +}; + +using digest_construction_fn_t = const EVP_MD *(*)(); + +// 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; + // the algorithm name as in OpenSSL 3.x + const char *str_v3; + // This will be updated to created atomfound exi + ERL_NIF_TERM atom; + const digest_type_flags_t flags; + // OpenSSL 1.0 API to create a resource for this digest algorithm (not used in 3.0 API) + digest_construction_fn_t v1_ctor; + size_t xof_default_length; + + const char *get_v3_name() const { + return this->str_v3 ? this->str_v3 : this->str; + } + // 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..6bbf57bb274a --- /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_type_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_type_t::check_kem_algorithm(bool fips_enabled) { +#ifdef HAVE_ML_KEM +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + const auto_kem_t kem(EVP_KEM_fetch(nullptr, this->init->str_v3, nullptr)); + if (!kem) { + return false; // not available by name + } + + const auto_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_type_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..746879b15639 --- /dev/null +++ b/lib/crypto/c_src/algorithms_kem.h @@ -0,0 +1,85 @@ +/* + * %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_type_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; + ERL_NIF_TERM atom; + + // 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..b08cdd91cd1a --- /dev/null +++ b/lib/crypto/c_src/algorithms_mac.cpp @@ -0,0 +1,166 @@ +/* + * %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_type_t::get_atom() const { + return this->init->atom; +} + +bool mac_type_t::is_available() const { + return this->init->type != NO_mac; +} + +void mac_type_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_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_type_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, this->atom); + // No extra checks, just convert name to atom and add + output.emplace_back(this, mac_type_flags_t{.fips_forbidden = this->fips_forbidden_hint}); + output.back().check_fips_availability(fips_enabled); +} + +extern "C" mac_type_C *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 &p; + } + } + return nullptr; +} + +extern "C" mac_type_C *get_mac_type_no_key(ERL_NIF_TERM type) { + for (auto &p : mac_collection) { + if (type == p.get_atom()) { + return &p; + } + } + return nullptr; +} + +extern "C" bool is_mac_forbidden_in_fips(const mac_type_C *p) { + return p ? p->is_forbidden_in_fips() : true; // forbidden if null +} + +extern "C" int get_mac_type_mactype(mac_type_C *p) { + return p ? p->init->type : NO_mac; +} + +#if defined(HAS_3_0_API) +extern "C" EVP_MAC *get_mac_type_resource(mac_type_C *p) { + return p ? p->evp_mac.pointer : nullptr; +} +#endif diff --git a/lib/crypto/c_src/algorithms_mac.h b/lib/crypto/c_src/algorithms_mac.h new file mode 100644 index 000000000000..8a642d3a2e27 --- /dev/null +++ b/lib/crypto/c_src/algorithms_mac.h @@ -0,0 +1,123 @@ +/* + * %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 + typedef struct mac_type_t mac_type_C; + + // + // 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); + mac_type_C *get_mac_type(ERL_NIF_TERM type, size_t key_len); + mac_type_C *get_mac_type_no_key(ERL_NIF_TERM type); + bool is_mac_forbidden_in_fips(const mac_type_C *p); + int get_mac_type_mactype(mac_type_C *p); // access field +#if defined(HAS_3_0_API) + // access field evp_mac (OpenSSL resource) + EVP_MAC *get_mac_type_resource(mac_type_C *p); +#endif + +#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; +struct mac_type_flags_t { + bool algorithm_init_failed : 1; + bool fips_forbidden : 1; +}; + +// 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_type_t { + const mac_probe_t *init = nullptr; // the mac_probe_t used to create this record +# if defined(HAS_3_0_API) + auto_mac_t evp_mac; // OpenSSL resource, frees automatically +# endif + + mac_type_flags_t flags = {}; + + mac_type_t(const mac_probe_t *init_, const mac_type_flags_t flags_) : init(init_), flags(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; + const char *str_v3; + ERL_NIF_TERM atom; + // Suggests that the algorithm is not available in FIPS to skip the probe + bool fips_forbidden_hint; + int pkey_type; // contains EVP_PKEY_* macro (a NID) + MAC_TYPE type; + size_t key_len; + + // 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..88e11c1fa3e9 --- /dev/null +++ b/lib/crypto/c_src/algorithms_pubkey.cpp @@ -0,0 +1,128 @@ +/* + * %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_type_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_type_t::check_against_fips() { + auto_pkey_ctx_t ctx(EVP_PKEY_CTX_new_from_name(nullptr, this->init->get_v3_name(), 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->get_v3_name(), 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->get_v3_name(), 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->get_v3_name(), 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->get_v3_name(), 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, const bool fips_enabled, std::vector &output) { + this->atom = create_or_existing_atom(env, this->str, this->atom); + output.emplace_back(this); +#if defined(FIPS_SUPPORT) && defined(HAS_3_0_API) + auto &algo = output.back(); + if (fips_enabled) { // attempt to instantiate the algorithm and set availability flags + algo.check_against_fips(); + } +#endif // FIPS_SUPPORT && HAS_3_0_API +} diff --git a/lib/crypto/c_src/algorithms_pubkey.h b/lib/crypto/c_src/algorithms_pubkey.h new file mode 100644 index 000000000000..38bb3d5f96d3 --- /dev/null +++ b/lib/crypto/c_src/algorithms_pubkey.h @@ -0,0 +1,103 @@ +/* + * %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; + +struct pubkey_type_flags_t { + 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; +}; + +// 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_type_t { + const pubkey_probe_t *init = nullptr; // the pubkey_probe_t used to create this record + pubkey_type_flags_t flags = {}; + + explicit pubkey_type_t(const pubkey_probe_t *probe) : init(probe) { + } + + 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 + const auto all_ops_forbidden = 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; + return (this->flags.algorithm_init_failed || all_ops_forbidden) && 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; + const char *str_v3; // if this is nullptr, .str will be used instead + ERL_NIF_TERM atom; + + const char *get_v3_name() const { return this->str_v3 ? this->str_v3 : this->str; } + // 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..0725ba819683 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.cpp @@ -0,0 +1,79 @@ +/* + * %CopyrightBegin% + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright Ericsson AB 2010-2025. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#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 rsaopts_as_list(ErlNifEnv *env, const bool fips_enabled) { + return rsaopt_collection.to_list(env, fips_enabled); +} + +ERL_NIF_TERM rsaopt_type_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); + output.emplace_back(this); + // No extra checks, just convert name to atom and add +} diff --git a/lib/crypto/c_src/algorithms_rsaopt.h b/lib/crypto/c_src/algorithms_rsaopt.h new file mode 100644 index 000000000000..8ece177ff776 --- /dev/null +++ b/lib/crypto/c_src/algorithms_rsaopt.h @@ -0,0 +1,85 @@ +/* + * %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_type_t { + const rsaopt_probe_t *init = nullptr; // the rsaopt_probe_t used to create this record + + struct { + bool fips_forbidden : 1; + } flags = {}; + + explicit rsaopt_type_t(const rsaopt_probe_t *init_): init(init_) {} + 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; + ERL_NIF_TERM atom; + + // 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..f54eb19445d0 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -23,6 +23,7 @@ #include "api_ng.h" #include "aes.h" #include "cipher.h" +#include "algorithms_cipher.h" /* * A unified set of functions for encryption/decryption. @@ -38,7 +39,7 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg /* <= 0.9.8l returns faulty ivec length */ # define GET_IV_LEN(Ciph) ((Ciph)->flags & ECB_BUG_0_9_8L) ? 0 : EVP_CIPHER_iv_length((Ciph)->cipher.p) #else -# define GET_IV_LEN(Ciph) EVP_CIPHER_iv_length((Ciph)->cipher.p) +# define GET_IV_LEN(Ciph) EVP_CIPHER_iv_length(get_cipher_type_resource(Ciph)) #endif #if !defined(HAVE_EVP_CIPHER_CTX_COPY) @@ -198,7 +199,7 @@ static int get_init_args(ErlNifEnv* env, int key_arg_num, int ivec_arg_num, int opts_arg_num, - const struct cipher_type_t **cipherp, + const cipher_type_C **cipherp, ERL_NIF_TERM *return_term) { int ivec_len; @@ -238,7 +239,8 @@ static int get_init_args(ErlNifEnv* env, goto err; } - if (!(*cipherp = get_cipher_type(argv[cipher_arg_num], ctx_res->key_bin.size))) + *cipherp = get_cipher_type(argv[cipher_arg_num], ctx_res->key_bin.size); + if (!*cipherp) { if (!get_cipher_type_no_key(argv[cipher_arg_num])) *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Unknown cipher"); @@ -247,14 +249,14 @@ static int get_init_args(ErlNifEnv* env, goto err; } - if ((*cipherp)->flags & AEAD_CIPHER) + if (get_cipher_type_flags(*cipherp).aead_cipher) { *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Missing arguments for this cipher"); goto err; } - 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; @@ -263,13 +265,13 @@ static int get_init_args(ErlNifEnv* env, /* Get ivec_len for this cipher (if we found one) */ #if !defined(HAVE_EVP_AES_CTR) /* This code is for historic OpenSSL where EVP_aes_*_ctr is not defined.... */ - if ((*cipherp)->cipher.p) { + if (get_cipher_type_resource(*cipherp)) { /* Not aes_ctr compatibility code since EVP_* was defined and assigned to (*cipherp)->cipher.p */ ivec_len = GET_IV_LEN(*cipherp); } else { /* No EVP_* was found */ - if ((*cipherp)->flags & AES_CTR_COMPAT) + if (get_cipher_type_flags(*cipherp).aes_ctr_compat) /* Use aes_ctr compatibility code later */ ivec_len = 16; else { @@ -281,9 +283,8 @@ static int get_init_args(ErlNifEnv* env, } #else /* Normal code */ - if (!((*cipherp)->cipher.p)) { - *return_term = - EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version"); + if (!get_cipher_type_resource(*cipherp)) { + *return_term = EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version"); goto err; } ivec_len = GET_IV_LEN(*cipherp); @@ -314,9 +315,9 @@ static int get_init_args(ErlNifEnv* env, ctx_res->iv_len = ivec_len; #if !defined(HAVE_EVP_AES_CTR) - if (!((*cipherp)->cipher.p) - && ((*cipherp)->flags & AES_CTR_COMPAT) - ) { + if (!get_cipher_type_resource(*cipherp) + && get_cipher_type_flags(*cipherp).aes_ctr_compat) + { /* Must use aes_ctr compatibility code */ ERL_NIF_TERM ecount_bin; unsigned char *outp; @@ -351,7 +352,7 @@ static int get_init_args(ErlNifEnv* env, goto err; } - if (!EVP_CipherInit_ex(ctx_res->ctx, (*cipherp)->cipher.p, NULL, NULL, NULL, ctx_res->encflag)) + if (!EVP_CipherInit_ex(ctx_res->ctx, get_cipher_type_resource(*cipherp), NULL, NULL, NULL, ctx_res->encflag)) { *return_term = EXCP_ERROR(env, "Can't initialize context, step 1"); goto err; @@ -364,7 +365,7 @@ static int get_init_args(ErlNifEnv* env, } #ifdef HAVE_RC2 - if (EVP_CIPHER_type((*cipherp)->cipher.p) == NID_rc2_cbc) { + if (EVP_CIPHER_type(get_cipher_type_resource(*cipherp)) == NID_rc2_cbc) { if (ctx_res->key_bin.size > INT_MAX / 8) { *return_term = EXCP_BADARG_N(env, key_arg_num, "To large rc2_cbc key"); 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..60b3f161f890 --- /dev/null +++ b/lib/crypto/c_src/auto_openssl_resource.cpp @@ -0,0 +1,99 @@ +/* + * %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" + +#ifdef HAVE_AUTO_PKEY_T +void auto_pkey_t::free_resource(EVP_PKEY *p) { + if (p) { + EVP_PKEY_free(p); + } +} + +void auto_pkey_ctx_t::free_resource(EVP_PKEY_CTX *p) { + if (p) { + EVP_PKEY_CTX_free(p); + } +} +#endif // HAVE_AUTO_PKEY_T + +#ifdef HAVE_AUTO_KEY_V1_T +void auto_key_v1_t::free_resource(EC_KEY *p) { + if (p) { + EC_KEY_free(p); + } +} +#endif // HAVE_AUTO_KEY_V1_T + +#ifdef HAVE_ML_KEM +void auto_kem_t::free_resource(EVP_KEM *p) { + if (p) { + EVP_KEM_free(p); + } +} +#endif + +#if defined(HAS_3_0_API) +void auto_mac_t::free_resource(EVP_MAC *p) { + if (p) { + EVP_MAC_free(p); + } +} +#endif + +#if defined(HAS_3_0_API) +void auto_mac_ctx_t::free_resource(EVP_MAC_CTX *p) { + if (p) { + EVP_MAC_CTX_free(p); + } +} +#endif + +void auto_cipher_t::free_resource(const EVP_CIPHER *p) { +#if defined(HAS_3_0_API) + if (p) { + EVP_CIPHER_free(const_cast(p)); + } +#endif + // in pre-3.0 the CIPHER object is const and cannot be freed +} + +void auto_cipher_ctx_t::free_resource(EVP_CIPHER_CTX *p) { + if (p) { + EVP_CIPHER_CTX_free(p); + } +} + +void auto_md_t::free_resource(evp_md_pointer_type_t p) { +#if defined(HAS_3_0_API) + if (p) { + EVP_MD_free(p); + } +#endif +} + +void auto_md_ctx_t::free_resource(evp_md_ctx_pointer_type_t p) { +#if defined(HAS_3_0_API) + if (p) { + EVP_MD_CTX_free(p); + } +#endif +} 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..29ae8df20d17 --- /dev/null +++ b/lib/crypto/c_src/auto_openssl_resource.h @@ -0,0 +1,146 @@ +/* + * %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 resource destructor as a static free_resource function with a call +// to a 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 + + // allow move and move assign + auto_openssl_resource_t(auto_openssl_resource_t &&other) noexcept { + this->pointer = other.pointer; + other.pointer = nullptr; + } + auto_openssl_resource_t &operator=(auto_openssl_resource_t &&other) noexcept { + this->pointer = other.pointer; + other.pointer = nullptr; + return *this; + } + + ~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; + } +}; + +#ifdef HAVE_EC +#if defined(HAVE_DH) && defined(HAS_EVP_PKEY_CTX) && (!DISABLE_EVP_DH) +#define HAVE_AUTO_PKEY_T 1 +struct auto_pkey_t : auto_openssl_resource_t { + auto_pkey_t() = default; + explicit auto_pkey_t(EVP_PKEY *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_PKEY *p); +}; + +struct auto_pkey_ctx_t : auto_openssl_resource_t { + auto_pkey_ctx_t() = default; + explicit auto_pkey_ctx_t(EVP_PKEY_CTX *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_PKEY_CTX *p); +}; +#else +#define HAVE_AUTO_KEY_V1_T 1 +// Pre-SSL 3.0 Key resource +struct auto_key_v1_t : auto_openssl_resource_t { + auto_key_v1_t() = default; + explicit auto_key_v1_t(EC_KEY *p) : auto_openssl_resource_t(p) {} + static void free_resource(EC_KEY *p); +}; +#endif // HAS_3_0_API && !DISABLE_EVP_DH && HAVE_DH +#endif // HAVE_EC + +#ifdef HAVE_ML_KEM +struct auto_kem_t : auto_openssl_resource_t { + auto_kem_t() = default; + explicit auto_kem_t(EVP_KEM *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_KEM *p); +}; +#endif + +#if defined(HAS_3_0_API) +struct auto_mac_t : auto_openssl_resource_t { + auto_mac_t() = default; + explicit auto_mac_t(EVP_MAC *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_MAC *p); +}; +#endif + +#if defined(HAS_3_0_API) +struct auto_mac_ctx_t : auto_openssl_resource_t { + auto_mac_ctx_t() = default; + explicit auto_mac_ctx_t(EVP_MAC_CTX *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_MAC_CTX *p); +}; +#endif + +struct auto_cipher_t : auto_openssl_resource_t { + auto_cipher_t() = default; + explicit auto_cipher_t(const EVP_CIPHER *p) : auto_openssl_resource_t(p) {} + static void free_resource(const EVP_CIPHER *p); +}; + +struct auto_cipher_ctx_t : auto_openssl_resource_t { + auto_cipher_ctx_t() = default; + explicit auto_cipher_ctx_t(EVP_CIPHER_CTX *p) : auto_openssl_resource_t(p) {} + static void free_resource(EVP_CIPHER_CTX *p); +}; + +#if defined(HAS_3_0_API) +using evp_md_pointer_type_t = EVP_MD *; +using evp_md_ctx_pointer_type_t = EVP_MD_CTX *; +#else +// Same as auto_md_t but takes const EVP_MD* and does not free anything as its been loaned to us as const +using evp_md_pointer_type_t = const EVP_MD *; +using evp_md_ctx_pointer_type_t = const EVP_MD_CTX *; +#endif + +struct auto_md_t : auto_openssl_resource_t { + auto_md_t() = default; + explicit auto_md_t(evp_md_pointer_type_t p) : auto_openssl_resource_t(p) {} + static void free_resource(evp_md_pointer_type_t p); +}; + +struct auto_md_ctx_t : auto_openssl_resource_t { + auto_md_ctx_t() = default; + explicit auto_md_ctx_t(evp_md_ctx_pointer_type_t p) : auto_openssl_resource_t(p) {} + static void free_resource(evp_md_ctx_pointer_type_t p); +}; + +#endif // __cplusplus diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index b57563b3a712..d8a06a4cd716 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -23,159 +23,10 @@ #include "cipher.h" #include "info.h" #include "evp.h" - -#define NOT_AEAD {{0,0,0}} -#define AEAD_CTRL {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}} - -static struct cipher_type_t cipher_types[] = -{ -#ifdef HAVE_RC2 - {{"rc2_cbc"}, "rc2-cbc", {&EVP_rc2_cbc}, 0, NO_FIPS_CIPHER, NOT_AEAD}, -#else - {{"rc2_cbc"}, "rc2-cbc", {NULL}, 0, NO_FIPS_CIPHER, NOT_AEAD}, -#endif - -#ifdef HAVE_RC4 - {{"rc4"}, "rc4", {&EVP_rc4}, 0, NO_FIPS_CIPHER, NOT_AEAD}, -#else - {{"rc4"}, "rc4", {NULL}, 0, NO_FIPS_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}, -#else - {{"des_cbc"}, "des-cbc", {NULL}, 0, 0}, - {{"des_cfb"}, "des-cfb", {NULL}, 0, 0}, - {{"des_ecb"}, "des-ecb", {NULL}, 0, 0}, -#endif - -#ifdef HAVE_DES_ede3_cbc - {{"des_ede3_cbc"}, "des-ede3-cbc", {&EVP_des_ede3_cbc}, 0, 0}, -#else - {{"des_ede3_cbc"}, "des-ede3-cbc", {NULL}, 0, 0}, -#endif - -#ifdef HAVE_DES_ede3_cfb - {{"des_ede3_cfb"}, "des-ede3-cfb", {&EVP_des_ede3_cfb8}, 0, 0}, -#else - {{"des_ede3_cfb"}, "des-ede3-cfb", {NULL}, 0, 0, NOT_AEAD}, -#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}, -#else - {{"blowfish_cbc"}, "BF-CBC", {NULL}, 0, 0, NOT_AEAD}, - {{"blowfish_cfb64"}, "BF-CFB", {NULL}, 0, 0, NOT_AEAD}, - {{"blowfish_ofb64"}, "BF-OFB", {NULL}, 0, 0, NOT_AEAD}, - {{"blowfish_ecb"}, "BF-ECB", {NULL}, 0, 0, NOT_AEAD}, -#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}, -#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}, -#endif - - {{"aes_128_cbc"}, "aes-128-cbc", {&EVP_aes_128_cbc}, 16, 0, NOT_AEAD}, - {{"aes_192_cbc"}, "aes-192-cbc", {&EVP_aes_192_cbc}, 24, 0, NOT_AEAD}, - {{"aes_256_cbc"}, "aes-256-cbc", {&EVP_aes_256_cbc}, 32, 0, NOT_AEAD}, - - {{"aes_128_ofb"}, "aes-128-ofb", {&EVP_aes_128_ofb}, 16, 0, NOT_AEAD}, - {{"aes_192_ofb"}, "aes-192-ofb", {&EVP_aes_192_ofb}, 24, 0, NOT_AEAD}, - {{"aes_256_ofb"}, "aes-256-ofb", {&EVP_aes_256_ofb}, 32, 0, NOT_AEAD}, - - {{"aes_128_cfb8"}, "aes-128-cfb8", {&EVP_aes_128_cfb8}, 16, AES_CFBx, NOT_AEAD}, - {{"aes_192_cfb8"}, "aes-192-cfb8", {&EVP_aes_192_cfb8}, 24, AES_CFBx, NOT_AEAD}, - {{"aes_256_cfb8"}, "aes-256-cfb8", {&EVP_aes_256_cfb8}, 32, AES_CFBx, NOT_AEAD}, - - {{"aes_128_cfb128"}, "aes-128-cfb", {&EVP_aes_128_cfb128}, 16, AES_CFBx, NOT_AEAD}, - {{"aes_192_cfb128"}, "aes-192-cfb", {&EVP_aes_192_cfb128}, 24, AES_CFBx, NOT_AEAD}, - {{"aes_256_cfb128"}, "aes-256-cfb", {&EVP_aes_256_cfb128}, 32, AES_CFBx, NOT_AEAD}, - - {{"aes_128_ecb"}, "aes-128-ecb", {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L, NOT_AEAD}, - {{"aes_192_ecb"}, "aes-192-ecb", {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L, NOT_AEAD}, - {{"aes_256_ecb"}, "aes-256-ecb", {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L, NOT_AEAD}, - -#if defined(HAVE_EVP_AES_CTR) - {{"aes_128_ctr"}, "aes-128-ctr", {&EVP_aes_128_ctr}, 16, 0, NOT_AEAD}, - {{"aes_192_ctr"}, "aes-192-ctr", {&EVP_aes_192_ctr}, 24, 0, NOT_AEAD}, - {{"aes_256_ctr"}, "aes-256-ctr", {&EVP_aes_256_ctr}, 32, 0, NOT_AEAD}, -#else - {{"aes_128_ctr"}, "aes-128-ctr", {NULL}, 16, AES_CTR_COMPAT, NOT_AEAD}, - {{"aes_192_ctr"}, "aes-192-ctr", {NULL}, 24, AES_CTR_COMPAT, NOT_AEAD}, - {{"aes_256_ctr"}, "aes-256-ctr", {NULL}, 32, AES_CTR_COMPAT, NOT_AEAD}, -#endif - -#if defined(HAVE_CHACHA20) - {{"chacha20"}, "chacha20", {&EVP_chacha20}, 32, NO_FIPS_CIPHER, NOT_AEAD}, -#else - {{"chacha20"}, "chacha20", {NULL}, 0, NO_FIPS_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}, -#else - {{"chacha20_poly1305"}, "chacha20-poly1305", {NULL}, 0, NO_FIPS_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}, -#endif -#if defined(HAVE_SM4_CCM) - {{"sm4_ccm"}, "sm4-ccm", {NULL}, 16, NO_FIPS_CIPHER | AEAD_CIPHER | CCM_MODE, AEAD_CTRL}, -#endif - -#if defined(HAVE_GCM) && defined(HAS_3_0_API) - {{"aes_128_gcm"}, "aes-128-gcm", {&EVP_aes_128_gcm}, 16, AEAD_CIPHER|GCM_MODE, AEAD_CTRL}, - {{"aes_192_gcm"}, "aes-192-gcm", {&EVP_aes_192_gcm}, 24, AEAD_CIPHER|GCM_MODE, AEAD_CTRL}, - {{"aes_256_gcm"}, "aes-256-gcm", {&EVP_aes_256_gcm}, 32, AEAD_CIPHER|GCM_MODE, AEAD_CTRL}, -#elif defined(HAVE_GCM) - {{"aes_128_gcm"}, "aes-128-gcm", {&EVP_aes_128_gcm}, 16, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, - {{"aes_192_gcm"}, "aes-192-gcm", {&EVP_aes_192_gcm}, 24, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, - {{"aes_256_gcm"}, "aes-256-gcm", {&EVP_aes_256_gcm}, 32, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}}, -#else - {{"aes_128_gcm"}, "aes-128-gcm", {NULL}, 16, AEAD_CIPHER|GCM_MODE, {{0,0,0}}}, - {{"aes_192_gcm"}, "aes-192-gcm", {NULL}, 24, AEAD_CIPHER|GCM_MODE, {{0,0,0}}}, - {{"aes_256_gcm"}, "aes-256-gcm", {NULL}, 32, AEAD_CIPHER|GCM_MODE, {{0,0,0}}}, -#endif - -#if defined(HAVE_CCM) && defined(HAS_3_0_API) - {{"aes_128_ccm"}, "aes-128-ccm", {&EVP_aes_128_ccm}, 16, AEAD_CIPHER|CCM_MODE, AEAD_CTRL}, - {{"aes_192_ccm"}, "aes-192-ccm", {&EVP_aes_192_ccm}, 24, AEAD_CIPHER|CCM_MODE, AEAD_CTRL}, - {{"aes_256_ccm"}, "aes-256-ccm", {&EVP_aes_256_ccm}, 32, AEAD_CIPHER|CCM_MODE, AEAD_CTRL}, -#elif defined(HAVE_CCM) - {{"aes_128_ccm"}, "aes-128-ccm", {&EVP_aes_128_ccm}, 16, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, - {{"aes_192_ccm"}, "aes-192-ccm", {&EVP_aes_192_ccm}, 24, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, - {{"aes_256_ccm"}, "aes-256-ccm", {&EVP_aes_256_ccm}, 32, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}}, -#else - {{"aes_128_ccm"}, "aes-128-ccm", {NULL}, 16, AEAD_CIPHER|CCM_MODE, {{0,0,0}}}, - {{"aes_192_ccm"}, "aes-192-ccm", {NULL}, 24, AEAD_CIPHER|CCM_MODE, {{0,0,0}}}, - {{"aes_256_ccm"}, "aes-256-ccm", {NULL}, 32, AEAD_CIPHER|CCM_MODE, {{0,0,0}}}, -#endif - - /*==== End of list ==== */ - - {{NULL},NULL,{NULL},0,0,NOT_AEAD} -}; +#include "algorithms_cipher.h" ErlNifResourceType* evp_cipher_ctx_rtype; -static size_t num_cipher_types = 0; - static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) { if (ctx == NULL) return; @@ -205,64 +56,6 @@ int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) { return 0; } -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); -#ifdef HAS_3_0_API - if (p->str_v3) { - p->cipher.p = EVP_CIPHER_fetch(NULL, p->str_v3, ""); -# ifdef FIPS_SUPPORT - /* Try if valid in FIPS */ - { - EVP_CIPHER *tmp = EVP_CIPHER_fetch(NULL, p->str_v3, "fips=yes"); - - if (tmp) { - EVP_CIPHER_free(tmp); - p->flags &= ~NO_FIPS_CIPHER; - } else - p->flags |= NO_FIPS_CIPHER; - } -# endif /* FIPS_SUPPORT and >=3.0.0 */ - } -#else - if (p->cipher.funcp) - p->cipher.p = p->cipher.funcp(); -#endif - } - p->type.atom = atom_false; /* end marker */ - - qsort(cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); -} - -const struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) -{ - struct cipher_type_t key; - - key.type.atom = type; - key.key_len = key_len; - - return bsearch(&key, cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types); -} - - -int cmp_cipher_types(const void *keyp, const void *elemp) { - const struct cipher_type_t *key = keyp; - const struct cipher_type_t *elem = elemp; - - if (key->type.atom < elem->type.atom) return -1; - else if (key->type.atom > elem->type.atom) return 1; - else /* key->type.atom == elem->type.atom */ - if (!elem->key_len || key->key_len == elem->key_len) return 0; - else if (key->key_len < elem->key_len) return -1; - else return 1; -} - - ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type) */ const struct cipher_type_t *cipherp; @@ -277,9 +70,9 @@ 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) + if ((cipher = get_cipher_type_resource(cipherp)) == NULL) return enif_raise_exception(env, atom_notsup); type = EVP_CIPHER_type(cipher); @@ -369,48 +162,3 @@ ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return ret; } - -const struct cipher_type_t* get_cipher_type_no_key(ERL_NIF_TERM type) -{ - struct cipher_type_t key; - - key.type.atom = type; - - return bsearch(&key, cipher_types, num_cipher_types, sizeof(cipher_types[0]), cmp_cipher_types_no_key); -} - -int cmp_cipher_types_no_key(const void *keyp, const void *elemp) { - const struct cipher_type_t *key = keyp; - const struct cipher_type_t *elem = elemp; - int ret; - - if (key->type.atom < elem->type.atom) ret = -1; - else if (key->type.atom > elem->type.atom) ret = 1; - else /* key->type.atom == elem->type.atom */ ret = 0; - - return ret; -} - - -ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) -{ - struct cipher_type_t* p; - ERL_NIF_TERM prev, hd; - - 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) ) - continue; - - 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..961fd37ef75d 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -25,39 +25,6 @@ #include "common.h" -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 */ - 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 */ - unsigned flags; - union { - 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 - -#ifdef FIPS_SUPPORT -# define CIPHER_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_MODE()) -#else -# define CIPHER_FORBIDDEN_IN_FIPS(P) 0 -#endif - extern ErlNifResourceType* evp_cipher_ctx_rtype; struct evp_cipher_ctx { EVP_CIPHER_CTX* ctx; @@ -77,13 +44,4 @@ ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf); -void init_cipher_types(ErlNifEnv* env); -const struct cipher_type_t* get_cipher_type_no_key(ERL_NIF_TERM type); -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); - #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..684391726f20 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}, @@ -174,6 +190,8 @@ static int verify_lib_version(void) return 1; } +#define REPORT_FAILURE(M) error_message = (M); ret = __LINE__; goto done +#define REPORT_FAILURE_NO_MESSAGE() error_message = NULL; ret = __LINE__; goto done static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) { @@ -190,6 +208,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) int vernum; ErlNifBinary rt_buf = { 0, NULL }; ErlNifBinary lib_bin; + const char* error_message = NULL; // To be printed before failing library init #ifdef HAVE_DYNAMIC_CRYPTO_LIB char lib_buf[1000]; void *handle; @@ -197,7 +216,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) int ret = -1; if (!verify_lib_version()) { - ret = __LINE__; goto done; + REPORT_FAILURE("Incompatible OpenSSL version found"); } /* load_info: {302, <<"/full/path/of/this/library">>,true|false} */ if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)) { @@ -244,7 +263,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; } @@ -264,12 +283,15 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) prov_cnt = 0; # ifdef FIPS_SUPPORT fips_provider = OSSL_PROVIDER_load(NULL, "fips"); + if (!fips_provider) { + enif_fprintf(stderr, "crypto: With FIPS enabled, attempt to load OpenSSL 'fips' provider has failed.\r\n"); + } # endif if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "default"))) { - ret = __LINE__; goto done; + REPORT_FAILURE("Attempt to load OpenSSL 'default' provider failed"); } if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "base"))) { - ret = __LINE__; goto done; + REPORT_FAILURE("Attempt to load OpenSSL 'base' provider failed"); } if ((prov[prov_cnt] = OSSL_PROVIDER_load(NULL, "legacy"))) { /* Don't fail loading if the legacy provider is missing */ @@ -284,18 +306,19 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) } /* Check if enter FIPS mode at module load (happening now) */ if (enable_fips_mode(env, tpl_array[2]) != atom_true) { - ret = __LINE__; goto done; + REPORT_FAILURE("Attempt to set FIPS mode failed. "\ + "Are OpenSSL and OS environment configured properly for FIPS?"); } #ifdef HAVE_DYNAMIC_CRYPTO_LIB if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name)) { ret = __LINE__; goto done; } if ((handle = enif_dlopen(lib_buf, &error_handler, NULL)) == NULL) { - ret = __LINE__; goto done; + REPORT_FAILURE("Attempt to load OpenSSL dynamic library failed"); } if ((funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks", &error_handler, NULL)) == NULL) { - ret = __LINE__; goto done; + REPORT_FAILURE("Attempt to load OpenSSL dynamic library succeeded but finding crypto callbacks in it failed"); } #else /* !HAVE_DYNAMIC_CRYPTO_LIB */ funcp = &get_crypto_callbacks; @@ -315,7 +338,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) if (!ccb || ccb->sizeof_me != sizeof(*ccb)) { PRINTF_ERR0("Invalid 'crypto_callbacks'"); - ret = __LINE__; goto done; + REPORT_FAILURE("Finding crypto callbacks in the OpenSSL library failed"); } #ifdef HAS_CRYPTO_MEM_FUNCTIONS @@ -337,19 +360,18 @@ 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; done: ASSERT(ret >= 0); - if (rt_buf.data) + if (rt_buf.data) { enif_release_binary(&rt_buf); + } + if (ret > 0 && error_message != NULL) { + fprintf(stderr, "crypto NIF initialization failed: %s\r\n", error_message); + } return ret; } @@ -396,7 +418,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 +433,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/dss.c b/lib/crypto/c_src/dss.c index f4cbb662c63b..a2d7139eaf47 100644 --- a/lib/crypto/c_src/dss.c +++ b/lib/crypto/c_src/dss.c @@ -22,6 +22,7 @@ #include "dss.h" #include "bn.h" +#include "evp_compat.h" #ifdef HAVE_DSA diff --git a/lib/crypto/c_src/evp_compat.cpp b/lib/crypto/c_src/evp_compat.cpp new file mode 100644 index 000000000000..26efa2bf1f36 --- /dev/null +++ b/lib/crypto/c_src/evp_compat.cpp @@ -0,0 +1,150 @@ +/* + * %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 "evp_compat.h" + +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) + +extern "C" HMAC_CTX *HMAC_CTX_new() { + const auto ctx = static_cast(CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__)); + if (!ctx) + return nullptr; + + HMAC_CTX_init(ctx); + return ctx; +} + +extern "C" void HMAC_CTX_free(HMAC_CTX *ctx) { + if (ctx == nullptr) + return; + + HMAC_CTX_cleanup(ctx); + CRYPTO_free(ctx); +} + +extern "C" void *BN_GENCB_get_arg(const BN_GENCB *cb) { + return cb->arg; +} + +extern "C" int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { + r->n = n; + r->e = e; + r->d = d; + return 1; +} + +extern "C" void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) { + *n = r->n; + *e = r->e; + *d = r->d; +} + +extern "C" int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) { + r->p = p; + r->q = q; + return 1; +} + +extern "C" void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) { + *p = r->p; + *q = r->q; +} + +extern "C" int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) { + r->dmp1 = dmp1; + r->dmq1 = dmq1; + r->iqmp = iqmp; + return 1; +} + +extern "C" void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) { + *dmp1 = r->dmp1; + *dmq1 = r->dmq1; + *iqmp = r->iqmp; +} + +extern "C" int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) { + d->pub_key = pub_key; + d->priv_key = priv_key; + return 1; +} + +extern "C" int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { + d->p = p; + d->q = q; + d->g = g; + return 1; +} + +extern "C" void DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) { + *p = dsa->p; + *q = dsa->q; + *g = dsa->g; +} + +extern "C" void DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) { + if (pub_key) + *pub_key = dsa->pub_key; + + if (priv_key) + *priv_key = dsa->priv_key; +} + +extern "C" int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); +extern "C" int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); +extern "C" int DH_set_length(DH *dh, long length); +extern "C" void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); +extern "C" void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); + +extern "C" int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) { + dh->pub_key = pub_key; + dh->priv_key = priv_key; + return 1; +} + +extern "C" int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) { + dh->p = p; + dh->q = q; + dh->g = g; + return 1; +} + +extern "C" int DH_set_length(DH *dh, long length) { + dh->length = length; + return 1; +} + +extern "C" void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) { + *p = dh->p; + *q = dh->q; + *g = dh->g; +} + +extern "C" void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) { + if (pub_key) + *pub_key = dh->pub_key; + + if (priv_key) + *priv_key = dh->priv_key; +} + +#endif // OPENSSL VERSION < 1.0.0 \ No newline at end of file diff --git a/lib/crypto/c_src/evp_compat.h b/lib/crypto/c_src/evp_compat.h index 4532cada4f87..9d39b137eff8 100644 --- a/lib/crypto/c_src/evp_compat.h +++ b/lib/crypto/c_src/evp_compat.h @@ -20,193 +20,51 @@ * %CopyrightEnd% */ -#ifndef E_EVP_COMPAT_H__ -#define E_EVP_COMPAT_H__ 1 +#pragma once -/* - * In OpenSSL 1.1.0, most structs are opaque. That means that - * the structs cannot be allocated as automatic variables on the - * C stack (because the size is unknown) and that it is necessary - * to use access functions. - * - * For backward compatibility to previous versions of OpenSSL, define - * on our versions of the new functions defined in 1.1.0 here, so that - * we don't have to sprinkle ifdefs throughout the code. - */ +#include "common.h" -static INLINE HMAC_CTX *HMAC_CTX_new(void); -static INLINE void HMAC_CTX_free(HMAC_CTX *ctx); +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0) -static INLINE HMAC_CTX *HMAC_CTX_new() +#ifdef __cplusplus +extern "C" { - HMAC_CTX *ctx; +#endif - if ((ctx = CRYPTO_malloc(sizeof(HMAC_CTX), __FILE__, __LINE__)) == NULL) - return NULL; - - HMAC_CTX_init(ctx); - return ctx; -} - -static INLINE void HMAC_CTX_free(HMAC_CTX *ctx) -{ - if (ctx == NULL) - return; + // In OpenSSL 1.1.0, most structs are opaque. That means that the structs cannot be allocated as automatic variables + // on the C stack (because the size is unknown) and that it is necessary to use access functions. For backward + // compatibility to previous versions of OpenSSL, define on our versions of the new functions defined in 1.1.0 here, + // so that we don't have to sprinkle ifdefs throughout the code. - HMAC_CTX_cleanup(ctx); - CRYPTO_free(ctx); -} + HMAC_CTX *HMAC_CTX_new(void); + void HMAC_CTX_free(HMAC_CTX *ctx); -/* Renamed in 1.1.0 */ +// Renamed in 1.1.0 #define EVP_MD_CTX_new() EVP_MD_CTX_create() #define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy((ctx)) -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb); - -static INLINE void *BN_GENCB_get_arg(BN_GENCB *cb) -{ - return cb->arg; -} - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); - -static INLINE int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - r->n = n; - r->e = e; - r->d = d; - return 1; -} - -static INLINE void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - *n = r->n; - *e = r->e; - *d = r->d; -} - -static INLINE int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - r->p = p; - r->q = q; - return 1; -} + void *BN_GENCB_get_arg(const BN_GENCB *cb); -static INLINE void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -{ - *p = r->p; - *q = r->q; -} + int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); + void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); + int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); + void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); + int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); + void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); -static INLINE int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - r->dmp1 = dmp1; - r->dmq1 = dmq1; - r->iqmp = iqmp; - return 1; -} - -static INLINE void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) -{ - *dmp1 = r->dmp1; - *dmq1 = r->dmq1; - *iqmp = r->iqmp; -} - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE void DSA_get0_pqg(const DSA *dsa, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DSA_get0_key(const DSA *dsa, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -{ - d->pub_key = pub_key; - d->priv_key = priv_key; - return 1; -} + int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); + int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); + void DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); + void DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key); -static INLINE int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - d->p = p; - d->q = q; - d->g = g; - return 1; -} - -static INLINE void -DSA_get0_pqg(const DSA *dsa, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dsa->p; - *q = dsa->q; - *g = dsa->g; -} - -static INLINE void -DSA_get0_key(const DSA *dsa, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) - *pub_key = dsa->pub_key; - - if (priv_key) - *priv_key = dsa->priv_key; -} - - - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); -static INLINE int DH_set_length(DH *dh, long length); -static INLINE void DH_get0_pqg(const DH *dh, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -static INLINE void DH_get0_key(const DH *dh, - const BIGNUM **pub_key, const BIGNUM **priv_key); - -static INLINE int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) -{ - dh->pub_key = pub_key; - dh->priv_key = priv_key; - return 1; -} - -static INLINE int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - dh->p = p; - dh->q = q; - dh->g = g; - return 1; -} - -static INLINE int DH_set_length(DH *dh, long length) -{ - dh->length = length; - return 1; -} - - - -static INLINE void -DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - *p = dh->p; - *q = dh->q; - *g = dh->g; -} - -static INLINE void -DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key) - *pub_key = dh->pub_key; + int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); + int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); + int DH_set_length(DH *dh, long length); + void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); + void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); - if (priv_key) - *priv_key = dh->priv_key; -} +#ifdef __cplusplus +} // extern "C" +#endif -#endif /* E_EVP_COMPAT_H__ */ +#endif // OPENSSL VERSION < 1.0.0 diff --git a/lib/crypto/c_src/fips.c b/lib/crypto/c_src/fips.c index 88271ebc9a6e..62e494b14d0c 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,35 @@ 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; + bool previous_setting = FIPS_MODE(); + bool fips_mode = fips_mode_to_set == atom_true; - } else if (fips_mode_to_set == atom_false) { - if (!FIPS_mode_set(0)) return atom_false; - else return 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); + } + else { + bool result = FIPS_mode_set(fips_mode); + if (result && previous_setting != fips_mode) { + /* Reinitialize the algorithms which may disappear or reappear when FIPS mode changes */ + algorithms_reset_cache(); + } + return result ? atom_true : atom_false; + } } #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..4c7612fa1b67 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 @@ -42,7 +42,7 @@ struct evp_md_ctx { /* Define resource types for OpenSSL context structures. */ static ErlNifResourceType* evp_md_ctx_rtype; -static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) { +static void evp_md_ctx_dtor(ErlNifEnv* env, const struct evp_md_ctx *ctx) { if (ctx == NULL) return; @@ -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); + { + digest_type_C* digp = get_digest_type(argv[0]); + if (digp == NULL) + return enif_make_badarg(env); + if (is_digest_forbidden_in_fips(digp)) + return RAISE_NOTSUP(env); + if ((md = get_digest_type_resource(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) + digest_type_C* digp = get_digest_type(argv[0]); + if (digp == 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_type_resource(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_type_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) + digest_type_C* digp = get_digest_type(argv[0]); + if (digp == 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_type_resource(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_type_resource(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_type_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")); @@ -288,12 +293,12 @@ 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) + if (!get_digest_type_resource(digp)) return EXCP_NOTSUP_N(env, 0, "Unsupported digest type"); - switch (EVP_MD_type(digp->md.p)) + switch (EVP_MD_type(get_digest_type_resource(digp))) { #ifdef HAVE_MD4 case NID_md4: @@ -374,9 +379,9 @@ 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) + if (!get_digest_type_resource(digp)) return EXCP_BADARG_N(env, 0, "Bad state"); if (!enif_inspect_binary(env, tuple[1], &ctx)) return EXCP_BADARG_N(env, 0, "Bad state"); @@ -384,7 +389,7 @@ ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if (!enif_inspect_iolist_as_binary(env, argv[1], &data)) return EXCP_BADARG_N(env, 0, "Bad data"); - switch (EVP_MD_type(digp->md.p)) + switch (EVP_MD_type(get_digest_type_resource(digp))) { #ifdef HAVE_MD4 case NID_md4: @@ -473,9 +478,9 @@ 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) + if ((md = get_digest_type_resource(digp)) == NULL) return EXCP_BADARG_N(env, 0, "Bad state"); if (!enif_inspect_binary(env, tuple[1], &ctx)) diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c index 1200cd4ad7a4..0432983ac77f 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 @@ -94,7 +94,7 @@ ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (key.size > INT_MAX) goto bad_arg; - if (digp->md.p == NULL) + if (get_digest_type_resource(digp) == NULL) goto err; if ((obj = enif_alloc_resource(hmac_context_rtype, sizeof(struct hmac_context))) == NULL) @@ -112,11 +112,11 @@ ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) // Check the return value of HMAC_Init: it may fail in FIPS mode // for disabled algorithms - if (!HMAC_Init_ex(obj->ctx, key.data, (int)key.size, digp->md.p, NULL)) + if (!HMAC_Init_ex(obj->ctx, key.data, (int)key.size, get_digest_type_resource(digp), NULL)) goto err; #else // In ancient versions of OpenSSL, this was a void function. - HMAC_Init_ex(obj->ctx, key.data, (int)key.size, digp->md.p, NULL); + HMAC_Init_ex(obj->ctx, key.data, (int)key.size, get_digest_type_resource(digp), NULL); #endif ret = enif_make_resource(env, obj); diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 3db57da49133..3c4adf61e46c 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -20,166 +20,25 @@ * %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" +#include "algorithms_cipher.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 +69,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; + mac_type_C* macp; ErlNifBinary key_bin, text; int ret_bin_alloc = 0; ERL_NIF_TERM return_term; @@ -233,32 +92,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) { + mac_type_C* macp2 = get_mac_type_no_key(argv[0]); + if (!macp2) + 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 +124,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_type_mactype(macp)) { /******** * HMAC * ********/ case HMAC_mac: { - struct digest_type_t *digp; + digest_type_C* digp = get_digest_type(argv[1]); - if ((digp = get_digest_type(argv[1])) == NULL) + if (digp == 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 +145,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_type_str_v3(digp); #else /* Old style */ - if (digp->md.p == NULL) + if (get_digest_type_resource(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + md = get_digest_type_resource(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) */ @@ -322,9 +178,8 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * CMAC * ********/ #ifdef HAVE_CMAC - case CMAC_mac: - { - const struct cipher_type_t *cipherp; + case CMAC_mac: { + const cipher_type_C *cipherp; if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) { /* Something went wrong. Find out what by retrying in another way. */ if (!get_cipher_type_no_key(argv[1])) @@ -335,13 +190,13 @@ 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; } - if (cipherp->cipher.p == NULL) + if (get_cipher_type_resource(cipherp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm"); goto err; @@ -349,13 +204,13 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) # if defined(HAS_3_0_API) name = "CMAC"; - subalg = cipherp->str_v3; + subalg = get_cipher_type_str_v3(cipherp); # else - /* Old style */ + /* Old style, pre 3.0 */ # ifdef HAVE_EVP_PKEY_new_CMAC_key - pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, get_cipher_type_resource(cipherp)); # else - if (!cmac_low_level(env, key_bin, cipherp->cipher.p, text, &ret_bin, &ret_bin_alloc, &return_term)) + if (!cmac_low_level(env, key_bin, get_cipher_type_resource(cipherp), text, &ret_bin, &ret_bin_alloc, &return_term)) goto err; else goto success; @@ -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; + mac_type_C* 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) { + if (get_mac_type_no_key(argv[0]) == 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_type_mactype(macp)) { /******** * HMAC * ********/ case HMAC_mac: { - struct digest_type_t *digp; - - if ((digp = get_digest_type(argv[1])) == NULL) + digest_type_C* digp = get_digest_type(argv[1]); + if (digp == 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_type_str_v3(digp); # else - if (digp->md.p == NULL) + if (get_digest_type_resource(digp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm"); goto err; } - md = digp->md.p; + md = get_digest_type_resource(digp); # ifdef HAVE_PKEY_new_raw_private_key /* Preferred for new applications according to EVP_PKEY_new_mac_key(3) */ @@ -677,23 +528,23 @@ 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; } - if (cipherp->cipher.p == NULL) + if (get_cipher_type_resource(cipherp) == NULL) { return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm"); goto err; } # if defined(HAS_3_0_API) - cipher = cipherp->str_v3; + cipher = get_cipher_type_str_v3(cipherp); # else /* Old style */ - pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, get_cipher_type_resource(cipherp)); # endif } break; @@ -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_type_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_type_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/openssl_config.h b/lib/crypto/c_src/openssl_config.h index 9957b58d985a..cd96109a7ccc 100644 --- a/lib/crypto/c_src/openssl_config.h +++ b/lib/crypto/c_src/openssl_config.h @@ -20,8 +20,7 @@ * %CopyrightEnd% */ -#ifndef E_OPENSSL_CONFIG_H__ -#define E_OPENSSL_CONFIG_H__ 1 +#pragma once #define OPENSSL_THREAD_DEFINES #include @@ -80,6 +79,10 @@ /* LibreSSL dislikes FIPS */ # undef FIPS_SUPPORT +#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,0,1) +#error "The OpenSSL versions before 1.0.1 are not supported, please upgrade." +#endif + # if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) /* LibreSSL wants the 1.0.1 API */ # define NEED_EVP_COMPATIBILITY_FUNCTIONS @@ -505,14 +508,6 @@ do { \ # define PRINTF_ERR2(FMT,A1,A2) #endif -#if defined(FIPS_SUPPORT) \ - && OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,0,1) -/* FIPS is not supported for versions < 1.0.1. If FIPS_SUPPORT is enabled - there are some warnings/errors for thoose -*/ -# undef FIPS_SUPPORT -#endif - #if defined(FIPS_SUPPORT) && \ defined(HAS_3_0_API) # define FIPS_mode() EVP_default_properties_is_fips_enabled(NULL) @@ -548,5 +543,3 @@ do { \ //# define CRYPTO_DEVELOP_ERRORS #endif - -#endif /* E_OPENSSL_CONFIG_H__ */ diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index dfc747df0289..5038a482cd93 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -446,6 +446,7 @@ int test_rsa_sign(int dtype, printf("To be faked\r\n"); /* To be faked */ + // TODO: OpenSSL 3.0 Use EVP_PKEY_get_bits(3), EVP_PKEY_get_security_bits(3) and EVP_PKEY_get_size(3). if ((slen = RSA_size(rsa)) < 0) goto err; add_test_data(sigret, (unsigned int)slen); /* The signature is 0,1,2...255,0,1... */ @@ -472,6 +473,7 @@ int test_rsa_verify(int dtype, && memcmp(m,fake_flag,m_len) == 0) { int size; + // TODO: OpenSSL 3.0 Use EVP_PKEY_get_bits(3), EVP_PKEY_get_security_bits(3) and EVP_PKEY_get_size(3). if ((size = RSA_size(rsa)) < 0) return 0; diff --git a/lib/crypto/c_src/pbkdf2_hmac.c b/lib/crypto/c_src/pbkdf2_hmac.c index 6d450cb4fc15..47257421adba 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) + digest_type_C* digp = get_digest_type(argv[0]); + if (digp == NULL) return EXCP_BADARG_N(env, 0, "Bad digest type"); - if (digp->md.p == NULL) + if (get_digest_type_resource(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_type_resource(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..43270265852d 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) { + digest_type_C* 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 == 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_type_resource(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_type_resource(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/crypto_app.md b/lib/crypto/doc/crypto_app.md index 842abe6ff9e7..7145f8912501 100644 --- a/lib/crypto/doc/crypto_app.md +++ b/lib/crypto/doc/crypto_app.md @@ -31,21 +31,26 @@ in its implementation. ## DEPENDENCIES -The current crypto implementation uses nifs to interface OpenSSLs crypto library -and may work with limited functionality with as old versions as _OpenSSL_ -0\.9.8c. FIPS mode support requires at least version 1.0.1 and a FIPS capable +The current crypto implementation uses a NIF module to interface OpenSSLs crypto +library. FIPS mode support requires at least version 1.0.1 and a FIPS capable OpenSSL installation. We recommend using a version that is officially supported by the OpenSSL project. API compatible backends like LibreSSL should also work. -The crypto app is tested daily with at least one version of each of the OpenSSL -1.0.1, 1.0.2, 1.1.0, 1.1.1 and 3.0. FIPS mode is also tested for 1.0.1, 1.0.2 -and 3.0. +The crypto app is tested daily with at least one version of: +* OpenSSL 1.0 (oldest 1.0.1f) +* OpenSSL 1.1 (multiple tags) +* OpenSSL 3.0 (multiple tags) +* OpenSSL 3.2 +* OpenSSL 3.5 +* LibreSSL 4.0 and 4.1. +FIPS mode is also tested for 1.0, 1.1 and 3.x. +OpenSSL versions 0.x and 1.0.0 are not supported anymore. Using OpenSSL 3.0 with Engines is supported since OTP 26.2. Source releases of OpenSSL can be downloaded from the -[OpenSSL](http://www.openssl.org) project home page, or mirror sites listed -there. +[OpenSSL Library](https://www.openssl-library.org/source/) +project home page, or mirror sites listed there. ## CONFIGURATION 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..5a179f431779 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,17 @@ 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. + +--- +It is not safe to use this function in your code, it is designed to be used by the +crypto library during the startup or by Erlang self-tests. + +This operation is not thread-safe, any 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 +3965,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, []).