diff options
author | Hans Nilsson <hans@erlang.org> | 2021-10-06 10:28:56 +0200 |
---|---|---|
committer | Hans Nilsson <hans@erlang.org> | 2021-10-06 10:28:56 +0200 |
commit | 0569c3fca57c26c583cea7cc7b5f1c68d28edcf1 (patch) | |
tree | 89c8c383e0feec6a98570a9227f5f09db256c56d | |
parent | 0b53017918a0e6470aa4785b612d4bd21de702c7 (diff) | |
parent | b51754190f94d8da328f6fc0f0cc4622ccce8205 (diff) | |
download | erlang-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.in | 1 | ||||
-rw-r--r-- | lib/crypto/c_src/aead.c | 42 | ||||
-rw-r--r-- | lib/crypto/c_src/api_ng.c | 255 | ||||
-rw-r--r-- | lib/crypto/c_src/atoms.c | 4 | ||||
-rw-r--r-- | lib/crypto/c_src/atoms.h | 2 | ||||
-rw-r--r-- | lib/crypto/c_src/cipher.c | 9 | ||||
-rw-r--r-- | lib/crypto/c_src/common.c | 53 | ||||
-rw-r--r-- | lib/crypto/c_src/common.h | 19 | ||||
-rw-r--r-- | lib/crypto/c_src/crypto.c | 4 | ||||
-rw-r--r-- | lib/crypto/c_src/mac.c | 66 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 214 |
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. + + +%%%---------------------------------------------------------------- |