Current section

Files

Jump to
hydro c_src hydro_nif.c
Raw

c_src/hydro_nif.c

#include "erl_nif.h"
#include <string.h>
#include <hydrogen.h>
#define ATOM_OK "ok"
#define ATOM_UNKNOWN "unknown"
#define ATOM_ERROR "error"
#define ATOM_TRUE "true"
#define ATOM_FALSE "false"
#define ATOM_OOM "out_of_memory"
#define ATOM_ENCRYPT_FAIL "encrypt_failed"
#define ATOM_DECRYPT_FAIL "decrypt_failed"
#define ATOM_BAD_SIZE "bad_size"
#define ATOM_BAD_SALT_SIZE "bad_salt_size"
#define ATOM_BAD_HASH_SIZE "bad_hash_size"
#define ATOM_BAD_CTX_SIZE "bad_context_size"
#define ATOM_BAD_KEY_SIZE "bad_key_size"
#define ATOM_BAD_NONCE_SIZE "bad_nonce_size"
#define MK_ATOM(env, str) enif_make_atom(env, str)
#define MK_BIN(env, bin) enif_make_binary(env, bin)
#define MK_TUPLE(env, ret1, ret2) enif_make_tuple2(env, ret1, ret2)
#define MK_RESOURCE(env, res) enif_make_resource(env, res)
#define BADARG(env) enif_make_badarg(env)
#define GET_BIN(env, term, bin) enif_inspect_binary(env, term, bin)
#define GET_RESOURCE(env, term, type, res) enif_get_resource(env, term, type, res)
#define MK_UINT(env, val, size) enif_get_uint(env, val, size)
#define ALLOC_BIN(size, pk) enif_alloc_binary(size, pk)
#define ALLOC_RESOURCE(env, type) enif_alloc_resource(env, type)
#define ERROR(env, atom_arg) enif_make_tuple2(env, MK_ATOM(env, ATOM_ERROR), MK_ATOM(env, atom_arg))
#define OOM_ERROR(env) ERROR(env, ATOM_OOM)
#define ENCRYPT_FAILED_ERROR(env) ERROR(env, ATOM_ENCRYPT_FAIL)
#define DECRYPT_FAILED_ERROR(env) ERROR(env, ATOM_DECRYPT_FAIL)
#define BAD_SALT_SIZE_ERROR(env) ERROR(env, ATOM_BAD_SALT_SIZE)
#define BAD_KEY_SIZE_ERROR(env) ERROR(env, ATOM_BAD_KEY_SIZE)
#define BAD_NONCE_SIZE_ERROR(env) ERROR(env, ATOM_BAD_NONCE_SIZE)
#define OK_TUPLE(env, ret) enif_make_tuple2(env, MK_ATOM(env, ATOM_OK), ret)
#define OK_TUPLE3(env, ret1, ret2) enif_make_tuple3(env, MK_ATOM(env, ATOM_OK), ret1, ret2)
#define RAISE(env, atom_arg) enif_raise_exception(env, MK_ATOM(env, atom_arg))
#define FREE(r) enif_free(r)
#define FREE_BIN(bin) enif_release_binary(bin)
#define FREE_RESOURCE(res) enif_release_resource(res)
#define IS_NUM(env, arg) enif_is_number(env, arg)
#define GT(arg1, arg2) arg1 > arg2
#define LT(arg1, arg2) arg1 < arg2
#define LT_OR_EQ(arg1, arg2) arg1 <= arg2
#define GT_OR_EQ(arg1, arg2) arg1 >= arg2
#define IN_RANGE(arg1, arg2, arg3) (LT_OR_EQ(arg2, arg1) && GT_OR_EQ(arg2, arg1))
#define NOT_IN_RANGE(arg1, arg2, arg3) (LT_OR_EQ(arg1, arg2) || GT_OR_EQ(arg1, arg3))
#define HASH_STATE_NAME "hydro_hash_state"
static ErlNifResourceType *hydro_hash_state_t = NULL;
static ErlNifResourceType *init_resource_type(ErlNifEnv * env)
{
return enif_open_resource_type(env, NULL, HASH_STATE_NAME, NULL,
ERL_NIF_RT_CREATE, NULL);
}
static int hydro_load(ErlNifEnv * env, void **priv_data, ERL_NIF_TERM load_info)
{
hydro_hash_state_t = init_resource_type(env);
return !hydro_hash_state_t || hydro_init() == -1 ? 1 : 0;
}
static int
hydro_upgrade(ErlNifEnv * env, void **priv, void **old_priv, ERL_NIF_TERM info)
{
return 0;
}
static void hydro_unload(ErlNifEnv * env, void *priv)
{
return;
}
static ERL_NIF_TERM hydro_error(ErlNifEnv * env, char *error_atom)
{
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, error_atom));
}
static ERL_NIF_TERM
enif_hydro_bin2hex(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary bin, hex;
if ((1 != argc)
|| (!GET_BIN(env, argv[0], &bin))) {
return BADARG(env);
}
if (!enif_alloc_binary((bin.size * 2) + 1, &hex)) {
return OOM_ERROR(env);
}
if (NULL ==
hydro_bin2hex((char *)hex.data, (bin.size * 2) + 1, bin.data,
bin.size)) {
return ENCRYPT_FAILED_ERROR(env);
}
return enif_make_binary(env, &hex);
}
static ERL_NIF_TERM
enif_hydro_random_buf(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
unsigned req_size;
ErlNifBinary buf;
if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) {
return enif_make_badarg(env);
}
if (!enif_alloc_binary(req_size, &buf)) {
return hydro_error(env, "alloc_failed");
}
hydro_random_buf(buf.data, buf.size);
return enif_make_binary(env, &buf);
}
static ERL_NIF_TERM
enif_hydro_random_buf_deterministic(ErlNifEnv * env, int argc,
ERL_NIF_TERM const argv[])
{
unsigned size;
ErlNifBinary buf, seed;
if ((argc != 2)
|| (!enif_get_uint(env, argv[0], &size))
|| (!GET_BIN(env, argv[1], &seed))) {
return enif_make_badarg(env);
}
if (!enif_alloc_binary(size, &buf)) {
return hydro_error(env, "alloc_failed");
}
hydro_random_buf_deterministic(buf.data, buf.size, seed.data);
return enif_make_binary(env, &buf);
}
static ERL_NIF_TERM
enif_hydro_random_u32(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
if (argc != 0) {
return enif_make_badarg(env);
}
uint32_t r_uint32 = hydro_random_u32();
return enif_make_uint(env, r_uint32);
}
static ERL_NIF_TERM
enif_hydro_random_uniform(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
unsigned upper_bound;
if ((argc != 1) || (!enif_get_uint(env, argv[0], &upper_bound))) {
return enif_make_badarg(env);
}
uint32_t r_uint32 = hydro_random_uniform(upper_bound);
return enif_make_uint(env, r_uint32);
}
static ERL_NIF_TERM
enif_hydro_random_ratchet(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
if ((argc != 0)) {
return enif_make_badarg(env);
}
hydro_random_ratchet();
return MK_ATOM(env, ATOM_OK);
}
static ERL_NIF_TERM
enif_hydro_hash_keygen(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary hash;
if (argc != 0) {
return enif_make_badarg(env);
}
if (!enif_alloc_binary(hydro_hash_KEYBYTES, &hash)) {
return hydro_error(env, "alloc_failed");
}
hydro_hash_keygen(hash.data);
return enif_make_binary(env, &hash);
}
static ERL_NIF_TERM
enif_hydro_hash_hash(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary h, m, c, k;
if ((3 != argc)
|| (!GET_BIN(env, argv[0], &m))
|| (!GET_BIN(env, argv[1], &c))
|| (!GET_BIN(env, argv[2], &k))) {
return BADARG(env);
}
unsigned char *key = k.data;
if (0 == k.size) {
key = NULL;
}
if (key && LT(k.size, hydro_hash_KEYBYTES)) {
return ERROR(env, ATOM_BAD_KEY_SIZE);
}
if (c.size != hydro_hash_CONTEXTBYTES) {
return ERROR(env, ATOM_BAD_CTX_SIZE);
}
if (!ALLOC_BIN(hydro_hash_BYTES, &h)) {
return OOM_ERROR(env);
}
if (0 !=
hydro_hash_hash(h.data, h.size, (const char *)m.data,
(unsigned long)m.size, (const char *)c.data, key)) {
FREE_BIN(&h);
return ENCRYPT_FAILED_ERROR(env);
}
return OK_TUPLE(env, MK_BIN(env, &h));
}
static ERL_NIF_TERM
enif_hydro_hash_init(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary c, k;
if ((2 != argc)
|| (!GET_BIN(env, argv[0], &c))
|| (!GET_BIN(env, argv[1], &k))) {
return BADARG(env);
}
unsigned char *key = (0 == k.size) ? NULL : k.data;
if (key && LT(k.size, hydro_hash_KEYBYTES)) {
return ERROR(env, ATOM_BAD_KEY_SIZE);
}
if (c.size != hydro_hash_CONTEXTBYTES) {
return ERROR(env, ATOM_BAD_CTX_SIZE);
}
hydro_hash_state *state =
(hydro_hash_state *) ALLOC_RESOURCE(hydro_hash_state_t,
sizeof(struct hydro_hash_state));
if (!state) {
return OOM_ERROR(env);
}
if (0 != hydro_hash_init(state, (const char *)c.data, key)) {
FREE_RESOURCE(state);
return ENCRYPT_FAILED_ERROR(env);
}
ERL_NIF_TERM r = MK_RESOURCE(env, state);
FREE_RESOURCE(state);
return OK_TUPLE(env, r);
}
static ERL_NIF_TERM
enif_hydro_hash_update(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary m;
hydro_hash_state *state;
if ((2 != argc)
||
(!GET_RESOURCE(env, argv[0], hydro_hash_state_t, (void **)&state))
|| (!GET_BIN(env, argv[1], &m))) {
return BADARG(env);
}
hydro_hash_state *new_state =
(hydro_hash_state *) ALLOC_RESOURCE(hydro_hash_state_t,
sizeof(struct hydro_hash_state));
memcpy(new_state->state, state->state, sizeof(*new_state));
if (0 !=
hydro_hash_update(new_state, (const char *)m.data,
(unsigned long)m.size)) {
return ENCRYPT_FAILED_ERROR(env);
}
ERL_NIF_TERM r = MK_RESOURCE(env, new_state);
FREE_RESOURCE(new_state);
return OK_TUPLE(env, r);
}
static ERL_NIF_TERM
enif_hydro_hash_final(ErlNifEnv * env, int argc, ERL_NIF_TERM const argv[])
{
ErlNifBinary h;
hydro_hash_state *state;
if ((1 != argc)
||
(!GET_RESOURCE(env, argv[0], hydro_hash_state_t, (void **)&state)))
{
return BADARG(env);
}
if (!ALLOC_BIN(hydro_hash_BYTES, &h)) {
return OOM_ERROR(env);
}
if (0 != hydro_hash_final(state, h.data, h.size)) {
FREE_BIN(&h);
return ENCRYPT_FAILED_ERROR(env);
}
ERL_NIF_TERM ret = enif_make_binary(env, &h);
return OK_TUPLE(env, ret);
}
static ErlNifFunc nif_funcs[] = {
{"hydro_bin2hex", 1,
enif_hydro_bin2hex, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_random_buf", 1,
enif_hydro_random_buf, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_random_buf_deterministic", 2,
enif_hydro_random_buf_deterministic, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_random_u32", 0,
enif_hydro_random_u32, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_random_uniform", 1,
enif_hydro_random_uniform, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_random_ratchet", 0,
enif_hydro_random_ratchet, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_hash_keygen", 0,
enif_hydro_hash_keygen, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_hash_hash", 3,
enif_hydro_hash_hash, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_hash_init", 2,
enif_hydro_hash_init, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_hash_update", 2,
enif_hydro_hash_update, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"hydro_hash_final", 1,
enif_hydro_hash_final, ERL_NIF_DIRTY_JOB_CPU_BOUND}
};
ERL_NIF_INIT(hydro_api, nif_funcs, &hydro_load, NULL, &hydro_upgrade,
&hydro_unload);