summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Nilsson <hans@erlang.org>2021-10-06 10:28:56 +0200
committerHans Nilsson <hans@erlang.org>2021-10-06 10:28:56 +0200
commit0569c3fca57c26c583cea7cc7b5f1c68d28edcf1 (patch)
tree89c8c383e0feec6a98570a9227f5f09db256c56d
parent0b53017918a0e6470aa4785b612d4bd21de702c7 (diff)
parentb51754190f94d8da328f6fc0f0cc4622ccce8205 (diff)
downloaderlang-0569c3fca57c26c583cea7cc7b5f1c68d28edcf1.tar.gz
Merge branch 'hans/crypto/error_refactor/OTP-17241' into maint
* hans/crypto/error_refactor/OTP-17241: crypto: Change some line breaks crypto: Do options checking in C. Simplification crypto: Remove iolist_to binary crypto: Re-structure existing code crypto: Extend badarg macros with argument number parameter crypto: Remove old and unused macro COMPAT
-rw-r--r--lib/crypto/c_src/Makefile.in1
-rw-r--r--lib/crypto/c_src/aead.c42
-rw-r--r--lib/crypto/c_src/api_ng.c255
-rw-r--r--lib/crypto/c_src/atoms.c4
-rw-r--r--lib/crypto/c_src/atoms.h2
-rw-r--r--lib/crypto/c_src/cipher.c9
-rw-r--r--lib/crypto/c_src/common.c53
-rw-r--r--lib/crypto/c_src/common.h19
-rw-r--r--lib/crypto/c_src/crypto.c4
-rw-r--r--lib/crypto/c_src/mac.c66
-rw-r--r--lib/crypto/src/crypto.erl214
11 files changed, 412 insertions, 257 deletions
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index 1af18b5879..92918b5d9f 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -93,6 +93,7 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \
$(OBJDIR)/bn$(TYPEMARKER).o \
$(OBJDIR)/cipher$(TYPEMARKER).o \
$(OBJDIR)/cmac$(TYPEMARKER).o \
+ $(OBJDIR)/common$(TYPEMARKER).o \
$(OBJDIR)/dh$(TYPEMARKER).o \
$(OBJDIR)/digest$(TYPEMARKER).o \
$(OBJDIR)/dss$(TYPEMARKER).o \
diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c
index 42c3d35928..8bbeb5bec4 100644
--- a/lib/crypto/c_src/aead.c
+++ b/lib/crypto/c_src/aead.c
@@ -51,30 +51,30 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
encflg = -1;
else
{
- ret = EXCP_BADARG(env, "Bad enc flag");
+ ret = EXCP_BADARG_N(env, 6, "Bad enc flag");
goto done;
}
type = argv[0];
if (!enif_is_atom(env, type))
- {ret = EXCP_BADARG(env, "non-atom cipher type"); goto done;}
+ {ret = EXCP_BADARG_N(env, 0, "non-atom cipher type"); goto done;}
if (!enif_inspect_iolist_as_binary(env, argv[1], &key))
- {ret = EXCP_BADARG(env, "non-binary key"); goto done;}
+ {ret = EXCP_BADARG_N(env, 1, "non-binary key"); goto done;}
if (!enif_inspect_iolist_as_binary(env, argv[2], &iv))
- {ret = EXCP_BADARG(env, "non-binary iv"); goto done;}
+ {ret = EXCP_BADARG_N(env, 2, "non-binary iv"); goto done;}
if (!enif_inspect_iolist_as_binary(env, argv[3], &in))
- {ret = EXCP_BADARG(env, "non-binary text"); goto done;}
+ {ret = EXCP_BADARG_N(env, 3, "non-binary text"); goto done;}
if (!enif_inspect_iolist_as_binary(env, argv[4], &aad))
- {ret = EXCP_BADARG(env, "non-binary AAD"); goto done;}
+ {ret = EXCP_BADARG_N(env, 4, "non-binary AAD"); goto done;}
if (encflg) {
if (!enif_get_uint(env, argv[5], &tag_len))
- {ret = EXCP_BADARG(env, "Bad Tag length"); goto done;}
+ {ret = EXCP_BADARG_N(env, 5, "Bad Tag length"); goto done;}
tag_data = NULL;
} else {
if (!enif_inspect_iolist_as_binary(env, argv[5], &tag))
- {ret = EXCP_BADARG(env, "non-binary Tag"); goto done;}
+ {ret = EXCP_BADARG_N(env, 5, "non-binary Tag"); goto done;}
tag_len = tag.size;
tag_data = tag.data;
}
@@ -84,16 +84,16 @@ 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(env, "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(env, "Unknown cipher"); goto done;}
+ {ret = EXCP_BADARG_N(env, 0, "Unknown cipher or invalid key size"); goto done;}
if (cipherp->flags & NON_EVP_CIPHER)
- {ret = EXCP_BADARG(env, "Bad cipher"); goto done;}
+ {ret = EXCP_BADARG_N(env, 0, "Bad cipher"); goto done;}
if (! (cipherp->flags & AEAD_CIPHER) )
- {ret = EXCP_BADARG(env, "Not aead cipher"); goto done;}
+ {ret = EXCP_BADARG_N(env, 0, "Not aead cipher"); goto done;}
if ((cipher = cipherp->cipher.p) == NULL)
- {ret = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version"); goto done;}
+ {ret = EXCP_NOTSUP_N(env, 0, "The cipher is not supported in this libcrypto version"); goto done;}
#if defined(HAVE_GCM_EVP_DECRYPT_BUG)
if ( !encflg && (cipherp->flags & GCM_MODE))
@@ -106,27 +106,27 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encflg) != 1)
{ret = EXCP_ERROR(env, "CipherInit failed"); goto done;}
if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_ivlen, (int)iv.size, NULL) != 1)
- {ret = EXCP_BADARG(env, "Bad IV length"); goto done;}
+ {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)
- {ret = EXCP_BADARG(env, "Can't set tag"); goto done;}
+ {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_BADARG(env, "Can't set key or iv"); goto done;}
+ {ret = EXCP_ERROR(env, "Can't set key or iv"); goto done;}
if (EVP_CipherUpdate(ctx, NULL, &len, NULL, (int)in.size) != 1)
- {ret = EXCP_BADARG(env, "Can't set text size"); goto done;}
+ {ret = EXCP_ERROR(env, "Can't set text size"); goto done;}
} else
#endif
{ /* GCM_MODE or CHACHA20_POLY1305 */
/* Set key and iv */
if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1)
- {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;}
+ {ret = EXCP_ERROR(env, "Can't set key and iv"); goto done;}
}
/* Set the AAD */
if (EVP_CipherUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1)
- {ret = EXCP_BADARG(env, "Can't set AAD"); goto done;}
+ {ret = EXCP_BADARG_N(env, 4, "Can't set AAD"); goto done;}
/* Set the plain text and get the crypto text (or vice versa :) ) */
if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL)
@@ -134,7 +134,7 @@ ERL_NIF_TERM aead_cipher_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
if (EVP_CipherUpdate(ctx, outp, &len, in.data, (int)in.size) != 1)
{
if (encflg)
- ret = EXCP_BADARG(env, "Can't set in-text");
+ ret = EXCP_BADARG_N(env, 3, "Can't set in-text");
else
/* Decrypt error */
ret = atom_error;
@@ -182,7 +182,7 @@ done:
return ret;
#else
- return EXCP_NOTSUP(env, "Unsupported Cipher");
+ return EXCP_NOTSUP_N(env, 0, "Unsupported Cipher");
#endif
}
diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c
index c2ae10f2ac..35404ba1e0 100644
--- a/lib/crypto/c_src/api_ng.c
+++ b/lib/crypto/c_src/api_ng.c
@@ -85,13 +85,117 @@ int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
/* Get the arguments for the initialization of the EVP_CIPHER_CTX. Check */
/* them and initialize that context. */
/*************************************************************************/
+
+static ERL_NIF_TERM get_opts(ErlNifEnv* env, const ERL_NIF_TERM opts, int opts_arg_num, int *encflgp, ERL_NIF_TERM *padflgp)
+{ /* boolean() | [{Tag,Val}] Tag = encrypt | padding */
+ unsigned list_len;
+ ERL_NIF_TERM p, hd, tl;
+
+ *padflgp = atom_false; /* Not valid as padding value */
+ /* First check if the opts is an atom: */
+ if (opts == atom_true)
+ {
+ *encflgp = 1;
+ *padflgp = atom_undefined;
+ return atom_ok;
+ }
+
+ if (opts == atom_false)
+ {
+ *encflgp = 0;
+ *padflgp = atom_undefined;
+ return atom_ok;
+ }
+
+ if (opts == atom_undefined)
+ /* For compat funcs in crypto.erl. TODO: check and remove */
+ {
+ *encflgp = -1;
+ *padflgp = atom_undefined;
+ return atom_ok;
+ }
+
+ if (!enif_is_list(env, opts) || !enif_get_list_length(env, opts, &list_len))
+ /* Not a boolean() and not a list, definitly an error */
+ return EXCP_BADARG_N(env, opts_arg_num, "Options are not a boolean or a proper list");
+
+ /* A list, might be a property list, as it should */
+ *encflgp = -14; /* why not? */
+ p = opts;
+ while (enif_get_list_cell(env, p, &hd, &tl))
+ /* Loop through the list [{Tag,Value},...]
+ - check that
+ + the list is proper
+ + each list element is a 2-tuple
+ + the Tag is known
+ + the Value is of right type
+ - assign Values of known Tags to their respective flags
+ */
+ {
+ int arity;
+ const ERL_NIF_TERM* elements;
+
+ if (!(enif_get_tuple(env, hd, &arity, &elements) && (arity == 2)) )
+ return EXCP_BADARG_N(env, opts_arg_num, "Options must be a property list!");
+
+ if (elements[0] == atom_encrypt)
+ {
+ if (*encflgp != -14)
+ return EXCP_BADARG_N(env, opts_arg_num, "'encrypt' option is present more than once!");
+ else if (elements[1] == atom_true)
+ *encflgp = 1;
+ else if (elements[1] == atom_false)
+ *encflgp = 0;
+ else if (elements[1] == atom_undefined)
+ *encflgp = -1; /* For compat funcs in crypto.erl. TODO: check and remove */
+ else
+ return EXCP_BADARG_N(env, opts_arg_num, "The 'encrypt' option must be a boolean!");
+ }
+ else if (elements[0] == atom_padding)
+ {
+ if (*padflgp != atom_false)
+ return EXCP_BADARG_N(env, opts_arg_num, "The 'padding' option is present more than once!");
+
+ else if ((elements[1] == atom_undefined) ||
+ (elements[1] == atom_none) ||
+ (elements[1] == atom_zero) ||
+ (elements[1] == atom_random) ||
+ (elements[1] == atom_pkcs_padding)
+ )
+ *padflgp = elements[1];
+
+ else
+ return EXCP_BADARG_N(env, opts_arg_num, "Bad 'padding' option value");
+ }
+ else
+ {
+ char msg[64];
+ if (enif_snprintf(msg, 64, "Bad tag in option: %T", elements[0]))
+ return EXCP_BADARG_N(env, opts_arg_num, msg);
+ else
+ return EXCP_BADARG_N(env, opts_arg_num, "Bad tag in option!");
+ }
+
+ p = tl; /* prepare for handling next list element or to exit the loop */
+ }
+
+ if (*encflgp == -14)
+ *encflgp = 1; /* {encrypt,true} is the default */
+
+ if (*padflgp == atom_false)
+ *padflgp = atom_undefined; /* {padding,undefined} is the default */
+
+ return atom_ok;
+}
+
+
static int get_init_args(ErlNifEnv* env,
struct evp_cipher_ctx *ctx_res,
- const ERL_NIF_TERM cipher_arg,
- const ERL_NIF_TERM key_arg,
- const ERL_NIF_TERM ivec_arg,
- const ERL_NIF_TERM encflg_arg,
- const ERL_NIF_TERM padding_arg,
+ const ERL_NIF_TERM argv[],
+ int cipher_arg_num,
+ int key_arg_num,
+ int ivec_arg_num,
+ int opts_arg_num,
const struct cipher_type_t **cipherp,
ERL_NIF_TERM *return_term)
{
@@ -103,57 +207,48 @@ static int get_init_args(ErlNifEnv* env,
#if !defined(HAVE_EVP_AES_CTR)
ctx_res->env = NULL; /* For testing if *env should be freed after errors */
#endif
- ctx_res->padding = atom_undefined;
ctx_res->padded_size = -1;
ctx_res->size = 0;
-
- /* Fetch the flag telling if we are going to encrypt (=true) or decrypt (=false) */
- if (encflg_arg == atom_true)
- ctx_res->encflag = 1;
- else if (encflg_arg == atom_false)
- ctx_res->encflag = 0;
- else if (encflg_arg == atom_undefined)
- /* For compat funcs in crypto.erl */
- ctx_res->encflag = -1;
- else
- {
- *return_term = EXCP_BADARG(env, "Bad enc flag");
- goto err;
- }
+
+ /* Fetch the options */
+ if ((*return_term =
+ get_opts(env, argv[opts_arg_num], opts_arg_num, &(ctx_res->encflag), &(ctx_res->padding))
+ ) != atom_ok)
+ goto err;
/* Fetch the key */
- if (!enif_inspect_iolist_as_binary(env, key_arg, &key_bin))
+ if (!enif_inspect_iolist_as_binary(env, argv[key_arg_num], &key_bin))
{
- *return_term = EXCP_BADARG(env, "Bad key");
+ *return_term = EXCP_BADARG_N(env, key_arg_num, "Bad key");
goto err;
}
/* Fetch cipher type */
- if (!enif_is_atom(env, cipher_arg))
+ if (!enif_is_atom(env, argv[cipher_arg_num]))
{
- *return_term = EXCP_BADARG(env, "Cipher id is not an atom");
+ *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Cipher id is not an atom");
goto err;
}
- if (!(*cipherp = get_cipher_type(cipher_arg, key_bin.size)))
+ if (!(*cipherp = get_cipher_type(argv[cipher_arg_num], key_bin.size)))
{
- if (!get_cipher_type_no_key(cipher_arg))
- *return_term = EXCP_BADARG(env, "Unknown cipher");
+ if (!get_cipher_type_no_key(argv[cipher_arg_num]))
+ *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Unknown cipher");
else
- *return_term = EXCP_BADARG(env, "Bad key size");
+ *return_term = EXCP_BADARG_N(env, key_arg_num, "Bad key size");
goto err;
}
if ((*cipherp)->flags & AEAD_CIPHER)
{
- *return_term = EXCP_BADARG(env, "Missing arguments for this cipher");
+ *return_term = EXCP_BADARG_N(env, cipher_arg_num, "Missing arguments for this cipher");
goto err;
}
if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp))
{
- *return_term = EXCP_NOTSUP(env, "Forbidden in FIPS");
+ *return_term = EXCP_NOTSUP_N(env, cipher_arg_num, "Forbidden in FIPS");
goto err;
}
@@ -171,32 +266,34 @@ static int get_init_args(ErlNifEnv* env,
ivec_len = 16;
else {
/* Unsupported crypto */
- *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version");
+ *return_term =
+ EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version");
goto err;
}
}
#else
/* Normal code */
if (!((*cipherp)->cipher.p)) {
- *return_term = EXCP_NOTSUP(env, "Cipher not supported in this libcrypto version");
+ *return_term =
+ EXCP_NOTSUP_N(env, cipher_arg_num, "Cipher not supported in this libcrypto version");
goto err;
}
ivec_len = GET_IV_LEN(*cipherp);
#endif
- /* (*cipherp)->cipher.p != NULL and ivec_len has a value */
+ /* Here: (*cipherp)->cipher.p != NULL and ivec_len has a value */
/* Fetch IV */
- if (ivec_len && (ivec_arg != atom_undefined)) {
- if (!enif_inspect_iolist_as_binary(env, ivec_arg, &ivec_bin))
+ if (ivec_len && (argv[ivec_arg_num] != atom_undefined)) {
+ if (!enif_inspect_iolist_as_binary(env, argv[ivec_arg_num], &ivec_bin))
{
- *return_term = EXCP_BADARG(env, "Bad iv type");
+ *return_term = EXCP_BADARG_N(env, ivec_arg_num, "Bad iv type");
goto err;
}
if (ivec_len != ivec_bin.size)
{
- *return_term = EXCP_BADARG(env, "Bad iv size");
+ *return_term = EXCP_BADARG_N(env, ivec_arg_num, "Bad iv size");
goto err;
}
}
@@ -211,7 +308,7 @@ static int get_init_args(ErlNifEnv* env,
ERL_NIF_TERM ecount_bin;
unsigned char *outp;
if ((outp = enif_make_new_binary(env, AES_BLOCK_SIZE, &ecount_bin)) == NULL) {
- *return_term = EXCP_ERROR(env, "Can't allocate ecount_bin");
+ *return_term = EXCP_ERROR(env, "Can't allocate output data binary");
goto err;
}
memset(outp, 0, AES_BLOCK_SIZE);
@@ -223,7 +320,7 @@ static int get_init_args(ErlNifEnv* env,
}
ctx_res->state =
enif_make_copy(ctx_res->env,
- enif_make_tuple4(env, key_arg, ivec_arg, ecount_bin, enif_make_int(env, 0)));
+ enif_make_tuple4(env, argv[key_arg_num], argv[ivec_arg_num], ecount_bin, enif_make_int(env, 0)));
goto success;
} else {
/* Flag for subsequent calls that no aes_ctr compatibility code should be called */
@@ -249,27 +346,27 @@ static int get_init_args(ErlNifEnv* env,
if (!EVP_CIPHER_CTX_set_key_length(ctx_res->ctx, (int)key_bin.size))
{
- *return_term = EXCP_ERROR(env, "Can't initialize context, key_length");
+ *return_term = EXCP_ERROR_N(env, key_arg_num, "Can't initialize context, key_length");
goto err;
}
#ifdef HAVE_RC2
if (EVP_CIPHER_type((*cipherp)->cipher.p) == NID_rc2_cbc) {
if (key_bin.size > INT_MAX / 8) {
- *return_term = EXCP_BADARG(env, "To large rc2_cbc key");
+ *return_term = EXCP_BADARG_N(env, key_arg_num, "To large rc2_cbc key");
goto err;
}
if (!EVP_CIPHER_CTX_ctrl(ctx_res->ctx, EVP_CTRL_SET_RC2_KEY_BITS, (int)key_bin.size * 8, NULL)) {
- *return_term = EXCP_ERROR(env, "ctrl rc2_cbc key");
+ *return_term = EXCP_BADARG_N(env, key_arg_num, "ctrl rc2_cbc key");
goto err;
}
}
#endif
- if (ivec_arg == atom_undefined || ivec_len == 0)
+ if (argv[ivec_arg_num] == atom_undefined || ivec_len == 0)
{
if (!EVP_CipherInit_ex(ctx_res->ctx, NULL, NULL, key_bin.data, NULL, -1)) {
- *return_term = EXCP_ERROR(env, "Can't initialize key");
+ *return_term = EXCP_BADARG_N(env, key_arg_num, "Can't initialize key");
goto err;
}
}
@@ -281,20 +378,9 @@ static int get_init_args(ErlNifEnv* env,
}
/* Set padding */
- if ((padding_arg == atom_undefined) ||
- (padding_arg == atom_none) ||
- (padding_arg == atom_zero) ||
- (padding_arg == atom_random) )
+ if (ctx_res->padding != atom_pkcs_padding) /* pkcs_padding is default */
EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0);
- else if (padding_arg != atom_pkcs_padding) /* pkcs_padding is default */
- {
- *return_term = EXCP_BADARG(env, "Bad padding flag");
- goto err;
- }
-
- ctx_res->padding = padding_arg;
-
*return_term = atom_ok;
#if !defined(HAVE_EVP_AES_CTR)
@@ -309,18 +395,18 @@ static int get_init_args(ErlNifEnv* env,
/*************************************************************************/
/* Get the arguments for the EVP_CipherUpdate function, and call it. */
/*************************************************************************/
-
static int get_update_args(ErlNifEnv* env,
struct evp_cipher_ctx *ctx_res,
- const ERL_NIF_TERM indata_arg,
+ const ERL_NIF_TERM argv[],
+ int indata_arg_num,
ERL_NIF_TERM *return_term)
{
ErlNifBinary in_data_bin, out_data_bin;
int out_len, block_size;
- if (!enif_inspect_binary(env, indata_arg, &in_data_bin) )
+ if (!enif_inspect_iolist_as_binary(env, argv[indata_arg_num], &in_data_bin) )
{
- *return_term = EXCP_BADARG(env, "Bad 2:nd arg");
+ *return_term = EXCP_BADARG_N(env, indata_arg_num, "Expected binary");
goto err0;
}
@@ -340,7 +426,7 @@ static int get_update_args(ErlNifEnv* env,
if (enif_get_tuple(env, state0, &tuple_argc, &tuple_argv) && (tuple_argc == 4)) {
/* A compatibility state term */
/* encrypt and decrypt is performed by calling the same function */
- newstate_and_outdata = aes_ctr_stream_encrypt_compat(env, state0, indata_arg);
+ newstate_and_outdata = aes_ctr_stream_encrypt_compat(env, state0, argv[indata_arg_num]);
if (enif_get_tuple(env, newstate_and_outdata, &tuple_argc, &tuple_argv) && (tuple_argc == 2)) {
/* newstate_and_outdata = {NewState, OutData} */
@@ -488,7 +574,11 @@ static int get_final_args(ErlNifEnv* env,
else
{
- *return_term = EXCP_ERROR(env, "Bad padding flg");
+ char msg[64];
+ if (enif_snprintf(msg, 64, "Bad padding flag: %T", ctx_res->padding))
+ *return_term = EXCP_ERROR(env, msg);
+ else
+ *return_term = EXCP_ERROR(env, "Bad padding flg");
goto err;
}
@@ -567,7 +657,7 @@ static int get_final_args(ErlNifEnv* env,
/*************************************************************************/
ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Cipher, Key, IVec, Encrypt, Padding) % if no IV for the Cipher, set IVec = <<>>
+{/* (Cipher, Key, IVec, OptionsMap) % if no IV for the Cipher, set IVec = <<>>
*/
struct evp_cipher_ctx *ctx_res = NULL;
const struct cipher_type_t *cipherp;
@@ -577,8 +667,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL)
return EXCP_ERROR(env, "Can't allocate resource");
- if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[3], argv[4],
- &cipherp, &ret))
+ if (get_init_args(env, ctx_res, argv, 0, 1, 2, 3, &cipherp, &ret))
ret = enif_make_resource(env, ctx_res);
/* else error msg in ret */
@@ -591,7 +680,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
else if (argv[3] == atom_false)
ctx_res->encflag = 0;
else {
- ret = EXCP_BADARG(env, "Bad enc flag");
+ ret = EXCP_BADARG_N(env, 3, "Expected true or false");
goto ret;
}
if (ctx_res->ctx) {
@@ -603,7 +692,7 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
}
ret = argv[0];
} else {
- ret = EXCP_BADARG(env, "Bad 1:st arg");
+ ret = EXCP_BADARG_N(env, 0, "Expected cipher name atom");
goto ret;
}
@@ -625,7 +714,7 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ctx_res_copy.ctx = NULL;
if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
- return EXCP_BADARG(env, "Bad 1:st arg");
+ return EXCP_BADARG_N(env, 0, "Bad State");
if (argc == 3) {
/* We have an IV in this call. Make a copy of the context */
@@ -647,13 +736,13 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
if (!enif_inspect_iolist_as_binary(env, argv[2], &ivec_bin))
{
- ret = EXCP_BADARG(env, "Bad iv type");
+ ret = EXCP_BADARG_N(env, 2, "Bad iv type");
goto err;
}
if (ctx_res_copy.iv_len != ivec_bin.size)
{
- ret = EXCP_BADARG(env, "Bad iv size");
+ ret = EXCP_BADARG_N(env, 2, "Bad iv size");
goto err;
}
@@ -676,11 +765,11 @@ ERL_NIF_TERM ng_crypto_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
goto err;
}
- get_update_args(env, &ctx_res_copy, argv[1], &ret);
+ get_update_args(env, &ctx_res_copy, argv, 1, &ret);
ctx_res->size = ctx_res_copy.size;
} else
/* argc != 3, that is, argc = 2 (we don't have an IV in this call) */
- get_update_args(env, ctx_res, argv[1], &ret);
+ get_update_args(env, ctx_res, argv, 1, &ret);
err:
if (ctx_res_copy.ctx)
@@ -694,11 +783,11 @@ ERL_NIF_TERM ng_crypto_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
{/* (Context, Data [, IV]) */
ErlNifBinary data_bin;
- if (!enif_inspect_binary(env, argv[1], &data_bin))
- return EXCP_BADARG(env, "expected binary as data");
+ if (!enif_inspect_iolist_as_binary(env, argv[1], &data_bin))
+ return EXCP_BADARG_N(env, 1, "expected binary");
if (data_bin.size > INT_MAX)
- return EXCP_BADARG(env, "to long data");
+ return EXCP_BADARG_N(env, 1, "too long data");
/* Run long jobs on a dirty scheduler to not block the current emulator thread */
if (data_bin.size > MAX_BYTES_TO_NIF) {
@@ -721,7 +810,7 @@ ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
ERL_NIF_TERM ret;
if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
- return EXCP_BADARG(env, "Bad arg");
+ return EXCP_BADARG_N(env, 0, "Bad State");
get_final_args(env, ctx_res, &ret);
@@ -733,7 +822,7 @@ ERL_NIF_TERM ng_crypto_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
/*************************************************************************/
ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Cipher, Key, IVec, Data, Encrypt, PaddingType) */
+{/* (Cipher, Key, IVec, Data, OptionsMap) */
struct evp_cipher_ctx ctx_res;
const struct cipher_type_t *cipherp;
ERL_NIF_TERM ret;
@@ -746,11 +835,11 @@ ERL_NIF_TERM ng_crypto_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
#endif
/* EVP_CipherInit */
- if (!get_init_args(env, &ctx_res, argv[0], argv[1], argv[2], argv[4], argv[5], &cipherp, &ret))
+ if (!get_init_args(env, &ctx_res, argv, 0, 1, 2, 4, &cipherp, &ret))
goto out;
/* out_data = EVP_CipherUpdate */
- if (!get_update_args(env, &ctx_res, argv[3], &ret))
+ if (!get_update_args(env, &ctx_res, argv, 3, &ret))
/* Got an exception as result in &ret */
goto out;
@@ -800,11 +889,11 @@ ERL_NIF_TERM ng_crypto_one_time_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
*/
ErlNifBinary data_bin;
- if (!enif_inspect_binary(env, argv[3], &data_bin))
- return EXCP_BADARG(env, "expected binary as data");
+ if (!enif_inspect_iolist_as_binary(env, argv[3], &data_bin))
+ return EXCP_BADARG_N(env, 3, "expected binary as data");
if (data_bin.size > INT_MAX)
- return EXCP_BADARG(env, "to long data");
+ return EXCP_BADARG_N(env, 3, "too long data");
/* Run long jobs on a dirty scheduler to not block the current emulator thread */
if (data_bin.size > MAX_BYTES_TO_NIF) {
@@ -827,7 +916,7 @@ ERL_NIF_TERM ng_crypto_get_data_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
ERL_NIF_TERM ret;
if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res))
- return EXCP_BADARG(env, "Bad arg");
+ return EXCP_BADARG_N(env, 0, "Bad State");
ret = enif_make_new_map(env);
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index 5671e6d801..cae3c5287d 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -24,6 +24,8 @@ ERL_NIF_TERM atom_true;
ERL_NIF_TERM atom_false;
ERL_NIF_TERM atom_sha;
ERL_NIF_TERM atom_error;
+ERL_NIF_TERM atom_encrypt;
+ERL_NIF_TERM atom_padding;
ERL_NIF_TERM atom_pkcs_padding;
ERL_NIF_TERM atom_zero;
ERL_NIF_TERM atom_random;
@@ -165,6 +167,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
atom_sha = enif_make_atom(env,"sha");
atom_error = enif_make_atom(env,"error");
+ atom_encrypt = enif_make_atom(env,"encrypt");
+ atom_padding = enif_make_atom(env,"padding");
atom_pkcs_padding = enif_make_atom(env,"pkcs_padding");
atom_zero = enif_make_atom(env,"zero");
atom_random = enif_make_atom(env,"random");
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 414e3045ea..d6bdc43a52 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -28,6 +28,8 @@ extern ERL_NIF_TERM atom_true;
extern ERL_NIF_TERM atom_false;
extern ERL_NIF_TERM atom_sha;
extern ERL_NIF_TERM atom_error;
+extern ERL_NIF_TERM atom_encrypt;
+extern ERL_NIF_TERM atom_padding;
extern ERL_NIF_TERM atom_pkcs_padding;
extern ERL_NIF_TERM atom_zero;
extern ERL_NIF_TERM atom_random;
diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c
index 5e7fc88d72..284ea0ba81 100644
--- a/lib/crypto/c_src/cipher.c
+++ b/lib/crypto/c_src/cipher.c
@@ -317,10 +317,13 @@ const struct cipher_type_t* get_cipher_type_no_key(ERL_NIF_TERM type)
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) return -1;
- else if (key->type.atom > elem->type.atom) return 1;
- else /* key->type.atom == elem->type.atom */ return 0;
+ 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;
}
diff --git a/lib/crypto/c_src/common.c b/lib/crypto/c_src/common.c
new file mode 100644
index 0000000000..e5160548c4
--- /dev/null
+++ b/lib/crypto/c_src/common.c
@@ -0,0 +1,53 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010-2021. 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 "common.h"
+
+ERL_NIF_TERM raise_exception(ErlNifEnv* env, ERL_NIF_TERM id, int arg_num, char* explanation, char* file, int line)
+/* Ex: raise_exception(atom_badarg, 1, "Unknown cipher", "api_ng.c", 17)
+ * -> {badarg, {"api_ng.c",17}, 1, "Unknown cipher"}
+ * id = atom_error | atom_notsup | atom_badarg
+ * arg_num is the (zero-based) position in argv[]. -1 is to signal that it is undefined.
+ */
+{
+ ERL_NIF_TERM file_info, exception;
+
+ file_info = enif_make_new_map(env);
+ enif_make_map_put(env, file_info,
+ enif_make_atom(env,"c_file_name"),
+ enif_make_string(env, file, (ERL_NIF_LATIN1)),
+ &file_info);
+ enif_make_map_put(env, file_info,
+ enif_make_atom(env,"c_file_line_num"),
+ enif_make_int(env, line),
+ &file_info);
+ enif_make_map_put(env, file_info,
+ enif_make_atom(env,"c_function_arg_num"),
+ enif_make_int(env, arg_num),
+ &file_info);
+ exception =
+ enif_make_tuple3(env,
+ id,
+ file_info,
+ enif_make_string(env, explanation, (ERL_NIF_LATIN1))
+ );
+
+ return enif_raise_exception(env, exception);
+}
diff --git a/lib/crypto/c_src/common.h b/lib/crypto/c_src/common.h
index 22aa38d806..8406cd0b7c 100644
--- a/lib/crypto/c_src/common.h
+++ b/lib/crypto/c_src/common.h
@@ -37,16 +37,13 @@
/* All nif functions return a valid value or throws an exception */
-#define EXCP(Env, Id, Str) enif_raise_exception((Env), \
- enif_make_tuple3((Env), \
- (Id), \
- enif_make_tuple2((Env), \
- enif_make_string((Env),__FILE__,(ERL_NIF_LATIN1)), \
- enif_make_int((Env), __LINE__)), \
- enif_make_string((Env),(Str),(ERL_NIF_LATIN1)) ))
-
-#define EXCP_NOTSUP(Env, Str) EXCP((Env), atom_notsup, (Str))
-#define EXCP_BADARG(Env, Str) EXCP((Env), atom_badarg, (Str))
-#define EXCP_ERROR(Env, Str) EXCP((Env), atom_error, (Str))
+ERL_NIF_TERM raise_exception(ErlNifEnv* env, ERL_NIF_TERM id, int arg_num, char* explanation, char* file, int Line);
+
+
+#define EXCP_ERROR(Env, Str) raise_exception((Env), atom_error, -1, (Str), __FILE__, __LINE__)
+#define EXCP_NOTSUP(Env, Str) raise_exception((Env), atom_notsup, -1, (Str), __FILE__, __LINE__)
+#define EXCP_ERROR_N(Env, ArgNum, Str) raise_exception((Env), atom_error, (ArgNum), (Str), __FILE__, __LINE__)
+#define EXCP_NOTSUP_N(Env, ArgNum, Str) raise_exception((Env), atom_notsup, (ArgNum), (Str), __FILE__, __LINE__)
+#define EXCP_BADARG_N(Env, ArgNum, Str) raise_exception((Env), atom_badarg, (ArgNum), (Str), __FILE__, __LINE__)
#endif /* E_COMMON_H__ */
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index f07ffdfedd..65804bf6fb 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -79,12 +79,12 @@ static ErlNifFunc nif_funcs[] = {
{"mac_update_nif", 2, mac_update_nif, 0},
{"mac_final_nif", 1, mac_final_nif, 0},
{"cipher_info_nif", 1, cipher_info_nif, 0},
- {"ng_crypto_init_nif", 5, ng_crypto_init_nif, 0},
+ {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0},
{"ng_crypto_update_nif", 2, ng_crypto_update_nif, 0},
{"ng_crypto_update_nif", 3, ng_crypto_update_nif, 0},
{"ng_crypto_final_nif", 1, ng_crypto_final_nif, 0},
{"ng_crypto_get_data_nif", 1, ng_crypto_get_data_nif, 0},
- {"ng_crypto_one_time_nif", 6, ng_crypto_one_time_nif, 0},
+ {"ng_crypto_one_time_nif", 5, ng_crypto_one_time_nif, 0},
{"strong_rand_bytes_nif", 1, strong_rand_bytes_nif, 0},
{"strong_rand_range_nif", 1, strong_rand_range_nif, 0},
{"rand_uniform_nif", 2, rand_uniform_nif, 0},
diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c
index 8735158a01..13bd2815b1 100644
--- a/lib/crypto/c_src/mac.c
+++ b/lib/crypto/c_src/mac.c
@@ -176,10 +176,10 @@ ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary text;
if (!enif_inspect_iolist_as_binary(env, argv[3], &text))
- return EXCP_BADARG(env, "Bad text");
+ return EXCP_BADARG_N(env, 3, "Bad text");
if (text.size > INT_MAX)
- return EXCP_BADARG(env, "Too long text");
+ return EXCP_BADARG_N(env, 3, "Too long text");
/* Run long jobs on a dirty scheduler to not block the current emulator thread */
if (text.size > MAX_BYTES_TO_NIF) {
@@ -213,28 +213,28 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
*/
if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin))
{
- return_term = EXCP_BADARG(env, "Bad key");
+ 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(env, "Bad 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(env, "Unknown mac algorithm");
+ return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm");
else
- return_term = EXCP_BADARG(env, "Bad key length");
+ return_term = EXCP_BADARG_N(env, 2, "Bad key length");
goto err;
}
if (MAC_FORBIDDEN_IN_FIPS(macp))
{
- return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS");
goto err;
}
@@ -256,17 +256,17 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
if ((digp = get_digest_type(argv[1])) == NULL)
{
- return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC");
+ return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC");
goto err;
}
if (digp->md.p == NULL)
{
- return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm");
+ return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm");
goto err;
}
if (DIGEST_FORBIDDEN_IN_FIPS(digp))
{
- return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS");
goto err;
}
md = digp->md.p;
@@ -300,22 +300,22 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
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]))
- return_term = EXCP_BADARG(env, "Unknown cipher");
+ return_term = EXCP_BADARG_N(env, 1, "Unknown cipher");
else
/* Cipher exists, so it must be the key size that is wrong */
- return_term = EXCP_BADARG(env, "Bad key size");
+ return_term = EXCP_BADARG_N(env, 2, "Bad key size");
goto err;
}
if (CIPHER_FORBIDDEN_IN_FIPS(cipherp))
{
- return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS");
goto err;
}
if (cipherp->cipher.p == NULL)
{
- return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm");
+ return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm");
goto err;
}
@@ -349,7 +349,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
case NO_mac:
default:
/* We know that this mac is supported with some version(s) of cryptolib */
- return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm");
+ return_term = EXCP_NOTSUP_N(env, 1, "Unsupported mac algorithm");
goto err;
}
@@ -506,22 +506,22 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
*/
if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin))
{
- return_term = EXCP_BADARG(env, "Bad key");
+ 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(env, "Unknown mac algorithm");
+ return_term = EXCP_BADARG_N(env, 0, "Unknown mac algorithm");
else
- return_term = EXCP_BADARG(env, "Bad key length");
+ return_term = EXCP_BADARG_N(env, 2, "Bad key length");
goto err;
}
if (MAC_FORBIDDEN_IN_FIPS(macp))
{
- return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 0, "MAC algorithm forbidden in FIPS");
goto err;
}
@@ -543,17 +543,17 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
if ((digp = get_digest_type(argv[1])) == NULL)
{
- return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC");
+ return_term = EXCP_BADARG_N(env, 1, "Bad digest algorithm for HMAC");
goto err;
}
if (digp->md.p == NULL)
{
- return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm");
+ return_term = EXCP_NOTSUP_N(env, 1, "Unsupported digest algorithm");
goto err;
}
if (DIGEST_FORBIDDEN_IN_FIPS(digp))
{
- return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 1, "Digest algorithm for HMAC forbidden in FIPS");
goto err;
}
md = digp->md.p;
@@ -579,22 +579,22 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
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]))
- return_term = EXCP_BADARG(env, "Unknown cipher");
+ return_term = EXCP_BADARG_N(env, 1, "Unknown cipher");
else
/* Cipher exists, so it must be the key size that is wrong */
- return_term = EXCP_BADARG(env, "Bad key size");
+ return_term = EXCP_BADARG_N(env, 2, "Bad key size");
goto err;
}
if (CIPHER_FORBIDDEN_IN_FIPS(cipherp))
{
- return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS");
+ return_term = EXCP_NOTSUP_N(env, 1, "Cipher algorithm not supported in FIPS");
goto err;
}
if (cipherp->cipher.p == NULL)
{
- return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm");
+ return_term = EXCP_NOTSUP_N(env, 1, "Unsupported cipher algorithm");
goto err;
}
@@ -621,7 +621,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
case NO_mac:
default:
/* We know that this mac is supported with some version(s) of cryptolib */
- return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm");
+ return_term = EXCP_NOTSUP_N(env, 0, "Unsupported mac algorithm");
goto err;
}
@@ -666,7 +666,7 @@ ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
#else
if (argv[0] != atom_hmac)
- return EXCP_NOTSUP(env, "Unsupported mac algorithm");
+ return EXCP_NOTSUP_N(env, 0, "Unsupported mac algorithm");
return hmac_init_nif(env, argc, argv);
#endif
@@ -679,10 +679,10 @@ ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary text;
if (!enif_inspect_iolist_as_binary(env, argv[1], &text))
- return EXCP_BADARG(env, "Bad text");
+ return EXCP_BADARG_N(env, 1, "Bad text");
if (text.size > INT_MAX)
- return EXCP_BADARG(env, "Too long text");
+ return EXCP_BADARG_N(env, 1, "Too long text");
/* Run long jobs on a dirty scheduler to not block the current emulator thread */
if (text.size > MAX_BYTES_TO_NIF) {
@@ -702,10 +702,10 @@ ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary text;
if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj))
- return EXCP_BADARG(env, "Bad ref");
+ return EXCP_BADARG_N(env, 0, "Bad ref");
if (!enif_inspect_iolist_as_binary(env, argv[1], &text))
- return EXCP_BADARG(env, "Bad text");
+ return EXCP_BADARG_N(env, 1, "Bad text");
if (EVP_DigestSignUpdate(obj->ctx, text.data, text.size) != 1)
return EXCP_ERROR(env, "EVP_DigestSignUpdate");
@@ -728,7 +728,7 @@ ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifBinary ret_bin;
if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj))
- return EXCP_BADARG(env, "Bad ref");
+ return EXCP_BADARG_N(env, 0, "Bad ref");
if (EVP_DigestSignFinal(obj->ctx, NULL, &size) != 1)
return EXCP_ERROR(env, "Can't get sign size");
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 5d72114055..473be885c9 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -42,7 +42,7 @@
-export([privkey_to_pubkey/2]).
-export([ec_curve/1, ec_curves/0]).
-export([rand_seed/1]).
-
+-export([format_error/2]).
%%%----------------------------------------------------------------
%% Removed functions.
%%
@@ -403,23 +403,47 @@
%%--------------------------------------------------------------------
-%%
-%% Make the new descriptive_error() look like the old run_time_error()
-%%
--define(COMPAT(CALL),
- try begin CALL end
- catch
- error:{error, {_File,_Line}, _Reason} ->
- error(badarg);
- error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg ->
- error(E)
- end).
-
+%% Compilation and loading
%%--------------------------------------------------------------------
-compile(no_native).
-on_load(on_load/0).
-define(CRYPTO_NIF_VSN,302).
+%%--------------------------------------------------------------------
+%% When generating documentation from crypto.erl, the macro ?CRYPTO_VSN is not defined.
+%% That causes the doc generation to stop...
+-ifndef(CRYPTO_VSN).
+-define(CRYPTO_VSN, "??").
+-endif.
+
+%%--------------------------------------------------------------------
+%% Call a nif and handle an error exceptions to fit into the error handling
+%% in the Erlang shell.
+%% If not called from a shell, an error exception will be propagated.
+
+-define(nif_call(Call), ?nif_call(Call, undefined, {})).
+
+-define(nif_call(Call, ArgMap), ?nif_call(Call, undefined, ArgMap)).
+
+-define(nif_call(Call, Args0, ArgMap),
+ try Call
+ catch
+ error
+ : {Id, #{c_file_name := C_file,
+ c_file_line_num := C_line,
+ c_function_arg_num := ArgNum}, Msg}
+ : Stack when is_list(C_file),
+ is_integer(C_line),
+ is_integer(ArgNum) ->
+ error({Id, {C_file,C_line}, Msg},
+ err_find_args(Args0, Stack),
+ [{error_info, #{erl_function_arg_num => err_remap_C_argnum(ArgNum, ArgMap)}}]
+ )
+ end).
+
+%%--------------------------------------------------------------------
+%% Error if the crypto nifs not are loaded
+
-define(nif_stub,nif_stub_error(?LINE)).
nif_stub_error(Line) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}).
@@ -427,16 +451,24 @@ nif_stub_error(Line) ->
%%--------------------------------------------------------------------
%%% API
%%--------------------------------------------------------------------
-%% Crypto app version history:
-%% (no version): Driver implementation
-%% 2.0 : NIF implementation, requires OTP R14
+version() ->
+ ?CRYPTO_VSN.
+
+format_error({Ex, {C_file,C_line}, Msg}, [{_M,_F,_Args,Opts} | _CallStack]) when Ex == badarg ;
+ Ex == notsup ->
+ case proplists:get_value(error_info, Opts) of
+ #{erl_function_arg_num := ErlArgNum} ->
+ FileMsg =
+ io_lib:format("(Found in the internal file ~s at line ~p)", [C_file, C_line]),
+ case ErlArgNum of
+ undefined ->
+ #{general => [Msg," ",FileMsg]};
+ N ->#{N => Msg,
+ general => FileMsg
+ }
+ end
+ end.
-%% When generating documentation from crypto.erl, the macro ?CRYPTO_VSN is not defined.
-%% That causes the doc generation to stop...
--ifndef(CRYPTO_VSN).
--define(CRYPTO_VSN, "??").
--endif.
-version() -> ?CRYPTO_VSN.
-spec start() -> ok | {error, Reason::term()}.
start() ->
@@ -769,11 +801,9 @@ cipher_info(Type) ->
FlagOrOptions :: crypto_opts() | boolean(),
State :: crypto_state() .
crypto_init(Cipher, Key, FlagOrOptions) ->
- ng_crypto_init_nif(Cipher,
- iolist_to_binary(Key),
- <<>>,
- get_crypto_opts(FlagOrOptions)).
-
+ ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, <<>>, FlagOrOptions),
+ {1,2,-1,3}
+ ).
-spec crypto_init(Cipher, Key, IV, FlagOrOptions) -> State | descriptive_error()
when Cipher :: cipher_iv(),
@@ -782,40 +812,7 @@ crypto_init(Cipher, Key, FlagOrOptions) ->
FlagOrOptions :: crypto_opts(),
State :: crypto_state() .
crypto_init(Cipher, Key, IV, FlagOrOptions) ->
- ng_crypto_init_nif(Cipher,
- iolist_to_binary(Key),
- iolist_to_binary(IV),
- get_crypto_opts(FlagOrOptions)).
-
-%%%----------------------------------------------------------------
-get_crypto_opts(Options) when is_list(Options) ->
- lists:foldl(fun chk_opt/2,
- #{encrypt => true,
- padding => undefined
- },
- Options);
-get_crypto_opts(Flag) when is_boolean(Flag) ->
- #{encrypt => Flag,
- padding => undefined
- };
-get_crypto_opts(X) ->
- error({badarg,{bad_option,X}}).
-
-
-chk_opt({Tag,Val}, A) ->
- case ok_opt(Tag,Val) of
- true ->
- A#{Tag => Val};
- false ->
- error({badarg,{bad_option,{Tag,Val}}})
- end;
-chk_opt(X, _) ->
- error({badarg,{bad_option,X}}).
-
-
-ok_opt(encrypt, V) -> lists:member(V, [true, false, undefined]);
-ok_opt(padding, V) -> lists:member(V, [none, pkcs_padding, zero, random, undefined]);
-ok_opt(_, _) -> false.
+ ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, IV, FlagOrOptions)).
%%%----------------------------------------------------------------
-spec crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> State | descriptive_error()
@@ -825,10 +822,10 @@ ok_opt(_, _) -> false.
State :: crypto_state() .
crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) ->
%% The IV is supposed to be supplied by calling crypto_update/3
- ng_crypto_init_nif(Cipher,
- iolist_to_binary(Key),
- undefined,
- get_crypto_opts(FlagOrOptions)).
+ ?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, undefined, FlagOrOptions),
+ [Cipher, Key, FlagOrOptions],
+ {1,2,-1,3}
+ ).
%%%----------------------------------------------------------------
%%%
@@ -842,7 +839,7 @@ crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) ->
Data :: iodata(),
Result :: binary() .
crypto_update(State, Data) ->
- ng_crypto_update_nif(State, iolist_to_binary(Data)).
+ ?nif_call(ng_crypto_update_nif(State, Data)).
%%%----------------------------------------------------------------
-spec crypto_dyn_iv_update(State, Data, IV) -> Result | descriptive_error()
@@ -851,7 +848,7 @@ crypto_update(State, Data) ->
IV :: iodata(),
Result :: binary() .
crypto_dyn_iv_update(State, Data, IV) ->
- ng_crypto_update_nif(State, iolist_to_binary(Data), iolist_to_binary(IV)).
+ ?nif_call(ng_crypto_update_nif(State, Data, IV)).
%%%----------------------------------------------------------------
%%%
@@ -863,7 +860,7 @@ crypto_dyn_iv_update(State, Data, IV) ->
when State :: crypto_state(),
FinalResult :: binary() .
crypto_final(State) ->
- ng_crypto_final_nif(State).
+ ?nif_call(ng_crypto_final_nif(State)).
%%%----------------------------------------------------------------
%%%
@@ -873,7 +870,7 @@ crypto_final(State) ->
when State :: crypto_state(),
Result :: map() .
crypto_get_data(State) ->
- ng_crypto_get_data_nif(State).
+ ?nif_call(ng_crypto_get_data_nif(State)).
%%%----------------------------------------------------------------
%%%
@@ -890,11 +887,10 @@ crypto_get_data(State) ->
Result :: binary() .
crypto_one_time(Cipher, Key, Data, FlagOrOptions) ->
- ng_crypto_one_time_nif(Cipher,
- iolist_to_binary(Key),
- <<>>,
- iolist_to_binary(Data),
- get_crypto_opts(FlagOrOptions)).
+ ?nif_call(ng_crypto_one_time_nif(alias(Cipher,Key), Key, <<>>, Data, FlagOrOptions),
+ [Cipher, Key, Data, FlagOrOptions],
+ {1,2,-1,3,4}
+ ).
-spec crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
@@ -907,11 +903,9 @@ crypto_one_time(Cipher, Key, Data, FlagOrOptions) ->
Result :: binary() .
crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
- ng_crypto_one_time_nif(Cipher,
- iolist_to_binary(Key),
- iolist_to_binary(IV),
- iolist_to_binary(Data),
- get_crypto_opts(FlagOrOptions)).
+ ?nif_call(ng_crypto_one_time_nif(alias(Cipher,Key), Key, IV, Data, FlagOrOptions),
+ [Cipher, Key, IV, Data, FlagOrOptions],
+ {}).
%%%----------------------------------------------------------------
-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) ->
@@ -927,7 +921,9 @@ crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
OutTag :: binary().
crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, true) ->
- crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, aead_tag_len(Cipher), true).
+ ?nif_call(aead_cipher_nif(alias(Cipher,Key), Key, IV, PlainText, AAD, aead_tag_len(Cipher), true),
+ {1,2,3,4,5,-1,6}
+ ).
-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, TagOrTagLength, EncFlag) ->
@@ -949,8 +945,10 @@ crypto_one_time_aead(Cipher, Key, IV, PlainText, AAD, true) ->
OutPlainText :: binary().
crypto_one_time_aead(Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg) ->
- aead_cipher(Cipher, iolist_to_binary(Key), IV,
- TextIn, AAD, TagOrTagLength, EncFlg).
+ ?nif_call(aead_cipher_nif(alias(Cipher,Key), Key, IV, TextIn, AAD, TagOrTagLength, EncFlg),
+ [Cipher, Key, IV, TextIn, AAD, TagOrTagLength, EncFlg],
+ {}
+ ).
aead_tag_len(aes_ccm ) -> 12;
@@ -968,12 +966,7 @@ aead_tag_len(_) ->
%%%----------------------------------------------------------------
%%% Cipher NIFs
-ng_crypto_init_nif(Cipher, Key, IVec, #{encrypt := EncryptFlag,
- padding := Padding}) ->
- ng_crypto_init_nif(alias(Cipher,Key), Key, IVec, EncryptFlag, Padding).
-
-ng_crypto_init_nif(_Cipher, _Key, _IVec, _EncryptFlag, _Padding) -> ?nif_stub.
-
+ng_crypto_init_nif(_Cipher, _Key, _IVec, _OptionsMap) -> ?nif_stub.
ng_crypto_update_nif(_State, _Data) -> ?nif_stub.
ng_crypto_update_nif(_State, _Data, _IV) -> ?nif_stub.
@@ -982,15 +975,7 @@ ng_crypto_final_nif(_State) -> ?nif_stub.
ng_crypto_get_data_nif(_State) -> ?nif_stub.
-ng_crypto_one_time_nif(Cipher, Key, IVec, Data, #{encrypt := EncryptFlag,
- padding := Padding}) ->
- ng_crypto_one_time_nif(alias(Cipher,Key), Key, IVec, Data, EncryptFlag, Padding).
-
-ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlag, _Padding) -> ?nif_stub.
-
-%% The default tag length is EVP_GCM_TLS_TAG_LEN(16),
-aead_cipher(Cipher, Key, IVec, AAD, In, TagOrLength, EncFlg) ->
- aead_cipher_nif(alias(Cipher,Key), Key, IVec, AAD, In, TagOrLength, EncFlg).
+ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _OptionsMap) -> ?nif_stub.
aead_cipher_nif(_Type, _Key, _Ivec, _AAD, _In, _TagOrTagLength, _EncFlg) -> ?nif_stub.
@@ -1000,13 +985,13 @@ cipher_info_nif(_Type) -> ?nif_stub.
%%% Cipher aliases
%%%
-alias(aes_cbc, Key) -> alias1(aes_cbc, size(Key));
-alias(aes_cfb8, Key) -> alias1(aes_cfb8, size(Key));
-alias(aes_cfb128, Key) -> alias1(aes_cfb128, size(Key));
-alias(aes_ctr, Key) -> alias1(aes_ctr, size(Key));
-alias(aes_ecb, Key) -> alias1(aes_ecb, size(Key));
-alias(aes_gcm, Key) -> alias1(aes_gcm, size(Key));
-alias(aes_ccm, Key) -> alias1(aes_ccm, size(Key));
+alias(aes_cbc, Key) -> alias1(aes_cbc, iolist_size(Key));
+alias(aes_cfb8, Key) -> alias1(aes_cfb8, iolist_size(Key));
+alias(aes_cfb128, Key) -> alias1(aes_cfb128, iolist_size(Key));
+alias(aes_ctr, Key) -> alias1(aes_ctr, iolist_size(Key));
+alias(aes_ecb, Key) -> alias1(aes_ecb, iolist_size(Key));
+alias(aes_gcm, Key) -> alias1(aes_gcm, iolist_size(Key));
+alias(aes_ccm, Key) -> alias1(aes_ccm, iolist_size(Key));
alias(Alg, _) -> Alg.
@@ -2511,3 +2496,24 @@ choose_otp_test_engine([LibName | T], Type, Acc) ->
end;
choose_otp_test_engine([], _, Acc) ->
Acc.
+
+%%%----------------------------------------------------------------
+%%% Error internals
+
+err_find_args(undefined, [{?MODULE,_F,Args,_Info}|_]) -> Args;
+err_find_args(Args, _) -> Args.
+
+
+err_remap_C_argnum(ArgNum, ArgMap) ->
+ try
+ element(ArgNum + 1, ArgMap) % 0-numbered in C-file's argv[], 1-numbered in the tuple
+ of
+ N when is_integer(N), N>0 -> N;
+ _ -> undefined
+ catch
+ error:badarg when ArgNum >= 0 -> ArgNum+1; % short ArgMap
+ error:badarg -> undefined
+ end.
+
+
+%%%----------------------------------------------------------------