enacl/c_src/sign.c
Jesper Louis Andersen 4939f7bb23 Protect the signature ctx with a mutex
This is the same game as with the
generichash construction. We want
to protect it with a mutex so
different processes can safely do
work on the same resource.

While here, also move the _update
function onto the dirty scheduler.
It is by far the most expensive
operation, and why it wasn't there
in the first place is odd. This should
unblock the scheduler on long
sign-checks. It also move the
possible mutex block onto the
dirty scheduler thread, away from
the core schedulers, improving
latency in the system as a result.
2020-01-24 15:18:04 +01:00

518 lines
14 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_error_tuple(env, "alloc_failed");
goto done;
}
obj->alive = 0;
obj->state = enif_alloc(crypto_sign_statebytes());
if (obj->state == NULL) {
ret = enacl_error_tuple(env, "state_malloc");
goto release;
}
obj->alive = 1;
if ((obj->mtx = enif_mutex_create("enacl.sign")) == NULL) {
ret = enacl_error_tuple(env, "mutex_create");
goto free;
}
if (0 != crypto_sign_init(obj->state)) {
ret = enacl_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:
// 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_tuple(env, "finalized");
goto done;
}
if (0 != crypto_sign_update(obj->state, data.data, data.size)) {
ret = enacl_error_tuple(env, "sign_update_error");
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_tuple(env, "finalized");
goto done;
}
if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) {
ret = enacl_error_tuple(env, "alloc_failed");
goto done;
}
if (0 != crypto_sign_final_create(obj->state, sig.data, &siglen, sk.data)) {
ret = enacl_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:
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_tuple(env, "finalized");
goto done;
}
if (0 == crypto_sign_final_verify(obj->state, sig.data, pk.data)) {
ret = enif_make_atom(env, ATOM_OK);
} else {
ret = enacl_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;
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_error_tuple(env, "alloc_failed");
}
if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
enif_release_binary(&pk);
return enacl_error_tuple(env, "alloc_failed");
}
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_error_tuple(env, "alloc_failed");
}
if (crypto_sign_ed25519_sk_to_pk(pk.data, sk.data) != 0) {
enif_release_binary(&pk);
return enacl_error_tuple(env, "crypto_sign_ed25519_sk_to_pk_failed");
}
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_error_tuple(env, "alloc_failed");
}
if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data,
ed25519_pk.data) != 0) {
enif_release_binary(&curve25519_pk);
return enacl_error_tuple(env, "ed25519_public_to_curve25519_failed");
}
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_error_tuple(env, "alloc_failed");
}
if (crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data,
ed25519_sk.data) != 0) {
enif_release_binary(&curve25519_sk);
return enacl_error_tuple(env, "ed25519_secret_to_curve25519_failed");
}
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_error_tuple(env, "alloc_failed");
}
if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
enif_release_binary(&pk);
return enacl_error_tuple(env, "alloc_failed");
}
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_error_tuple(env, "alloc_failed");
}
if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
enif_release_binary(&pk);
return enacl_error_tuple(env, "alloc_failed");
}
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));
}
/*
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *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_error_tuple(env, "alloc_failed");
}
crypto_sign(sm.data, &smlen, m.data, m.size, sk.data);
return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen);
}
/*
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
const unsigned char *sm, unsigned long long smlen,
const unsigned char *pk);
*/
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_error_tuple(env, "alloc_failed");
}
if (0 == crypto_sign_open(m.data, &mlen, sm.data, sm.size, pk.data)) {
return enif_make_sub_binary(env, enif_make_binary(env, &m), 0, mlen);
} else {
enif_release_binary(&m);
return enacl_error_tuple(env, "failed_verification");
}
}
/*
int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *sk);
*/
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_error_tuple(env, "alloc_failed");
}
crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data);
return enif_make_binary(env, &sig);
}
/*
int crypto_sign_verify_detached(const unsigned char *sig,
const unsigned char *m,
unsigned long long mlen,
const unsigned char *pk);
*/
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);
}
}