summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Nilsson <hans@erlang.org>2021-09-24 09:27:32 +0200
committerHans Nilsson <hans@erlang.org>2021-10-06 10:27:09 +0200
commit9962ca7b1d5a32780e9d0a13aaaa29f8d6574fed (patch)
tree02dfc5301fa84c256e4da4dae272101f7ca66283
parent951e07fc8e3418c926f1d1ae31188fe8bd9a324e (diff)
downloaderlang-9962ca7b1d5a32780e9d0a13aaaa29f8d6574fed.tar.gz
crypto: Do options checking in C. Simplification
in the options handling. Avoids building a map.
-rw-r--r--lib/crypto/c_src/api_ng.c168
-rw-r--r--lib/crypto/src/crypto.erl41
2 files changed, 123 insertions, 86 deletions
diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c
index 8492ce1e5b..35404ba1e0 100644
--- a/lib/crypto/c_src/api_ng.c
+++ b/lib/crypto/c_src/api_ng.c
@@ -86,72 +86,138 @@ int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
/* 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 argv[],
int cipher_arg_num,
int key_arg_num,
int ivec_arg_num,
- int optsmap_arg_num,
- /* int encflg_arg_num, */
- /* int padding_arg_num, */
+ int opts_arg_num,
const struct cipher_type_t **cipherp,
ERL_NIF_TERM *return_term)
{
int ivec_len;
ErlNifBinary key_bin;
ErlNifBinary ivec_bin;
- ERL_NIF_TERM encflg, padflg;
ctx_res->ctx = NULL; /* For testing if *ctx should be freed after errors */
#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 options from optsmap ****/
- if (!enif_is_map(env, argv[optsmap_arg_num]))
- {
- *return_term = EXCP_BADARG_N(env, optsmap_arg_num, "Options map expected");
- goto err;
- }
- /** Fetch encrypt_flag **/
- if (!enif_get_map_value(env, argv[optsmap_arg_num], atom_encrypt, &encflg))
- {
- *return_term = EXCP_BADARG_N(env, optsmap_arg_num, "Missing encrypt flag in Options");
- goto err;
- }
- else if (encflg == atom_true)
- ctx_res->encflag = 1;
- else if (encflg == atom_false)
- ctx_res->encflag = 0;
- else if (encflg == atom_undefined)
- /* For compat funcs in crypto.erl */
- ctx_res->encflag = -1;
- else
- {
- *return_term = EXCP_BADARG_N(env, optsmap_arg_num, "Bad encrypt flag");
- goto err;
- }
- /** Fetch padding flag **/
- if (!enif_get_map_value(env, argv[optsmap_arg_num], atom_padding, &padflg))
- padflg = atom_undefined;
- else if ((padflg != atom_undefined) &&
- (padflg != atom_none) &&
- (padflg != atom_zero) &&
- (padflg != atom_random) &&
- (padflg != atom_pkcs_padding)
- )
- {
- *return_term = EXCP_BADARG_N(env, optsmap_arg_num, "Bad padding flag");
- goto err;
- }
- /**** end of options checking ****/
+
+ /* 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, argv[key_arg_num], &key_bin))
+ if (!enif_inspect_iolist_as_binary(env, argv[key_arg_num], &key_bin))
{
*return_term = EXCP_BADARG_N(env, key_arg_num, "Bad key");
goto err;
@@ -215,7 +281,7 @@ static int get_init_args(ErlNifEnv* env,
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 && (argv[ivec_arg_num] != atom_undefined)) {
@@ -312,11 +378,9 @@ static int get_init_args(ErlNifEnv* env,
}
/* Set padding */
- if (padflg != atom_pkcs_padding) /* pkcs_padding is default */
+ if (ctx_res->padding != atom_pkcs_padding) /* pkcs_padding is default */
EVP_CIPHER_CTX_set_padding(ctx_res->ctx, 0);
- ctx_res->padding = padflg;
-
*return_term = atom_ok;
#if !defined(HAVE_EVP_AES_CTR)
@@ -510,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;
}
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 9e83d50616..1c081dd0f8 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -800,8 +800,7 @@ cipher_info(Type) ->
Key :: iodata(),
FlagOrOptions :: crypto_opts() | boolean(),
State :: crypto_state() .
-crypto_init(Cipher, Key, FlagOrOptions0) ->
- FlagOrOptions = get_crypto_opts(FlagOrOptions0),
+crypto_init(Cipher, Key, FlagOrOptions) ->
?nif_call(ng_crypto_init_nif(alias(Cipher,Key), Key, <<>>, FlagOrOptions),
{1,2,-1,3}).
@@ -815,37 +814,7 @@ crypto_init(Cipher, Key, IV, FlagOrOptions) ->
?nif_call(ng_crypto_init_nif(alias(Cipher,Key),
Key,
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.
+ FlagOrOptions)).
%%%----------------------------------------------------------------
-spec crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) -> State | descriptive_error()
@@ -859,7 +828,7 @@ crypto_dyn_iv_init(Cipher, Key, FlagOrOptions) ->
ng_crypto_init_nif(alias(Cipher,Key),
Key,
undefined,
- get_crypto_opts(FlagOrOptions)),
+ FlagOrOptions),
{1,2,-1,3}
)
.
@@ -928,7 +897,7 @@ crypto_one_time(Cipher, Key, Data, FlagOrOptions) ->
Key,
<<>>,
Data,
- get_crypto_opts(FlagOrOptions)),
+ FlagOrOptions),
[Cipher, Key, Data, FlagOrOptions],
{1,2,-1,3,4}).
@@ -947,7 +916,7 @@ crypto_one_time(Cipher, Key, IV, Data, FlagOrOptions) ->
Key,
IV,
Data,
- get_crypto_opts(FlagOrOptions))).
+ FlagOrOptions)).
%%%----------------------------------------------------------------
-spec crypto_one_time_aead(Cipher, Key, IV, InText, AAD, EncFlag::true) ->