Added generic hash NIF
This commit is contained in:
parent
36eedc6751
commit
ba640b0659
@ -9,6 +9,8 @@
|
||||
#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
|
||||
@ -17,15 +19,24 @@
|
||||
|
||||
//{"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) {
|
||||
return sodium_init();
|
||||
// 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, ...) */
|
||||
@ -293,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,
|
||||
@ -358,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);
|
||||
}
|
||||
@ -1101,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));
|
||||
}
|
||||
@ -1129,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));
|
||||
}
|
||||
@ -1291,6 +1313,211 @@ ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, ERL_NIF_TER
|
||||
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;
|
||||
|
||||
size_t hashSize;
|
||||
|
||||
// Validate the arguments
|
||||
if( (argc != 3) ||
|
||||
(!enif_get_uint64(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;
|
||||
|
||||
size_t hashSize;
|
||||
|
||||
// Validate the arguments
|
||||
if( (argc != 2) ||
|
||||
(!enif_get_uint64(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;
|
||||
|
||||
size_t hashSize;
|
||||
|
||||
crypto_generichash_state *state;
|
||||
|
||||
// Validate the arguments
|
||||
if( (argc != 3) ||
|
||||
(!enif_get_uint64(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;
|
||||
|
||||
size_t hashSize;
|
||||
|
||||
crypto_generichash_state *state;
|
||||
|
||||
// Validate the arguments
|
||||
if( (argc != 2) ||
|
||||
(!enif_get_uint64(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},
|
||||
@ -1395,7 +1622,19 @@ static ErlNifFunc nif_funcs[] = {
|
||||
{"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);
|
||||
|
@ -113,6 +113,16 @@
|
||||
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
|
||||
@ -160,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),
|
||||
@ -180,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).
|
||||
|
||||
@ -257,6 +280,33 @@ 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.
|
||||
|
@ -129,6 +129,21 @@
|
||||
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
|
||||
@ -153,6 +168,21 @@ 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).
|
||||
|
Loading…
x
Reference in New Issue
Block a user