Added generic hash NIF

This commit is contained in:
Venkatakumar Srinivasan 2017-05-09 12:31:20 -05:00 committed by Irina Guberman
parent 36eedc6751
commit ba640b0659
3 changed files with 327 additions and 8 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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).