diff options
-rw-r--r-- | lib/crypto/c_src/crypto.c | 13 | ||||
-rw-r--r-- | lib/crypto/c_src/hmac.c | 11 | ||||
-rw-r--r-- | lib/crypto/c_src/hmac.h | 3 | ||||
-rw-r--r-- | lib/crypto/c_src/mac.c | 294 | ||||
-rw-r--r-- | lib/crypto/c_src/mac.h | 5 | ||||
-rw-r--r-- | lib/crypto/src/crypto.erl | 75 |
6 files changed, 337 insertions, 64 deletions
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index ab6907f828..802818541b 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -73,11 +73,10 @@ static ErlNifFunc nif_funcs[] = { {"hash_init_nif", 1, hash_init_nif, 0}, {"hash_update_nif", 2, hash_update_nif, 0}, {"hash_final_nif", 1, hash_final_nif, 0}, - {"hmac_init_nif", 2, hmac_init_nif, 0}, - {"hmac_update_nif", 2, hmac_update_nif, 0}, - {"hmac_final_nif", 1, hmac_final_nif, 0}, - {"hmac_final_nif", 2, hmac_final_nif, 0}, {"mac_nif", 4, mac_nif, 0}, + {"mac_init_nif", 3, mac_init_nif, 0}, + {"mac_update_nif", 2, mac_update_nif, 0}, + {"mac_final_nif", 1, mac_final_nif, 0}, {"cipher_info_nif", 1, cipher_info_nif, 0}, {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif, 0}, {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, @@ -176,9 +175,15 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) if (!enif_inspect_binary(env, tpl_array[1], &lib_bin)) return __LINE__; +#ifdef HAS_EVP_PKEY_CTX + if (!init_mac_ctx(env)) { + return __LINE__; + } +#else if (!init_hmac_ctx(env)) { return __LINE__; } +#endif if (!init_hash_ctx(env)) { return __LINE__; } diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c index 060ad6230f..1a4f96e1d5 100644 --- a/lib/crypto/c_src/hmac.c +++ b/lib/crypto/c_src/hmac.c @@ -18,6 +18,8 @@ * %CopyrightEnd% */ +#ifndef HAS_EVP_PKEY_CTX + #include "hmac.h" #include "digest.h" @@ -63,17 +65,17 @@ static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) } ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key) */ +{/* (hmac, Type, Key) */ struct digest_type_t *digp = NULL; ErlNifBinary key; ERL_NIF_TERM ret; struct hmac_context *obj = NULL; - ASSERT(argc == 2); + ASSERT(argc == 3); - if ((digp = get_digest_type(argv[0])) == NULL) + if ((digp = get_digest_type(argv[1])) == NULL) goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + if (!enif_inspect_iolist_as_binary(env, argv[2], &key)) goto bad_arg; if (key.size > INT_MAX) goto bad_arg; @@ -213,3 +215,4 @@ ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return ret; } +#endif diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h index 01c6d3d226..5df837a874 100644 --- a/lib/crypto/c_src/hmac.h +++ b/lib/crypto/c_src/hmac.h @@ -21,6 +21,8 @@ #ifndef E_HMAC_H__ #define E_HMAC_H__ 1 +#ifndef HAS_EVP_PKEY_CTX + #include "common.h" int init_hmac_ctx(ErlNifEnv *env); @@ -28,5 +30,6 @@ int init_hmac_ctx(ErlNifEnv *env); ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif #endif /* E_HMAC_H__ */ diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c index 0ee431f394..0482f97028 100644 --- a/lib/crypto/c_src/mac.c +++ b/lib/crypto/c_src/mac.c @@ -21,6 +21,7 @@ #include "common.h" #include "cipher.h" #include "digest.h" +#include "hmac.h" #include "mac.h" /*************************** @@ -184,7 +185,6 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) size_t size; EVP_PKEY *pkey = NULL; EVP_MD_CTX *mctx = NULL; - EVP_PKEY_CTX *pctx = NULL; #endif /*--------------------------------- @@ -341,7 +341,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) goto err; } - if (EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, pkey) != 1) + if (EVP_DigestSignInit(mctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) { return_term = EXCP_ERROR(env, "EVP_DigestSign"); goto err; @@ -399,7 +399,7 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return_term = enif_make_binary(env, &ret_bin); ret_bin_alloc = 0; - out: /* Common for success: and for err: */ + err: #ifdef HAS_EVP_PKEY_CTX if (pkey) @@ -408,17 +408,10 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) EVP_MD_CTX_free(mctx); #endif - return return_term; - - - /***************** - Error exit - ******************/ - err: if (ret_bin_alloc) enif_release_binary(&ret_bin); - goto out; + return return_term; } @@ -511,3 +504,282 @@ int hmac_low_level(ErlNifEnv* env, const EVP_MD *md, return 1; } #endif + + +/******************************************************************* + * + * Mac ctx + * + ******************************************************************/ + +int init_mac_ctx(ErlNifEnv *env); + +struct mac_context +{ + EVP_MD_CTX *ctx; +}; + +static ErlNifResourceType* mac_context_rtype; + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context*); + +int init_mac_ctx(ErlNifEnv *env) { + mac_context_rtype = enif_open_resource_type(env, NULL, "mac_context", + (ErlNifResourceDtor*) mac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (mac_context_rtype == NULL) + goto err; + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'mac_context'"); + return 0; +} + + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context *obj) +{ + if (obj == NULL) + return; + + if (obj->ctx) + EVP_MD_CTX_free(obj->ctx); +} + +/******************************************************************* + * + * Mac nif + * + ******************************************************************/ + +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + struct mac_type_t *macp; + ErlNifBinary key_bin; + ERL_NIF_TERM return_term; + const EVP_MD *md = NULL; + EVP_PKEY *pkey = NULL; + + /*--------------------------------- + Get common indata and validate it + */ + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) + { + return_term = EXCP_BADARG(env, "Bad key"); + goto err; + } + + if (!(macp = get_mac_type(argv[0]))) + { + return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + goto err; + } + + /*-------------------------------------------------- + Algorithm dependent indata checking and computation. + If EVP_PKEY is available, only set the pkey variable + and do the computation after the switch statement. + If not available, do the low-level calls in the + corresponding case part + */ + switch (macp->type) { + + /******** + * HMAC * + ********/ + case HMAC_mac: + { + struct digest_type_t *digp; + + if ((digp = get_digest_type(argv[1])) == NULL) + { + return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + goto err; + } + if (digp->md.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + goto err; + } + + md = digp->md.p; + +# ifdef HAVE_PKEY_new_raw_private_key + /* Prefered for new applications according to EVP_PKEY_new_mac_key(3) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# else + /* Available in older versions */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# endif + } + break; + + + /******** + * CMAC * + ********/ +#if defined(HAVE_CMAC) && defined(HAVE_EVP_PKEY_new_CMAC_key) + case CMAC_mac: + { + const struct cipher_type_t *cipherp; + if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) + { /* Something went wrong. Find out what by retrying in another way. */ + if (!get_cipher_type_no_key(argv[1])) + return_term = EXCP_BADARG(env, "Unknown cipher"); + else + /* Cipher exists, so it must be the key size that is wrong */ + return_term = EXCP_BADARG(env, "Bad key size"); + goto err; + } + + if (FORBIDDEN_IN_FIPS(cipherp)) + { + return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + goto err; + } + + if (cipherp->cipher.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + goto err; + } + + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); + } + break; +#endif /* HAVE_CMAC && HAVE_EVP_PKEY_new_CMAC_key */ + + + /************ + * POLY1305 * + ************/ +#ifdef HAVE_POLY1305 + case POLY1305_mac: + if (key_bin.size != 32) + { + return_term = EXCP_BADARG(env, "Bad key size, != 32 bytes"); + goto err; + } + /* poly1305 implies that EVP_PKEY_new_raw_private_key exists */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size); + break; +#endif + + + /*************** + * Unknown MAC * + ***************/ + 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"); + goto err; + } + + /*----------------------------------------- + Common computations + */ + if (!pkey) + { + return_term = EXCP_ERROR(env, "EVP_PKEY_key creation"); + goto err; + } + + if ((obj = enif_alloc_resource(mac_context_rtype, sizeof(struct mac_context))) == NULL) + { + return_term = EXCP_ERROR(env, "Can't allocate mac_context_rtype"); + goto err; + } + + if ((obj->ctx = EVP_MD_CTX_new()) == NULL) + { + return_term = EXCP_ERROR(env, "EVP_MD_CTX_new"); + goto err; + } + + if (EVP_DigestSignInit(obj->ctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSign"); + goto err; + } + + return_term = enif_make_resource(env, obj); + + err: + + if (obj) + enif_release_resource(obj); + + if (pkey) + EVP_PKEY_free(pkey); + + return return_term; + +#else + if (argv[0] != atom_hmac) + return EXCP_NOTSUP(env, "Unsupported mac algorithm"); + + return hmac_init_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref, Text) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + ErlNifBinary text; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (EVP_DigestSignUpdate(obj->ctx, text.data, text.size) != 1) + return EXCP_ERROR(env, "EVP_DigestSignUpdate"); + + CONSUME_REDS(env, text); + return argv[0]; + +#else + return hmac_update_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj; + size_t size; + ErlNifBinary ret_bin; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (EVP_DigestSignFinal(obj->ctx, NULL, &size) != 1) + return EXCP_ERROR(env, "Can't get sign size"); + + if (!enif_alloc_binary(size, &ret_bin)) + return EXCP_ERROR(env, "Alloc binary"); + + if (EVP_DigestSignFinal(obj->ctx, ret_bin.data, &size) != 1) + { + enif_release_binary(&ret_bin); + return EXCP_ERROR(env, "Signing"); + } + + return enif_make_binary(env, &ret_bin); + +#else + return hmac_final_nif(env, argc, argv); +#endif +} + diff --git a/lib/crypto/c_src/mac.h b/lib/crypto/c_src/mac.h index 00b94a2232..053a331324 100644 --- a/lib/crypto/c_src/mac.h +++ b/lib/crypto/c_src/mac.h @@ -23,6 +23,7 @@ #include "common.h" +int init_mac_ctx(ErlNifEnv *env); void init_mac_types(ErlNifEnv* env); @@ -30,4 +31,8 @@ ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env); ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + #endif /* E_MAC_H__ */ diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index d2a5786be8..371a2ee451 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -63,7 +63,8 @@ crypto_dyn_iv_init/3, crypto_dyn_iv_update/3, supports/1, - mac/3, mac/4, mac/5 + mac/3, mac/4, mac/5, + mac_init/3, mac_update/2, mac_final/1 ]). @@ -617,17 +618,33 @@ hash_final(Context) -> %%%================================================================ mac(Type, SubType, Key, Data, MacLength) -> - erlang:binary_part(mac(Type, SubType, Key, Data), 0, MacLength). + erlang:binary_part(mac(Type,SubType,Key,Data), 0, MacLength). -mac(poly1305, _, Key, Data) -> mac_nif(poly1305, undefined, Key, Data); +mac(poly1305, _, Key, Data) -> mac(poly1305, undefined, Key, Data); mac(Type, SubType, Key, Data) -> mac_nif(Type, SubType, Key, Data). mac(poly1305, Key, Data) -> mac(poly1305, undefined, Key, Data). - + +mac_init(Type, SubType, Key) -> + mac_init_nif(Type, SubType, Key). + +mac_update(Ref, Data) -> + mac_update_nif(Ref, Data). + +mac_final(Ref) -> + mac_final_nif(Ref). + +mac_final(Ref, MacLength) -> + erlang:binary_part(mac_final(Ref), 0, MacLength). + mac_nif(_Type, _SubType, _Key, _Data) -> ?nif_stub. +mac_init_nif(_Type, _SubType, _Key) -> ?nif_stub. +mac_update_nif(_Ref, _Data) -> ?nif_stub. +mac_final_nif(_Ref) -> ?nif_stub. + %%%---- HMAC @@ -641,8 +658,7 @@ mac_nif(_Type, _SubType, _Key, _Data) -> ?nif_stub. Data :: iodata(), Mac :: binary() . hmac(Type, Key, Data) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, undefined, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(mac(hmac, Type, Key, Data)). -spec hmac(Type, Key, Data, MacLength) -> Mac when Type :: hmac_hash_algorithm(), @@ -652,8 +668,7 @@ hmac(Type, Key, Data) -> Mac :: binary() . hmac(Type, Key, Data, MacLength) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, MacLength, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(mac(hmac, Type, Key, Data, MacLength)). %%%---- hmac_init, hamc_update, hmac_final @@ -664,29 +679,28 @@ hmac(Type, Key, Data, MacLength) -> Key :: iodata(), State :: hmac_state() . hmac_init(Type, Key) -> - notsup_to_error(hmac_init_nif(Type, Key)). + ?COMPAT(mac_init(hmac, Type, Key)). %%%---- hmac_update -spec hmac_update(State, Data) -> NewState when Data :: iodata(), State :: hmac_state(), NewState :: hmac_state(). -hmac_update(State, Data0) -> - Data = iolist_to_binary(Data0), - hmac_update(State, Data, erlang:byte_size(Data), max_bytes()). +hmac_update(State, Data) -> + ?COMPAT(mac_update(State, Data)). %%%---- hmac_final -spec hmac_final(State) -> Mac when State :: hmac_state(), Mac :: binary(). hmac_final(Context) -> - notsup_to_error(hmac_final_nif(Context)). + ?COMPAT(mac_final(Context)). -spec hmac_final_n(State, HashLen) -> Mac when State :: hmac_state(), HashLen :: integer(), Mac :: binary(). hmac_final_n(Context, HashLen) -> - notsup_to_error(hmac_final_nif(Context, HashLen)). + ?COMPAT(mac_final(Context, HashLen)). %%%---- CMAC @@ -708,14 +722,14 @@ cmac(Type, Key, Data) -> Mac :: binary(). cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). + ?COMPAT(mac(cmac, alias(Type), Key, Data, MacLength)). %%%---- POLY1305 -spec poly1305(iodata(), iodata()) -> Mac when Mac :: binary(). poly1305(Key, Data) -> - ?COMPAT( mac(poly1305, Key, Data) ). + ?COMPAT(mac(poly1305, Key, Data)). %%%================================================================ %%% @@ -2260,35 +2274,6 @@ hash_init_nif(_Hash) -> ?nif_stub. hash_update_nif(_State, _Data) -> ?nif_stub. hash_final_nif(_State) -> ?nif_stub. -%% HMAC -------------------------------------------------------------------- - -hmac(Type, Key, Data, MacSize, Size, MaxBytes) when Size =< MaxBytes -> - ?COMPAT( - case MacSize of - undefined -> mac(hmac, Type, Key, Data); - _ -> mac(hmac, Type, Key, Data, MacSize) - end - ); -hmac(Type, Key, Data, MacSize, Size, MaxBytes) -> - State0 = hmac_init(Type, Key), - State1 = hmac_update(State0, Data, Size, MaxBytes), - case MacSize of - undefined -> hmac_final(State1); - _ -> hmac_final_n(State1, MacSize) - end. - -hmac_update(State, Data, Size, MaxBytes) when Size =< MaxBytes -> - notsup_to_error(hmac_update_nif(State, Data)); -hmac_update(State0, Data, _, MaxBytes) -> - <<Increment:MaxBytes/binary, Rest/binary>> = Data, - State = notsup_to_error(hmac_update_nif(State0, Increment)), - hmac_update(State, Rest, erlang:byte_size(Rest), MaxBytes). - -hmac_init_nif(_Type, _Key) -> ?nif_stub. -hmac_update_nif(_Context, _Data) -> ?nif_stub. -hmac_final_nif(_Context) -> ?nif_stub. -hmac_final_nif(_Context, _MacSize) -> ?nif_stub. - %% CIPHERS -------------------------------------------------------------------- cipher_info_nif(_Type) -> ?nif_stub. |