263 lines
5.9 KiB
C
263 lines
5.9 KiB
C
#include "saltywitch.h"
|
|
|
|
struct saltywitch_generichash_context {
|
|
uint64_t outlen;
|
|
ErlNifMutex *mutex;
|
|
crypto_generichash_state *state;
|
|
};
|
|
|
|
static ErlNifResourceType *_generichash_context_rtype = NULL;
|
|
|
|
static void _generichash_dtor(ErlNifEnv *env, void *context)
|
|
{
|
|
struct saltywitch_generichash_context *ctx = context;
|
|
|
|
if (ctx == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (ctx->state != NULL) {
|
|
sodium_free(ctx->state);
|
|
ctx->state = NULL;
|
|
}
|
|
|
|
if (ctx->mutex != NULL) {
|
|
enif_mutex_destroy(ctx->mutex);
|
|
ctx->mutex = NULL;
|
|
}
|
|
}
|
|
|
|
int saltywitch_nif_init_generichash(ErlNifEnv *env)
|
|
{
|
|
_generichash_context_rtype = enif_open_resource_type(env,
|
|
NULL,
|
|
"generichash_context",
|
|
_generichash_dtor,
|
|
ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER,
|
|
NULL);
|
|
|
|
if (_generichash_context_rtype == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ERL_NIF_TERM saltywitch_generichash_keygen(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ERL_NIF_TERM term;
|
|
|
|
unsigned char *key = enif_make_new_binary(env, crypto_generichash_KEYBYTES, &term);
|
|
if (key == NULL) {
|
|
return saltywitch_exception(env, atom_error, atom_err_nif_alloc);
|
|
}
|
|
|
|
crypto_generichash_keygen(key);
|
|
|
|
return term;
|
|
}
|
|
|
|
ERL_NIF_TERM saltywitch_generichash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
unsigned char *hash = NULL;
|
|
ERL_NIF_TERM term;
|
|
ErlNifBinary bin;
|
|
|
|
ErlNifBinary key = {
|
|
.size = 0,
|
|
.data = NULL,
|
|
};
|
|
|
|
uint64_t len = crypto_generichash_BYTES;
|
|
|
|
if (enif_inspect_binary(env, argv[0], &bin) == false) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
switch (argc) {
|
|
case 3:
|
|
if (enif_get_uint64(env, argv[2], &len) == false) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
/* fallthrough */
|
|
case 2:
|
|
if (enif_inspect_binary(env, argv[1], &key) == false) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
}
|
|
|
|
hash = enif_make_new_binary(env, len, &term);
|
|
if (hash == NULL) {
|
|
return enif_raise_exception(env, enif_make_atom(env, "opaque"));
|
|
}
|
|
|
|
crypto_generichash(hash, len, bin.data, bin.size, key.data, key.size);
|
|
|
|
return term;
|
|
}
|
|
|
|
ERL_NIF_TERM saltywitch_generichash_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
struct saltywitch_generichash_context *ctx;
|
|
ERL_NIF_TERM term;
|
|
|
|
ERL_NIF_TERM err = atom_badarg;
|
|
ERL_NIF_TERM reason = atom_err_opaque;
|
|
|
|
ErlNifBinary key = {
|
|
.size = 0,
|
|
.data = NULL,
|
|
};
|
|
|
|
uint64_t len = crypto_generichash_BYTES;
|
|
|
|
switch (argc) {
|
|
case 2:
|
|
if (enif_get_uint64(env, argv[1], &len) == false) {
|
|
reason = atom_err_invalid_type;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (len < crypto_generichash_BYTES_MIN) {
|
|
reason = atom_err_output_too_small;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (len > crypto_generichash_BYTES_MAX) {
|
|
reason = atom_err_output_too_large;
|
|
goto badarg_FAULT;
|
|
}
|
|
/* fallthrough */
|
|
case 1:
|
|
if (enif_inspect_binary(env, argv[0], &key) == false) {
|
|
reason = atom_err_invalid_type;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (key.size < crypto_generichash_KEYBYTES_MIN) {
|
|
reason = atom_err_key_too_small;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (key.size > crypto_generichash_KEYBYTES_MAX) {
|
|
reason = atom_err_key_too_large;
|
|
goto badarg_FAULT;
|
|
}
|
|
}
|
|
|
|
ctx = enif_alloc_resource(_generichash_context_rtype, sizeof(struct saltywitch_generichash_context));
|
|
if (ctx == NULL) {
|
|
reason = atom_err_nif_alloc;
|
|
goto resource_alloc_FAULT;
|
|
}
|
|
|
|
ctx->outlen = len;
|
|
|
|
ctx->mutex = enif_mutex_create("saltywitch.generichash");
|
|
if (ctx->mutex == NULL) {
|
|
reason = atom_err_nif_alloc;
|
|
goto mutex_FAULT;
|
|
}
|
|
|
|
ctx->state = sodium_malloc(sizeof(crypto_generichash_state));
|
|
if (ctx->state == NULL) {
|
|
goto state_alloc_FAULT;
|
|
}
|
|
|
|
crypto_generichash_init(ctx->state, key.data, key.size, len);
|
|
|
|
term = enif_make_resource(env, ctx);
|
|
|
|
enif_release_resource(ctx);
|
|
|
|
return term;
|
|
|
|
state_alloc_FAULT:
|
|
enif_mutex_destroy(ctx->mutex);
|
|
mutex_FAULT:
|
|
enif_release_resource(ctx);
|
|
resource_alloc_FAULT:
|
|
err = atom_error;
|
|
badarg_FAULT:
|
|
return saltywitch_exception(env, err, reason);
|
|
}
|
|
|
|
ERL_NIF_TERM saltywitch_generichash_update(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
struct saltywitch_generichash_context *ctx = NULL;
|
|
ErlNifBinary message;
|
|
|
|
ERL_NIF_TERM err = atom_badarg;
|
|
ERL_NIF_TERM reason = atom_err_opaque;
|
|
|
|
if (enif_get_resource(env, argv[0], _generichash_context_rtype, (void **) &ctx) == false) {
|
|
reason = atom_err_invalid_type;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (ctx == NULL) {
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (ctx->mutex == NULL || ctx->state == NULL) {
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (enif_inspect_iolist_as_binary(env, argv[1], &message) == false) {
|
|
reason = atom_err_invalid_type;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
enif_mutex_lock(ctx->mutex);
|
|
crypto_generichash_update(ctx->state, message.data, message.size);
|
|
enif_mutex_unlock(ctx->mutex);
|
|
|
|
return atom_ok;
|
|
|
|
badarg_FAULT:
|
|
return saltywitch_exception(env, err, reason);
|
|
}
|
|
|
|
ERL_NIF_TERM saltywitch_generichash_final(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
struct saltywitch_generichash_context *ctx = NULL;
|
|
unsigned char *hash;
|
|
ERL_NIF_TERM term;
|
|
|
|
ERL_NIF_TERM err = atom_badarg;
|
|
ERL_NIF_TERM reason = atom_err_opaque;
|
|
|
|
if (enif_get_resource(env, argv[0], _generichash_context_rtype, (void **) &ctx) == false) {
|
|
reason = atom_err_invalid_type;
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (ctx == NULL) {
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
if (ctx->mutex == NULL || ctx->state == NULL) {
|
|
goto badarg_FAULT;
|
|
}
|
|
|
|
hash = enif_make_new_binary(env, ctx->outlen, &term);
|
|
if (hash == NULL) {
|
|
reason = atom_err_nif_alloc;
|
|
goto error_FAULT;
|
|
}
|
|
|
|
enif_mutex_lock(ctx->mutex);
|
|
crypto_generichash_final(ctx->state, hash, ctx->outlen);
|
|
sodium_free(ctx->state);
|
|
ctx->state = NULL;
|
|
ctx->outlen = 0;
|
|
enif_mutex_unlock(ctx->mutex);
|
|
|
|
return term;
|
|
|
|
error_FAULT:
|
|
err = atom_error;
|
|
badarg_FAULT:
|
|
return saltywitch_exception(env, err, reason);
|
|
}
|