diff --git a/Makefile b/Makefile index aa3e984..1b167ab 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,26 @@ REBAR=rebar3 +RUN_EQC=erl -pa _build/default/lib/enacl/ebin -noshell -s enacl_eqc -s init stop .PHONY: compile compile: $(REBAR) compile | sed -e 's|_build/default/lib/enacl/||g' +eqc_compile: compile + erlc -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl + +eqc_mini_compile: compile + erlc -Dmini -o _build/default/lib/enacl/ebin eqc_test/enacl_eqc.erl + +eqc_run: eqc_compile + $(RUN_EQC) + +eqc_mini_run: eqc_mini_compile + $(RUN_EQC) + +.PHONE: console +console: compile + $(REBAR) shell + .PHONY: clean clean: $(REBAR) clean diff --git a/README.md b/README.md index 464abae..0152985 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,14 @@ or rebar compile +To build and run licensed eqc test execute: + + make eqc_run + +To build and run eqc-mini version of test execute: + + make eqc_mini_run + ## Features: * Complete NaCl library, implementing all default functionality. diff --git a/c_src/Makefile b/c_src/Makefile index b762894..6fbe66e 100644 --- a/c_src/Makefile +++ b/c_src/Makefile @@ -10,7 +10,7 @@ C_SRC_DIR = $(CURDIR) C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so # System type and C compiler/flags. - +MACHINE_SYS := $(shell uname -m) UNAME_SYS := $(shell uname -s) ifeq ($(UNAME_SYS), Darwin) CC ?= cc diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 8b9601a..1046af7 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -4,17 +4,39 @@ #include +#define ATOM_OK "ok" +#define ATOM_ERROR "error" +#define ATOM_TRUE "true" +#define ATOM_FALSE "false" + +#define CRYPTO_GENERICHASH_STATE_RESOURCE "crypto_generichash_state" + +#ifdef ERL_NIF_DIRTY_JOB_CPU_BOUND +#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c,ERL_NIF_DIRTY_JOB_CPU_BOUND} +#else +#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c} +#endif + +//{"crypto_box_keypair", 0, enif_crypto_box_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND} /* Errors */ + +/* This is a global variable for resource type */ +static ErlNifResourceType *generichash_state_type = NULL; + static ERL_NIF_TERM nacl_error_tuple(ErlNifEnv *env, char *error_atom) { - return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, error_atom)); + return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, error_atom)); } /* Initialization */ static int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { - sodium_init(); - return 0; + // Create a new resource type for crypto_generichash_state + if( !(generichash_state_type = enif_open_resource_type(env, NULL, CRYPTO_GENERICHASH_STATE_RESOURCE, NULL, ERL_NIF_RT_CREATE, NULL)) ) { + return -1; + } + + return sodium_init(); } /* Low-level functions (Hashing, String Equality, ...) */ @@ -282,7 +304,9 @@ ERL_NIF_TERM enif_crypto_box(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[] return nacl_error_tuple(env, "alloc_failed"); } - crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data, pk.data, sk.data); + if( 0 != crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data, pk.data, sk.data) ) { + return nacl_error_tuple(env, "box_error"); + } return enif_make_sub_binary( env, @@ -347,7 +371,10 @@ ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM con return nacl_error_tuple(env, "alloc_failed"); } - crypto_box_beforenm(k.data, pk.data, sk.data); + if( 0 != crypto_box_beforenm(k.data, pk.data, sk.data) ) { + // error + return nacl_error_tuple(env, "error_gen_shared_secret"); + } return enif_make_binary(env, &k); } @@ -1012,10 +1039,10 @@ ERL_NIF_TERM enif_crypto_onetimeauth_verify(ErlNifEnv *env, int argc, ERL_NIF_TE static ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { - size_t req_size; + unsigned req_size; ErlNifBinary result; - if ((argc != 1) || (!enif_get_uint64(env, argv[0], &req_size))) { + if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) { return enif_make_badarg(env); } @@ -1090,7 +1117,10 @@ ERL_NIF_TERM enif_crypto_kx_server_session_keys(ErlNifEnv *env, int argc, ERL_NI return nacl_error_tuple(env, "alloc_failed"); } - crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data, server_sk.data, client_pk.data); + if( 0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data, server_sk.data, client_pk.data) ) { + // suspicious client public key + return nacl_error_tuple(env, "invalid_client_public_key"); + } return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx)); } @@ -1118,7 +1148,10 @@ ERL_NIF_TERM enif_crypto_kx_client_session_keys(ErlNifEnv *env, int argc, ERL_NI return nacl_error_tuple(env, "alloc_failed"); } - crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data, client_sk.data, server_pk.data); + if( 0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data, client_sk.data, server_pk.data) ) { + // suspicious server public key + return nacl_error_tuple(env, "invalid_server_public_key"); + } return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx)); } @@ -1198,6 +1231,293 @@ ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const return enif_make_binary(env, &out); } +static +ERL_NIF_TERM enif_crypto_pwhash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary h, p, s; + + // Validate the arguments + if( (argc != 2) || + (!enif_inspect_iolist_as_binary(env, argv[0], &p)) || + (!enif_inspect_binary(env, argv[1], &s)) ) { + return enif_make_badarg(env); + } + + // Check Salt size + if(s.size != crypto_pwhash_SALTBYTES) { + return nacl_error_tuple(env, "invalid_salt_size"); + } + + // Allocate memory for return binary + if( !enif_alloc_binary(crypto_box_SEEDBYTES, &h) ) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if( crypto_pwhash(h.data, h.size, (char *)p.data, p.size, s.data, + crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) { + /* out of memory */ + enif_release_binary(&h); + return nacl_error_tuple(env, "out_of_memory"); + } + + ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM ret = enif_make_binary(env, &h); + + return enif_make_tuple2(env, ok, ret); +} + +static +ERL_NIF_TERM enif_crypto_pwhash_str(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary h, p; + + // Validate the arguments + if( (argc != 1) || + (!enif_inspect_iolist_as_binary(env, argv[0], &p)) ) { + return enif_make_badarg(env); + } + + // Allocate memory for return binary + if( !enif_alloc_binary(crypto_pwhash_STRBYTES, &h) ) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if( crypto_pwhash_str((char *)h.data, (char *)p.data, p.size, + crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0) { + /* out of memory */ + enif_release_binary(&h); + return nacl_error_tuple(env, "out_of_memory"); + } + + ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM ret = enif_make_binary(env, &h); + + return enif_make_tuple2(env, ok, ret); +} + +static +ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary h, p; + + // Validate the arguments + if( (argc != 2) || + (!enif_inspect_binary(env, argv[0], &h)) || + (!enif_inspect_binary(env, argv[1], &p)) ) { + return enif_make_badarg(env); + } + + ERL_NIF_TERM retVal = enif_make_atom(env, ATOM_TRUE); + if( crypto_pwhash_str_verify((char *)h.data, (char *)p.data, p.size) != 0) { + /* wrong password */ + retVal = enif_make_atom(env, ATOM_FALSE); + } + + return retVal; +} + +/* + * Generic hash + */ +static +ERL_NIF_TERM enif_crypto_generichash_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_BYTES); +} + +static +ERL_NIF_TERM enif_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_BYTES_MIN); +} + +static +ERL_NIF_TERM enif_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_BYTES_MAX); +} + +static +ERL_NIF_TERM enif_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_KEYBYTES); +} + +static +ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN); +} + +static +ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX); +} + +static +ERL_NIF_TERM enif_crypto_generichash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary hash, message, key; + + unsigned hashSize; + + // Validate the arguments + if( (argc != 3) || + (!enif_get_uint(env, argv[0], &hashSize)) || + (!enif_inspect_binary(env, argv[1], &message)) || + (!enif_inspect_binary(env, argv[2], &key)) ) { + return enif_make_badarg(env); + } + + // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX + if( (hashSize < crypto_generichash_BYTES_MIN) || + (hashSize > crypto_generichash_BYTES_MAX) ) { + return nacl_error_tuple(env, "invalid_hash_size"); + } + + // validate key size + unsigned char *k = key.data; + if( 0 == key.size ) { + k = NULL; + } else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) { + return nacl_error_tuple(env, "invalid_key_size"); + } + + // allocate memory for hash + if( !enif_alloc_binary(hashSize, &hash) ) { + return nacl_error_tuple(env, "alloc_failed"); + } + + // calculate hash + if( 0 != crypto_generichash(hash.data, hash.size, message.data, message.size, k, key.size) ) { + enif_release_binary(&hash); + return nacl_error_tuple(env, "hash_error"); + } + + ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM ret = enif_make_binary(env, &hash); + + return enif_make_tuple2(env, ok, ret); +} + +static +ERL_NIF_TERM enif_crypto_generichash_init(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary key; + + unsigned hashSize; + + // Validate the arguments + if( (argc != 2) || + (!enif_get_uint(env, argv[0], &hashSize)) || + (!enif_inspect_binary(env, argv[1], &key)) ) { + return enif_make_badarg(env); + } + + // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX + if( (hashSize < crypto_generichash_BYTES_MIN) || + (hashSize > crypto_generichash_BYTES_MAX) ) { + return nacl_error_tuple(env, "invalid_hash_size"); + } + + // validate key size + unsigned char *k = key.data; + if( 0 == key.size ) { + k = NULL; + } else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) { + return nacl_error_tuple(env, "invalid_key_size"); + } + + // Create a resource for hash state + crypto_generichash_state *state = (crypto_generichash_state *)enif_alloc_resource(generichash_state_type, crypto_generichash_statebytes()); + if( !state ) { + return nacl_error_tuple(env, "alloc_failed"); + } + + // Call the library function + if( 0 != crypto_generichash_init(state, k, key.size, hashSize) ) { + return nacl_error_tuple(env, "hash_init_error"); + } + + + // Create return values + ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate"); + ERL_NIF_TERM e2 = argv[0]; + ERL_NIF_TERM e3 = enif_make_resource(env, state); + + + // release dynamically allocated memory to erlang to mange + enif_release_resource(state); + + // return a tuple + return enif_make_tuple3(env, e1, e2, e3); +} + +static +ERL_NIF_TERM enif_crypto_generichash_update(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary message; + + unsigned hashSize; + + crypto_generichash_state *state; + + // Validate the arguments + if( (argc != 3) || + (!enif_get_uint(env, argv[0], &hashSize)) || + (!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) || + (!enif_inspect_binary(env, argv[2], &message)) ) { + return enif_make_badarg(env); + } + + // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX + if( (hashSize < crypto_generichash_BYTES_MIN) || + (hashSize > crypto_generichash_BYTES_MAX) ) { + return nacl_error_tuple(env, "invalid_hash_size"); + } + + // Update hash state + if( 0 != crypto_generichash_update(state, message.data, message.size) ) { + return nacl_error_tuple(env, "hash_update_error"); + } + + + // Generate return value + ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate"); + ERL_NIF_TERM e2 = argv[0]; + ERL_NIF_TERM e3 = enif_make_resource(env, state); + + // return a tuple + return enif_make_tuple3(env, e1, e2, e3); +} + +static +ERL_NIF_TERM enif_crypto_generichash_final(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary hash; + + unsigned hashSize; + + crypto_generichash_state *state; + + // Validate the arguments + if( (argc != 2) || + (!enif_get_uint(env, argv[0], &hashSize)) || + (!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) ) { + return enif_make_badarg(env); + } + + // Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX + if( (hashSize < crypto_generichash_BYTES_MIN) || + (hashSize > crypto_generichash_BYTES_MAX) ) { + return nacl_error_tuple(env, "invalid_hash_size"); + } + + // allocate memory for hash + if( !enif_alloc_binary(hashSize, &hash) ) { + return nacl_error_tuple(env, "alloc_failed"); + } + + // calculate hash + if( 0 != crypto_generichash_final(state, hash.data, hash.size) ) { + enif_release_binary(&hash); + return nacl_error_tuple(env, "hash_error"); + } + + ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM ret = enif_make_binary(env, &hash); + + return enif_make_tuple2(env, ok, ret); +} + /* Tie the knot to the Erlang world */ static ErlNifFunc nif_funcs[] = { {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES}, @@ -1207,97 +1527,114 @@ static ErlNifFunc nif_funcs[] = { {"crypto_box_SECRETKEYBYTES", 0, enif_crypto_box_SECRETKEYBYTES}, {"crypto_box_BEFORENMBYTES", 0, enif_crypto_box_BEFORENMBYTES}, - {"crypto_box_keypair", 0, enif_crypto_box_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_box_keypair", 0, enif_crypto_box_keypair), - {"crypto_box", 4, enif_crypto_box, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_box_open", 4, enif_crypto_box_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + + erl_nif_dirty_job_cpu_bound_macro("crypto_box", 4, enif_crypto_box), + erl_nif_dirty_job_cpu_bound_macro("crypto_box_open", 4, enif_crypto_box_open), {"crypto_box_beforenm", 2, enif_crypto_box_beforenm}, {"crypto_box_afternm_b", 3, enif_crypto_box_afternm}, - {"crypto_box_afternm", 3, enif_crypto_box_afternm, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_box_afternm", 3, enif_crypto_box_afternm), {"crypto_box_open_afternm_b", 3, enif_crypto_box_open_afternm}, - {"crypto_box_open_afternm", 3, enif_crypto_box_open_afternm, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_box_open_afternm", 3, enif_crypto_box_open_afternm), {"crypto_sign_PUBLICKEYBYTES", 0, enif_crypto_sign_PUBLICKEYBYTES}, {"crypto_sign_SECRETKEYBYTES", 0, enif_crypto_sign_SECRETKEYBYTES}, - {"crypto_sign_keypair", 0, enif_crypto_sign_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_sign_keypair", 0, enif_crypto_sign_keypair), - {"crypto_sign", 2, enif_crypto_sign, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_sign_open", 2, enif_crypto_sign_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_sign", 2, enif_crypto_sign), + erl_nif_dirty_job_cpu_bound_macro("crypto_sign_open", 2, enif_crypto_sign_open), - {"crypto_sign_detached", 2, enif_crypto_sign_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_sign_detached", 2, enif_crypto_sign_detached), + erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached), {"crypto_box_SEALBYTES", 0, enif_crypto_box_SEALBYTES}, - {"crypto_box_seal", 2, enif_crypto_box_seal, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_box_seal_open", 3, enif_crypto_box_seal_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal", 2, enif_crypto_box_seal), + erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal_open", 3, enif_crypto_box_seal_open), {"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES}, {"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES}, {"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES}, {"crypto_secretbox_KEYBYTES", 0, enif_crypto_secretbox_KEYBYTES}, {"crypto_secretbox_b", 3, enif_crypto_secretbox}, - {"crypto_secretbox", 3, enif_crypto_secretbox, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox", 3, enif_crypto_secretbox), {"crypto_secretbox_open_b", 3, enif_crypto_secretbox_open}, - {"crypto_secretbox_open", 3, enif_crypto_secretbox_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3, enif_crypto_secretbox_open), {"crypto_stream_chacha20_KEYBYTES", 0, enif_crypto_stream_chacha20_KEYBYTES}, {"crypto_stream_chacha20_NONCEBYTES", 0, enif_crypto_stream_chacha20_NONCEBYTES}, {"crypto_stream_chacha20_b", 3, enif_crypto_stream_chacha20}, - {"crypto_stream_chacha20", 3, enif_crypto_stream_chacha20, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20", 3, enif_crypto_stream_chacha20), {"crypto_stream_chacha20_xor_b", 3, enif_crypto_stream_chacha20_xor}, - {"crypto_stream_chacha20_xor", 3, enif_crypto_stream_chacha20_xor, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20_xor", 3, enif_crypto_stream_chacha20_xor), {"crypto_stream_KEYBYTES", 0, enif_crypto_stream_KEYBYTES}, {"crypto_stream_NONCEBYTES", 0, enif_crypto_stream_NONCEBYTES}, {"crypto_stream_b", 3, enif_crypto_stream}, - {"crypto_stream", 3, enif_crypto_stream, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_stream", 3, enif_crypto_stream), {"crypto_stream_xor_b", 3, enif_crypto_stream_xor}, - {"crypto_stream_xor", 3, enif_crypto_stream_xor, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_stream_xor", 3, enif_crypto_stream_xor), {"crypto_auth_BYTES", 0, enif_crypto_auth_BYTES}, {"crypto_auth_KEYBYTES", 0, enif_crypto_auth_KEYBYTES}, {"crypto_auth_b", 2, enif_crypto_auth}, - {"crypto_auth", 2, enif_crypto_auth, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_auth", 2, enif_crypto_auth), {"crypto_auth_verify_b", 3, enif_crypto_auth_verify}, - {"crypto_auth_verify", 3, enif_crypto_auth_verify, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_auth_verify", 3, enif_crypto_auth_verify), - {"crypto_shorthash_BYTES", 0, enif_crypto_auth_BYTES}, + {"crypto_shorthash_BYTES", 0, enif_crypto_shorthash_BYTES}, {"crypto_shorthash_KEYBYTES", 0, enif_crypto_shorthash_KEYBYTES}, {"crypto_shorthash", 2, enif_crypto_shorthash}, {"crypto_onetimeauth_BYTES", 0, enif_crypto_onetimeauth_BYTES}, {"crypto_onetimeauth_KEYBYTES", 0, enif_crypto_onetimeauth_KEYBYTES}, {"crypto_onetimeauth_b", 2, enif_crypto_onetimeauth}, - {"crypto_onetimeauth", 2, enif_crypto_onetimeauth, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth", 2, enif_crypto_onetimeauth), {"crypto_onetimeauth_verify_b", 3, enif_crypto_onetimeauth_verify}, - {"crypto_onetimeauth_verify", 3, enif_crypto_onetimeauth_verify, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth_verify", 3, enif_crypto_onetimeauth_verify), {"crypto_hash_b", 1, enif_crypto_hash}, - {"crypto_hash", 1, enif_crypto_hash, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_hash", 1, enif_crypto_hash), {"crypto_verify_16", 2, enif_crypto_verify_16}, {"crypto_verify_32", 2, enif_crypto_verify_32}, {"sodium_memzero", 1, enif_sodium_memzero}, - {"crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_pwhash", 2, enif_crypto_pwhash}, + {"crypto_pwhash_str", 1, enif_crypto_pwhash_str}, + {"crypto_pwhash_str_verify", 2, enif_crypto_pwhash_str_verify}, - {"crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult), + + erl_nif_dirty_job_cpu_bound_macro("crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair), {"crypto_sign_ed25519_public_to_curve25519", 1, enif_crypto_sign_ed25519_public_to_curve25519}, {"crypto_sign_ed25519_secret_to_curve25519", 1, enif_crypto_sign_ed25519_secret_to_curve25519}, {"crypto_sign_ed25519_PUBLICKEYBYTES", 0, enif_crypto_sign_ed25519_PUBLICKEYBYTES}, {"crypto_sign_ed25519_SECRETKEYBYTES", 0, enif_crypto_sign_ed25519_SECRETKEYBYTES}, - {"randombytes", 1, enif_randombytes, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("randombytes", 1, enif_randombytes), - {"crypto_kx_keypair", 0, enif_crypto_kx_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_kx_client_session_keys", 3, enif_crypto_kx_client_session_keys, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"crypto_kx_server_session_keys", 3, enif_crypto_kx_server_session_keys, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + erl_nif_dirty_job_cpu_bound_macro("crypto_kx_keypair", 0, enif_crypto_kx_keypair), + erl_nif_dirty_job_cpu_bound_macro("crypto_kx_client_session_keys", 3, enif_crypto_kx_client_session_keys), + erl_nif_dirty_job_cpu_bound_macro("crypto_kx_server_session_keys", 3, enif_crypto_kx_server_session_keys), {"crypto_kx_PUBLICKEYBYTES", 0, enif_crypto_kx_PUBLICKEYBYTES}, {"crypto_kx_SECRETKEYBYTES", 0, enif_crypto_kx_SECRETKEYBYTES}, {"crypto_kx_SESSIONKEYBYTES", 0, enif_crypto_kx_SESSIONKEYBYTES}, - {"scramble_block_16", 2, enif_scramble_block_16} + {"scramble_block_16", 2, enif_scramble_block_16}, + + {"crypto_generichash_BYTES", 0, enif_crypto_generichash_BYTES}, + {"crypto_generichash_BYTES_MIN", 0, enif_crypto_generichash_BYTES_MIN}, + {"crypto_generichash_BYTES_MAX", 0, enif_crypto_generichash_BYTES_MAX}, + {"crypto_generichash_KEYBYTES", 0, enif_crypto_generichash_KEYBYTES}, + {"crypto_generichash_KEYBYTES_MIN", 0, enif_crypto_generichash_KEYBYTES_MIN}, + {"crypto_generichash_KEYBYTES_MAX", 0, enif_crypto_generichash_KEYBYTES_MAX}, + {"crypto_generichash", 3, enif_crypto_generichash}, + {"crypto_generichash_init", 2, enif_crypto_generichash_init}, + {"crypto_generichash_update", 3, enif_crypto_generichash_update}, + {"crypto_generichash_final", 2, enif_crypto_generichash_final} + }; ERL_NIF_INIT(enacl_nif, nif_funcs, enif_crypto_load, NULL, NULL, NULL); diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 8b801c4..3bc2987 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,7 +2,21 @@ -include_lib("eqc/include/eqc.hrl"). -compile(export_all). +-ifndef(mini). -compile({parse_transform, eqc_parallelize}). +-define(FAULT(Arg1, Arg2), fault(Arg1, Arg2)). +-define(FAULT_RATE(Arg1, Arg2, Arg3), fault_rate(Arg1, Arg2, Arg3)). +-else. +-define(FAULT(Arg1, Arg2), noop_fault(Arg1, Arg2)). +-define(FAULT_RATE(Arg1, Arg2, Arg3), noop_fault_rate(Arg1, Arg2, Arg3)). +-endif. + +start()-> + eqc:module(?MODULE). + +noop_fault(_Bad, Good) -> Good. + +noop_fault_rate(_1, _2, Gen) -> Gen. non_byte_int() -> oneof([ @@ -14,7 +28,7 @@ g_iolist() -> ?SIZED(Sz, g_iolist(Sz)). g_iolist(0) -> - fault( + ?FAULT( oneof([ elements([a,b,c]), real(), @@ -22,7 +36,7 @@ g_iolist(0) -> ]), return([])); g_iolist(N) -> - fault( + ?FAULT( oneof([ elements([a,b,c]), real(), @@ -34,7 +48,7 @@ g_iolist(N) -> ])). g_iodata() -> - fault( + ?FAULT( oneof([elements([a,b,c]), real()]), oneof([binary(), g_iolist(), eqc_gen:largebinary(64*1024)])). @@ -50,7 +64,7 @@ v_iodata(Structure) -> v_iolist(Structure). %% Generator for binaries of a given size with different properties and fault injection: g_binary(Sz) -> - fault(g_binary_bad(Sz), g_binary_good(Sz)). + ?FAULT(g_binary_bad(Sz), g_binary_good(Sz)). g_binary_good(Sz) when Sz =< 32 -> binary(Sz); g_binary_good(Sz) -> eqc_gen:largebinary(Sz). @@ -74,7 +88,7 @@ nonce_valid(N) -> v_binary(enacl:box_nonce_size(), N). %% Generator of natural numbers g_nat() -> - fault(g_nat_bad(), nat()). + ?FAULT(g_nat_bad(), nat()). g_nat_bad() -> oneof([ @@ -106,7 +120,7 @@ keypair_bad() -> end). keypair() -> - fault(keypair_bad(), keypair_good()). + ?FAULT(keypair_bad(), keypair_good()). %% CRYPTO BOX %% --------------------------- @@ -158,10 +172,10 @@ failure(X) -> {failure, X}. prop_box_correct() -> ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, keypair()), - fault_rate(1, 40, keypair())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, keypair()), + ?FAULT_RATE(1, 40, keypair())}, begin case v_iodata(Msg) andalso nonce_valid(Nonce) andalso keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of true -> @@ -182,10 +196,10 @@ prop_box_correct() -> prop_box_failure_integrity() -> ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, keypair()), - fault_rate(1, 40, keypair())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, keypair()), + ?FAULT_RATE(1, 40, keypair())}, begin case v_iodata(Msg) andalso nonce_valid(Nonce) @@ -207,7 +221,7 @@ prop_box_failure_integrity() -> end). prop_seal_box_failure_integrity() -> - ?FORALL({Msg, {PK1, SK1}}, {fault_rate(1,40,g_iodata()), fault_rate(1,40,keypair())}, + ?FORALL({Msg, {PK1, SK1}}, {?FAULT_RATE(1,40,g_iodata()), ?FAULT_RATE(1,40,keypair())}, begin case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of true -> @@ -225,8 +239,8 @@ prop_seal_box_failure_integrity() -> prop_seal_box_correct() -> ?FORALL({Msg, {PK1, SK1}}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, keypair())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, keypair())}, begin case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of true -> @@ -243,7 +257,7 @@ prop_seal_box_correct() -> %% PRECOMPUTATIONS beforenm_key() -> - ?LET([{PK1, SK1}, {PK2, SK2}], [fault_rate(1, 40, keypair()), fault_rate(1, 40, keypair())], + ?LET([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())], case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of true -> enacl:box_beforenm(PK1, SK2); @@ -259,7 +273,7 @@ v_key(K) when is_binary(K) -> byte_size(K) == enacl:box_beforenm_bytes(); v_key(_) -> false. prop_beforenm_correct() -> - ?FORALL([{PK1, SK1}, {PK2, SK2}], [fault_rate(1, 40, keypair()), fault_rate(1, 40, keypair())], + ?FORALL([{PK1, SK1}, {PK2, SK2}], [?FAULT_RATE(1, 40, keypair()), ?FAULT_RATE(1, 40, keypair())], case keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of true -> equals(enacl:box_beforenm(PK1, SK2), enacl:box_beforenm(PK2, SK1)); @@ -272,9 +286,9 @@ prop_beforenm_correct() -> prop_afternm_correct() -> ?FORALL([Msg, Nonce, Key], - [fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, beforenm_key())], + [?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, beforenm_key())], begin case v_iodata(Msg) andalso nonce_valid(Nonce) andalso v_key(Key) of true -> @@ -325,7 +339,7 @@ sign_keypair_good() -> return(enacl:sign_keypair()). sign_keypair() -> - fault(sign_keypair_bad(), sign_keypair_good()). + ?FAULT(sign_keypair_bad(), sign_keypair_good()). sign_keypair_public_valid(#{ public := Public }) when is_binary(Public) -> @@ -342,8 +356,8 @@ sign_keypair_valid(KP) -> prop_sign_detached() -> ?FORALL({Msg, KeyPair}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, sign_keypair())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, sign_keypair())}, begin case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of true -> @@ -358,8 +372,8 @@ prop_sign_detached() -> prop_sign() -> ?FORALL({Msg, KeyPair}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, sign_keypair())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, sign_keypair())}, begin case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of true -> @@ -403,10 +417,10 @@ signed_message_bad_d() -> {binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}. signed_message(M) -> - fault(signed_message_bad(), signed_message_good(M)). + ?FAULT(signed_message_bad(), signed_message_good(M)). signed_message_d(M) -> - fault(signed_message_bad(), signed_message_good(M)). + ?FAULT(signed_message_bad(), signed_message_good(M)). signed_message_valid({valid, _}, _) -> true; signed_message_valid({invalid, _}, _) -> true; @@ -451,18 +465,18 @@ prop_sign_open() -> key_sz(Sz) -> equals(enacl:secretbox_key_size(), Sz). -prop_key_sizes() -> - conjunction([{secret, key_sz(enacl:secretbox_key_size())}, - {stream, key_sz(enacl:stream_key_size())}, - {auth, key_sz(enacl:auth_key_size())}, - {onetimeauth, key_sz(enacl:onetime_auth_key_size())}]). +%% prop_key_sizes() -> +%% conjunction([{secret, key_sz(enacl:secretbox_key_size())}, +%% {stream, key_sz(enacl:stream_key_size())}, +%% {auth, key_sz(enacl:auth_key_size())}, +%% {onetimeauth, key_sz(enacl:onetime_auth_key_size())}]). nonce_sz(Sz) -> equals(enacl:secretbox_nonce_size(), Sz). -prop_nonce_sizes() -> - conjunction([{secret, nonce_sz(enacl:secretbox_nonce_size())}, - {stream, nonce_sz(enacl:stream_nonce_size())}]). +%% prop_nonce_sizes() -> +%% conjunction([{secret, nonce_sz(enacl:secretbox_nonce_size())}, +%% {stream, nonce_sz(enacl:stream_nonce_size())}]). secret_key_good() -> Sz = enacl:secretbox_key_size(), @@ -474,7 +488,7 @@ secret_key_bad() -> ?SUCHTHAT(B, binary(), byte_size(B) /= enacl:secretbox_key_size())]). secret_key() -> - fault(secret_key_bad(), secret_key_good()). + ?FAULT(secret_key_bad(), secret_key_good()). secret_key_valid(SK) when is_binary(SK) -> Sz = enacl:secretbox_key_size(), @@ -497,9 +511,9 @@ secretbox_open(Msg, Nonce, Key) -> prop_secretbox_correct() -> ?FORALL({Msg, Nonce, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, secret_key())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, secret_key())}, begin case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of true -> @@ -527,8 +541,8 @@ prop_secretbox_failure_integrity() -> prop_stream_correct() -> ?FORALL({Len, Nonce, Key}, {int(), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, secret_key())}, + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, secret_key())}, case Len >= 0 andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of true -> CipherStream = enacl:stream(Len, Nonce, Key), @@ -541,29 +555,29 @@ xor_bytes(<>, <>) -> [A bxor B | xor_bytes(As, Bs)]; xor_bytes(<<>>, <<>>) -> []. -prop_stream_xor_correct() -> - ?FORALL({Msg, Nonce, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, nonce()), - fault_rate(1, 40, secret_key())}, - case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of - true -> - Stream = enacl:stream(iolist_size(Msg), Nonce, Key), - CipherText = enacl:stream_xor(Msg, Nonce, Key), - StreamXor = enacl:stream_xor(CipherText, Nonce, Key), - conjunction([ - {'xor', equals(iolist_to_binary(Msg), StreamXor)}, - {stream, equals(iolist_to_binary(xor_bytes(Stream, iolist_to_binary(Msg))), CipherText)} - ]); - false -> - badargs(fun() -> enacl:stream_xor(Msg, Nonce, Key) end) - end). +%% prop_stream_xor_correct() -> +%% ?FORALL({Msg, Nonce, Key}, +%% {?FAULT_RATE(1, 40, g_iodata()), +%% ?FAULT_RATE(1, 40, nonce()), +%% ?FAULT_RATE(1, 40, secret_key())}, +%% case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of +%% true -> +%% Stream = enacl:stream(iolist_size(Msg), Nonce, Key), +%% CipherText = enacl:stream_xor(Msg, Nonce, Key), +%% StreamXor = enacl:stream_xor(CipherText, Nonce, Key), +%% conjunction([ +%% {'xor', equals(iolist_to_binary(Msg), StreamXor)}, +%% {stream, equals(iolist_to_binary(xor_bytes(Stream, iolist_to_binary(Msg))), CipherText)} +%% ]); +%% false -> +%% badargs(fun() -> enacl:stream_xor(Msg, Nonce, Key) end) +%% end). %% CRYPTO AUTH prop_auth_correct() -> ?FORALL({Msg, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, secret_key())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, secret_key())}, case v_iodata(Msg) andalso secret_key_valid(Key) of true -> Authenticator = enacl:auth(Msg, Key), @@ -588,7 +602,7 @@ authenticator_good(_Msg, _Key) -> binary(enacl:auth_size()). authenticator(Msg, Key) -> - fault(authenticator_bad(), authenticator_good(Msg, Key)). + ?FAULT(authenticator_bad(), authenticator_good(Msg, Key)). authenticator_valid({valid, _}) -> true; authenticator_valid({invalid, _}) -> true; @@ -596,8 +610,8 @@ authenticator_valid(_) -> false. prop_auth_verify_correct() -> ?FORALL({Msg, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, secret_key())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, secret_key())}, ?FORALL(Authenticator, authenticator(Msg, Key), case v_iodata(Msg) andalso secret_key_valid(Key) andalso authenticator_valid(Authenticator) of true -> @@ -614,8 +628,8 @@ prop_auth_verify_correct() -> %% CRYPTO ONETIME AUTH prop_onetimeauth_correct() -> ?FORALL({Msg, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, secret_key())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, secret_key())}, case v_iodata(Msg) andalso secret_key_valid(Key) of true -> Authenticator = enacl:onetime_auth(Msg, Key), @@ -640,7 +654,7 @@ ot_authenticator_good(_Msg, _Key) -> binary(enacl:auth_size()). ot_authenticator(Msg, Key) -> - fault(ot_authenticator_bad(), ot_authenticator_good(Msg, Key)). + ?FAULT(ot_authenticator_bad(), ot_authenticator_good(Msg, Key)). ot_authenticator_valid({valid, _}) -> true; ot_authenticator_valid({invalid, _}) -> true; @@ -648,8 +662,8 @@ ot_authenticator_valid(_) -> false. prop_onetime_auth_verify_correct() -> ?FORALL({Msg, Key}, - {fault_rate(1, 40, g_iodata()), - fault_rate(1, 40, secret_key())}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, secret_key())}, ?FORALL(Authenticator, ot_authenticator(Msg, Key), case v_iodata(Msg) andalso secret_key_valid(Key) andalso ot_authenticator_valid(Authenticator) of true -> @@ -706,7 +720,7 @@ verify_pair_good(Sz) -> ?SUCHTHAT({X, Y}, {binary(Sz), binary(Sz)}, X /= Y)]). verify_pair(Sz) -> - fault(verify_pair_bad(Sz), verify_pair_good(Sz)). + ?FAULT(verify_pair_bad(Sz), verify_pair_good(Sz)). verify_pair_valid(Sz, X, Y) -> byte_size(X) == Sz andalso byte_size(Y) == Sz. diff --git a/src/enacl.erl b/src/enacl.erl index 04a3572..b19ae55 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -106,6 +106,23 @@ kx_session_key_size/0 ]). +%% Password Hashing - Argon2 Algorithm +-export([ + pwhash/2, + pwhash_str/1, + pwhash_str_verify/2 +]). + +%% Generic hash functions +-export([ + generichash/3, + generichash/2, + + generichash_init/2, + generichash_update/2, + generichash_final/1 +]). + %% Libsodium specific functions (which are also part of the "undocumented" interface to NaCl -export([ randombytes/1 @@ -153,6 +170,13 @@ -define(CRYPTO_KX_SECRETKEYBYTES, 32). -define(CRYPTO_KX_SESSIONKEYBYTES, 32). +-define(CRYPTO_GENERICHASH_BYTES_MIN, 16). +-define(CRYPTO_GENERICHASH_BYTES_MAX, 64). +-define(CRYPTO_GENERICHASH_BYTES, 32). +-define(CRYPTO_GENERICHASH_KEYBYTES_MIN, 16). +-define(CRYPTO_GENERICHASH_KEYBYTES_MAX, 64). +-define(CRYPTO_GENERICHASH_KEYBYTES, 32). + %% @doc Verify makes sure the constants defined in libsodium matches ours verify() -> true = equals(binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()), ?P_ZEROBYTES), @@ -173,7 +197,13 @@ verify() -> {crypto_secretbox_BOXZEROBYTES, ?CRYPTO_SECRETBOX_BOXZEROBYTES}, {crypto_kx_SESSIONKEYBYTES, ?CRYPTO_KX_SESSIONKEYBYTES}, {crypto_kx_PUBLICKEYBYTES, ?CRYPTO_KX_PUBLICKEYBYTES}, - {crypto_kx_SECRETKEYBYTES, ?CRYPTO_KX_SECRETKEYBYTES} + {crypto_kx_SECRETKEYBYTES, ?CRYPTO_KX_SECRETKEYBYTES}, + {crypto_generichash_BYTES, ?CRYPTO_GENERICHASH_BYTES}, + {crypto_generichash_BYTES_MIN, ?CRYPTO_GENERICHASH_BYTES_MIN}, + {crypto_generichash_BYTES_MAX, ?CRYPTO_GENERICHASH_BYTES_MAX}, + {crypto_generichash_KEYBYTES, ?CRYPTO_GENERICHASH_KEYBYTES}, + {crypto_generichash_KEYBYTES_MIN, ?CRYPTO_GENERICHASH_KEYBYTES_MIN}, + {crypto_generichash_KEYBYTES_MAX, ?CRYPTO_GENERICHASH_KEYBYTES_MAX} ], run_verifiers(Verifiers). @@ -249,6 +279,59 @@ unsafe_memzero(X) when is_binary(X) -> unsafe_memzero(_) -> error(badarg). + +%% @doc generichash/3 creates a hash of the message using a key. +%% +%% This function generates a hash of the message using a key. The hash size is +%% either 16, 32 or 64 bytes +%% @end +-spec generichash(iodata(), binary()) -> {ok, binary()} | {error, term()}. +generichash(HashSize, Message, Key) -> + enacl_nif:crypto_generichash(HashSize, Message, Key). + +%% @doc generichash/2 creates a hash of the message. +%% +%% This function generates a hash of the message. The hash size is +%% either 16, 32 or 64 bytes +%% @end +generichash(HashSize, Message) -> + enacl_nif:crypto_generichash(HashSize, Message, <<>>). + +generichash_init(HashSize, Key) -> + enacl_nif:crypto_generichash_init(HashSize, Key). + +generichash_update({hashstate, HashSize, HashState}, Message) -> + enacl_nif:crypto_generichash_update(HashSize, HashState, Message). + +generichash_final({hashstate, HashSize, HashState}) -> + enacl_nif:crypto_generichash_final(HashSize, HashState). + + +%% @doc pwhash/2 hash a password +%% +%% This function generates a fixed size salted hash of a user defined password. +%% @end +-spec pwhash(iodata(), binary()) -> {ok, binary()} | {error, term()}. +pwhash(Password, Salt) -> + enacl_nif:crypto_pwhash(Password, Salt). + +%% @doc pwhash_str_verify/2 generates a ASCII encoded hash of a password +%% +%% This function generates a fixed size, salted, ASCII encoded hash of a user defined password. +%% @end +-spec pwhash_str(iodata()) -> {ok, iodata()} | {error, term()}. +pwhash_str(Password) -> + enacl_nif:crypto_pwhash_str(Password). + +%% @doc pwhash_str_verify/2 compares a password with a hash +%% +%% This function verifies that the hash is generated from the password. The +%% function returns true if the verifcate succeeds, false otherwise +%% @end +-spec pwhash_str_verify(binary(), iodata()) -> boolean(). +pwhash_str_verify(HashPassword, Password) -> + enacl_nif:crypto_pwhash_str_verify(HashPassword, Password). + %% Public Key Crypto %% --------------------- %% @doc box_keypair/0 creates a new Public/Secret keypair. @@ -913,6 +996,8 @@ kx_public_key_size() -> kx_secret_key_size() -> enacl_nif:crypto_kx_SECRETKEYBYTES(). + + %% Obtaining random bytes %% @doc randombytes/1 produces a stream of random bytes of the given size diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 71e7cfa..44196ba 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -122,6 +122,28 @@ sodium_memzero/1 ]). +%% Password Hashing - Argon2 Algorithm +-export([ + crypto_pwhash/2, + crypto_pwhash_str/1, + crypto_pwhash_str_verify/2 +]). + +%% Generic hash +-export([ + crypto_generichash_BYTES/0, + crypto_generichash_BYTES_MIN/0, + crypto_generichash_BYTES_MAX/0, + crypto_generichash_KEYBYTES/0, + crypto_generichash_KEYBYTES_MIN/0, + crypto_generichash_KEYBYTES_MAX/0, + + crypto_generichash/3, + crypto_generichash_init/2, + crypto_generichash_update/3, + crypto_generichash_final/2 +]). + %% Access to the RNG -export([ randombytes/1 @@ -146,6 +168,25 @@ init() -> SoName = filename:join(Dir, atom_to_list(?MODULE)), erlang:load_nif(SoName, 0). +crypto_generichash_BYTES() -> erlang:nif_error(nif_not_loaded). +crypto_generichash_BYTES_MIN() -> erlang:nif_error(nif_not_loaded). +crypto_generichash_BYTES_MAX() -> erlang:nif_error(nif_not_loaded). +crypto_generichash_KEYBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_generichash_KEYBYTES_MIN() -> erlang:nif_error(nif_not_loaded). +crypto_generichash_KEYBYTES_MAX() -> erlang:nif_error(nif_not_loaded). + +crypto_generichash(_HashSize, _Message, _Key) -> erlang:nif_error(nif_not_loaded). + +crypto_generichash_init(_HashSize, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_generichash_update(_HashSize, _HashState, _Message) -> erlang:nif_error(nif_not_loaded). +crypto_generichash_final(_HashSize, _HashState) -> erlang:nif_error(nif_not_loaded). + + + +crypto_pwhash(_Password, _Salt) -> erlang:nif_error(nif_not_loaded). +crypto_pwhash_str(_Password) -> erlang:nif_error(nif_not_loaded). +crypto_pwhash_str_verify(_HashedPassword, _Password) -> erlang:nif_error(nif_not_loaded). + crypto_box_NONCEBYTES() -> erlang:nif_error(nif_not_loaded). crypto_box_ZEROBYTES() -> erlang:nif_error(nif_not_loaded). crypto_box_BOXZEROBYTES() -> erlang:nif_error(nif_not_loaded).