
The code path was wrong in a lot of cases in sign_init/0 so even if it succeeded it would return a failure. Fixed by cleaning up the code.
481 lines
13 KiB
C
481 lines
13 KiB
C
#include <sodium.h>
|
|
|
|
#include <erl_nif.h>
|
|
|
|
#include "enacl.h"
|
|
#include "sign.h"
|
|
|
|
typedef struct enacl_sign_ctx {
|
|
ErlNifMutex *mtx;
|
|
crypto_sign_state *state; // The underlying signature state
|
|
int alive; // Is the context still valid for updates/finalization
|
|
} enacl_sign_ctx;
|
|
|
|
ErlNifResourceType *enacl_sign_ctx_rtype = NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (obj->mtx != NULL)
|
|
enif_mutex_destroy(obj->mtx);
|
|
|
|
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 = enacl_internal_error(env);
|
|
goto done;
|
|
}
|
|
obj->alive = 0;
|
|
obj->state = enif_alloc(crypto_sign_statebytes());
|
|
if (obj->state == NULL) {
|
|
goto release;
|
|
}
|
|
obj->alive = 1;
|
|
|
|
if ((obj->mtx = enif_mutex_create("enacl.sign")) == NULL) {
|
|
goto free;
|
|
}
|
|
|
|
if (0 != crypto_sign_init(obj->state)) {
|
|
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:
|
|
// This also frees the mutex via the destructor
|
|
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;
|
|
|
|
enif_mutex_lock(obj->mtx);
|
|
if (!obj->alive) {
|
|
ret = enacl_error_finalized(env);
|
|
goto done;
|
|
}
|
|
|
|
if (0 != crypto_sign_update(obj->state, data.data, data.size)) {
|
|
ret = enacl_internal_error(env); // This should never be hit
|
|
goto done;
|
|
}
|
|
|
|
ret = argv[0];
|
|
goto done;
|
|
|
|
bad_arg:
|
|
return enif_make_badarg(env);
|
|
done:
|
|
enif_mutex_unlock(obj->mtx);
|
|
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;
|
|
|
|
enif_mutex_lock(obj->mtx);
|
|
if (!obj->alive) {
|
|
ret = enacl_error_finalized(env);
|
|
goto done;
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
|
|
ret = enacl_internal_error(env);
|
|
goto done;
|
|
}
|
|
|
|
crypto_sign_final_create(obj->state, sig.data, &siglen, sk.data);
|
|
|
|
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);
|
|
cleanup:
|
|
obj->alive = 0;
|
|
sodium_memzero(obj->state, crypto_sign_statebytes());
|
|
enif_free(obj->state);
|
|
obj->state = NULL;
|
|
done:
|
|
enif_mutex_unlock(obj->mtx);
|
|
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;
|
|
|
|
enif_mutex_lock(obj->mtx);
|
|
if (!obj->alive) {
|
|
ret = enacl_error_finalized(env);
|
|
goto done;
|
|
}
|
|
|
|
if (0 == crypto_sign_final_verify(obj->state, sig.data, pk.data)) {
|
|
ret = enif_make_atom(env, "true");
|
|
} else {
|
|
ret = enif_make_atom(env, "false");
|
|
}
|
|
// 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;
|
|
done:
|
|
enif_mutex_unlock(obj->mtx);
|
|
return ret;
|
|
}
|
|
|
|
/* Ed 25519 */
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary pk, sk;
|
|
|
|
if (argc != 0) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
|
|
enif_release_binary(&pk);
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_ed25519_keypair(pk.data, sk.data);
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &pk),
|
|
enif_make_binary(env, &sk));
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_sk_to_pk(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary pk, sk;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &sk)) ||
|
|
(sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_ed25519_sk_to_pk(pk.data, sk.data);
|
|
|
|
return enif_make_binary(env, &pk);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary curve25519_pk, ed25519_pk;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_pk)) ||
|
|
(ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data);
|
|
|
|
return enif_make_binary(env, &curve25519_pk);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary curve25519_sk, ed25519_sk;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &ed25519_sk)) ||
|
|
(ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data);
|
|
|
|
return enif_make_binary(env, &curve25519_sk);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_SEEDBYTES(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_SEEDBYTES);
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_keypair(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary pk, sk;
|
|
|
|
if (argc != 0) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
|
|
enif_release_binary(&pk);
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_keypair(pk.data, sk.data);
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &pk),
|
|
enif_make_binary(env, &sk));
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_seed_keypair(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary pk, sk, seed;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &seed))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_PUBLICKEYBYTES, &pk)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
|
|
enif_release_binary(&pk);
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_seed_keypair(pk.data, sk.data, seed.data);
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &pk),
|
|
enif_make_binary(env, &sk));
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary m, sk, sm;
|
|
unsigned long long smlen;
|
|
|
|
if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!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(m.size + crypto_sign_BYTES, &sm)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);
|
|
|
|
return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_open(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary m, sm, pk;
|
|
unsigned long long mlen;
|
|
|
|
if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &sm)) ||
|
|
(!enif_inspect_binary(env, argv[1], &pk))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (pk.size != crypto_sign_PUBLICKEYBYTES) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(sm.size, &m)) {
|
|
return enacl_internal_error(env);
|
|
}
|
|
|
|
if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
|
|
ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK);
|
|
ERL_NIF_TERM ret_bin =
|
|
enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
|
|
return enif_make_tuple2(env, ret_ok, ret_bin);
|
|
} else {
|
|
enif_release_binary(&m);
|
|
return enacl_error_tuple(env, "failed_verification");
|
|
}
|
|
}
|
|
|
|
ERL_NIF_TERM enacl_crypto_sign_detached(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary m, sk, sig;
|
|
unsigned long long siglen;
|
|
|
|
if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!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 enacl_internal_error(env);
|
|
}
|
|
|
|
crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);
|
|
|
|
return enif_make_binary(env, &sig);
|
|
}
|
|
|
|
ERL_NIF_TERM
|
|
enacl_crypto_sign_verify_detached(ErlNifEnv *env, int argc,
|
|
ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary m, sig, pk;
|
|
|
|
if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &sig)) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
|
|
(!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_verify_detached(sig.data, m.data, m.size, pk.data)) {
|
|
return enif_make_atom(env, ATOM_TRUE);
|
|
} else {
|
|
return enif_make_atom(env, ATOM_FALSE);
|
|
}
|
|
}
|