From f5b8a8eb3b78a97c0372b6143af6ca6d6be1f0d5 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Sun, 19 Jan 2020 17:38:42 +0100 Subject: [PATCH] Pull signing out to its own module --- CHANGELOG.md | 3 + c_src/enacl_nif.c | 155 ++------------------------------- c_src/sign.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++ c_src/sign.h | 19 ++++ 4 files changed, 245 insertions(+), 148 deletions(-) create mode 100644 c_src/sign.c create mode 100644 c_src/sign.h diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b5343..4a42ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. resource rather than on the Erlang side. This avoids some checks in the code, and streamlines a good deal of the interface. - Split AEAD routines off from the main enacl_nif.c file +- Renamed many routines from enif_* to enacl_*. This better reflects where they live + in the code base, and avoids pollution of the enif_* "namespace". +- Split Sign Public Key routines from the rest. Modernize the handling of contexts. ### Fixes - Fix a resource leak in generichash/sign init/update/final. diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index b1610c1..1dae63f 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -7,6 +7,7 @@ #include "enacl.h" #include "generichash.h" #include "hash.h" +#include "sign.h" #define CRYPTO_SIGN_STATE_RESOURCE "crypto_sign_state" @@ -18,13 +19,6 @@ { a, b, c } #endif -//{"crypto_box_keypair", 0, enif_crypto_box_keypair, -// ERL_NIF_DIRTY_JOB_CPU_BOUND} -/* Errors */ - -/* These are global variables for resource types */ -static ErlNifResourceType *sign_state_type = NULL; - /* Initialization */ static int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { @@ -32,10 +26,8 @@ static int enif_crypto_load(ErlNifEnv *env, void **priv_data, if (!enacl_init_generic_hash_ctx(env)) { return -1; } - // Create a new resource type for crypto_sign_state - if (!(sign_state_type = - enif_open_resource_type(env, NULL, CRYPTO_SIGN_STATE_RESOURCE, NULL, - ERL_NIF_RT_CREATE, NULL))) { + + if (!enacl_init_sign_ctx(env)) { return -1; } @@ -639,139 +631,6 @@ enif_crypto_sign_verify_detached(ErlNifEnv *env, int argc, } } -/* - int crypto_sign_init(crypto_sign_state *state) - */ - -static ERL_NIF_TERM enif_crypto_sign_init(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - if ((argc != 0)) { - return enif_make_badarg(env); - } - - void *state = enif_alloc_resource(sign_state_type, crypto_sign_statebytes()); - - if (!state) { - return nacl_error_tuple(env, "alloc_failed"); - } - - if (0 != crypto_sign_init(state)) { - enif_release_resource(state); - return nacl_error_tuple(env, "sign_init_error"); - } - - // Create return values - ERL_NIF_TERM e1 = enif_make_atom(env, "signstate"); - ERL_NIF_TERM e2 = enif_make_resource(env, state); - - // release dynamically allocated memory to erlang to mange - enif_release_resource(state); - - // return a tuple - return enif_make_tuple2(env, e1, e2); -} - -/* - int crypto_sign_update(crypto_sign_state *state, - const unsigned char *m, - unsigned long long mlen); - */ - -static ERL_NIF_TERM enif_crypto_sign_update(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - ErlNifBinary data; - - void *state; - - // Validate the arguments - if ((argc != 2) || - (!enif_get_resource(env, argv[0], sign_state_type, (void **)&state)) || - (!enif_inspect_binary(env, argv[1], &data))) { - return enif_make_badarg(env); - } - - if (0 != crypto_sign_update(state, data.data, data.size)) { - return nacl_error_tuple(env, "sign_update_error"); - } - - // Generate return value - ERL_NIF_TERM e1 = enif_make_atom(env, "signstate"); - ERL_NIF_TERM e2 = enif_make_resource(env, state); - - // return a tuple - return enif_make_tuple2(env, e1, e2); -} - -/* - int crypto_sign_final_create(crypto_sign_state *state, - unsigned char *sig, - unsigned long long *siglen_p, - const unsigned char *sk); - */ - -static ERL_NIF_TERM enif_crypto_sign_final_create(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - ErlNifBinary sk, sig; - - void *state; - - unsigned long long siglen; - - if ((argc != 2) || - (!enif_get_resource(env, argv[0], sign_state_type, (void **)&state)) || - (!enif_inspect_binary(env, argv[1], &sk))) { - return enif_make_badarg(env); - } - - if (sk.size != crypto_sign_SECRETKEYBYTES) { - return enif_make_badarg(env); - } - - if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) { - return nacl_error_tuple(env, "alloc_failed"); - } - - if (0 != crypto_sign_final_create(state, sig.data, &siglen, sk.data)) { - enif_release_binary(&sig); - return nacl_error_tuple(env, "sign_error"); - } - - ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); - ERL_NIF_TERM ret = enif_make_binary(env, &sig); - - return enif_make_tuple2(env, ok, ret); -} - -/* - int crypto_sign_final_verify(crypto_sign_state *state, - unsigned char *sig, - const unsigned char *pk); - */ - -static ERL_NIF_TERM enif_crypto_sign_final_verify(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - ErlNifBinary pk, sig; - - void *state; - - if ((argc != 3) || - (!enif_get_resource(env, argv[0], sign_state_type, (void **)&state)) || - (!enif_inspect_binary(env, argv[1], &sig)) || - (!enif_inspect_binary(env, argv[2], &pk))) { - return enif_make_badarg(env); - } - - if (pk.size != crypto_sign_PUBLICKEYBYTES) { - return enif_make_badarg(env); - } - - if (0 == crypto_sign_final_verify(state, sig.data, pk.data)) { - return enif_make_atom(env, ATOM_OK); - } - - return nacl_error_tuple(env, "failed_verification"); -} - /* Sealed box functions */ static ERL_NIF_TERM enif_crypto_box_SEALBYTES(ErlNifEnv *env, int argc, @@ -1584,12 +1443,12 @@ static ErlNifFunc nif_funcs[] = { enif_crypto_sign_detached), erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached), - {"crypto_sign_init", 0, enif_crypto_sign_init}, - {"crypto_sign_update", 2, enif_crypto_sign_update}, + {"crypto_sign_init", 0, enacl_crypto_sign_init}, + {"crypto_sign_update", 2, enacl_crypto_sign_update}, erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_create", 2, - enif_crypto_sign_final_create), + enacl_crypto_sign_final_create), erl_nif_dirty_job_cpu_bound_macro("crypto_sign_final_verify", 3, - enif_crypto_sign_final_verify), + enacl_crypto_sign_final_verify), {"crypto_sign_ed25519_sk_to_pk", 1, enif_crypto_sign_ed25519_sk_to_pk}, diff --git a/c_src/sign.c b/c_src/sign.c new file mode 100644 index 0000000..bb82f65 --- /dev/null +++ b/c_src/sign.c @@ -0,0 +1,216 @@ +#include "erl_nif.h" + +#include + +#include "enacl.h" +#include "sign.h" + +typedef struct enacl_sign_ctx { + crypto_sign_state *state; // The underlying signature state + int alive; // Is the context still valid for updates/finalization +} enacl_sign_ctx; + +static ErlNifResourceType *enacl_sign_ctx_rtype = NULL; + +static void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *); + +int enacl_init_sign_ctx(ErlNifEnv *env) { + enacl_sign_ctx_rtype = + enif_open_resource_type(env, NULL, "enacl_sign_context", + (ErlNifResourceDtor *)enacl_sign_ctx_dtor, + ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL); + + if (enacl_sign_ctx_rtype == NULL) + return 0; + + return 1; +} + +static void enacl_sign_ctx_dtor(ErlNifEnv *env, enacl_sign_ctx *obj) { + if (!obj->alive) + return; + + if (obj->state) { + sodium_memzero(obj->state, crypto_sign_statebytes()); + enif_free(obj->state); + } + + return; +} + +/* + int crypto_sign_init(crypto_sign_state *state) + */ + +ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ERL_NIF_TERM ret; + enacl_sign_ctx *obj = NULL; + + if (argc != 0) + goto bad_arg; + + if ((obj = enif_alloc_resource(enacl_sign_ctx_rtype, + sizeof(enacl_sign_ctx))) == NULL) { + ret = nacl_error_tuple(env, "alloc_failed"); + goto done; + } + obj->alive = 0; + obj->state = enif_alloc(crypto_sign_statebytes()); + if (obj->state == NULL) { + ret = nacl_error_tuple(env, "state_malloc"); + goto release; + } + obj->alive = 1; + + if (0 != crypto_sign_init(obj->state)) { + ret = nacl_error_tuple(env, "sign_init_error"); + goto free; + } + + // Create return values + ret = enif_make_resource(env, obj); + + goto release; + +bad_arg: + return enif_make_badarg(env); +free: + if (obj->alive) + if (obj->state != NULL) { + sodium_memzero(obj->state, crypto_sign_statebytes()); + enif_free(obj->state); + obj->state = NULL; + } +release: + enif_release_resource(obj); +done: + return ret; +} + +/* + int crypto_sign_update(crypto_sign_state *state, + const unsigned char *m, + unsigned long long mlen); + */ + +ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ERL_NIF_TERM ret; + enacl_sign_ctx *obj = NULL; + ErlNifBinary data; + + // Validate the arguments + if (argc != 2) + goto bad_arg; + + if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj)) + goto bad_arg; + + if (!enif_inspect_binary(env, argv[1], &data)) + goto bad_arg; + + if (!obj->alive) { + ret = nacl_error_tuple(env, "finalized"); + goto done; + } + + if (0 != crypto_sign_update(obj->state, data.data, data.size)) { + ret = nacl_error_tuple(env, "sign_update_error"); + goto done; + } + + ret = argv[0]; + goto done; + +bad_arg: + return enif_make_badarg(env); +done: + return ret; +} + +ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ERL_NIF_TERM ret; + enacl_sign_ctx *obj = NULL; + ErlNifBinary sk, sig; + unsigned long long siglen; + + if (argc != 2) + goto bad_arg; + if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &sk)) + goto bad_arg; + if (sk.size != crypto_sign_SECRETKEYBYTES) + goto bad_arg; + + if (!obj->alive) { + ret = nacl_error_tuple(env, "finalized"); + goto done; + } + + if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) { + ret = nacl_error_tuple(env, "alloc_failed"); + goto done; + } + + if (0 != crypto_sign_final_create(obj->state, sig.data, &siglen, sk.data)) { + ret = nacl_error_tuple(env, "sign_error"); + goto release; + } + + ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM signature = enif_make_binary(env, &sig); + + ret = enif_make_tuple2(env, ok, signature); + goto cleanup; +bad_arg: + return enif_make_badarg(env); +release: + enif_release_binary(&sig); +cleanup: + obj->alive = 0; + sodium_memzero(obj->state, crypto_sign_statebytes()); + enif_free(obj->state); + obj->state = NULL; +done: + return ret; +} + +ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ErlNifBinary pk, sig; + enacl_sign_ctx *obj = NULL; + ERL_NIF_TERM ret; + + if (argc != 3) + goto bad_arg; + if (!enif_get_resource(env, argv[0], enacl_sign_ctx_rtype, (void **)&obj)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[1], &sig)) + goto bad_arg; + if (!enif_inspect_binary(env, argv[2], &pk)) + goto bad_arg; + if (pk.size != crypto_sign_PUBLICKEYBYTES) + goto bad_arg; + + if (0 == crypto_sign_final_verify(obj->state, sig.data, pk.data)) { + ret = enif_make_atom(env, ATOM_OK); + } else { + ret = nacl_error_tuple(env, "failed_verification"); + } + // Mark as done + goto cleanup; + +bad_arg: + return enif_make_badarg(env); +cleanup: + // Get rid of the context and mark it as dead + obj->alive = 0; + sodium_memzero(obj->state, crypto_sign_statebytes()); + enif_free(obj->state); + obj->state = NULL; + + return ret; +} diff --git a/c_src/sign.h b/c_src/sign.h new file mode 100644 index 0000000..5b91ce3 --- /dev/null +++ b/c_src/sign.h @@ -0,0 +1,19 @@ +#ifndef ENACL_SIGN_H +#define ENACL_SIGN_H + +#include "erl_nif.h" + +int enacl_init_sign_ctx(ErlNifEnv *env); + +ERL_NIF_TERM enacl_crypto_sign_init(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_sign_update(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_sign_final_create(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_sign_final_verify(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +#endif \ No newline at end of file