
The API for pwhash_str returns a cstring in the output buffer. These are null terminated. However, we return the full buffer as a binary back to Erlang. This means that we have a buffer with 0'es in the end. The tests take this buffer and passes it back in as is. Hence all the tests pass. However, it is conceivable that if we write said buffer to disk somewhere, we are not going to write those 0's out. When we then load the ASCII-armored Argon2 string into memory again, it is not 0-terminated as a cstring should be, and this produces errors all over the place. The fix is twofold: * Return the full buffer to Erlang, but use binary:split/2 to create a subbinary with the relevant part. * Add a 0 in the end of ASCII Argon2 string before passing it to libsodium Since we are looking at pwhashing, and Argon2, we expect the computational problem to be memory bound. Thus, spending a bit more work in memory is not going to have any considerable impact on the speed of this system.
1769 lines
53 KiB
C
1769 lines
53 KiB
C
#include "erl_nif.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <sodium.h>
|
|
|
|
#define ATOM_OK "ok"
|
|
#define ATOM_ERROR "error"
|
|
#define ATOM_TRUE "true"
|
|
#define ATOM_FALSE "false"
|
|
|
|
#define CRYPTO_GENERICHASH_STATE_RESOURCE "crypto_generichash_state"
|
|
|
|
#ifdef ERL_NIF_DIRTY_JOB_CPU_BOUND
|
|
#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c,ERL_NIF_DIRTY_JOB_CPU_BOUND}
|
|
#else
|
|
#define erl_nif_dirty_job_cpu_bound_macro(a,b,c) {a,b,c}
|
|
#endif
|
|
|
|
//{"crypto_box_keypair", 0, enif_crypto_box_keypair, ERL_NIF_DIRTY_JOB_CPU_BOUND}
|
|
/* Errors */
|
|
|
|
/* This is a global variable for resource type */
|
|
static ErlNifResourceType *generichash_state_type = NULL;
|
|
|
|
static
|
|
ERL_NIF_TERM nacl_error_tuple(ErlNifEnv *env, char *error_atom) {
|
|
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, error_atom));
|
|
}
|
|
|
|
/* Initialization */
|
|
static
|
|
int enif_crypto_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
|
|
// Create a new resource type for crypto_generichash_state
|
|
if( !(generichash_state_type = enif_open_resource_type(env, NULL, CRYPTO_GENERICHASH_STATE_RESOURCE, NULL, ERL_NIF_RT_CREATE, NULL)) ) {
|
|
return -1;
|
|
}
|
|
|
|
return sodium_init();
|
|
}
|
|
|
|
/* Low-level functions (Hashing, String Equality, ...) */
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_hash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary input;
|
|
ErlNifBinary result;
|
|
|
|
if ((argc != 1) || (!enif_inspect_iolist_as_binary(env, argv[0], &input))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_hash_BYTES, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_hash(result.data, input.data, input.size);
|
|
|
|
return enif_make_binary(env, &result);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary x,y;
|
|
|
|
if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
|
|
|| (!enif_inspect_binary(env, argv[1], &y))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (x.size != 16 || y.size != 16) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (0 == crypto_verify_16(x.data, y.data)) {
|
|
return enif_make_atom(env, "true");
|
|
} else {
|
|
return enif_make_atom(env, "false");
|
|
}
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_verify_32(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary x,y;
|
|
|
|
if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x))
|
|
|| (!enif_inspect_binary(env, argv[1], &y))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (x.size != 32 || y.size != 32) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (0 == crypto_verify_32(x.data, y.data)) {
|
|
return enif_make_atom(env, "true");
|
|
} else {
|
|
return enif_make_atom(env, "false");
|
|
}
|
|
}
|
|
|
|
/* This is very unsafe. It will not affect things that have been binary_copy()'ed
|
|
Use this for destroying key material from ram but nothing more. Be careful! */
|
|
static
|
|
ERL_NIF_TERM enif_sodium_memzero(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary x;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &x))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
sodium_memzero(x.data,x.size);
|
|
|
|
return enif_make_atom(env, "ok");
|
|
}
|
|
|
|
/* Curve 25519 */
|
|
static
|
|
ERL_NIF_TERM enif_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ERL_NIF_TERM result;
|
|
ErlNifBinary secret, basepoint, output;
|
|
uint8_t bp[crypto_scalarmult_curve25519_BYTES];
|
|
|
|
if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &secret))
|
|
|| (!enif_inspect_binary(env, argv[1], &basepoint))
|
|
|| (secret.size != crypto_scalarmult_curve25519_BYTES)
|
|
|| (basepoint.size != crypto_scalarmult_curve25519_BYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
memcpy(bp, basepoint.data, crypto_scalarmult_curve25519_BYTES);
|
|
|
|
/* Clear the high-bit. Better safe than sorry. */
|
|
bp[31] &= 0x7f;
|
|
|
|
do
|
|
{
|
|
if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
|
|
result = nacl_error_tuple(env, "alloc_failed");
|
|
continue;
|
|
}
|
|
|
|
if (crypto_scalarmult_curve25519(output.data, secret.data, bp) < 0) {
|
|
result = nacl_error_tuple(env, "scalarmult_curve25519_failed");
|
|
continue;
|
|
}
|
|
|
|
result = enif_make_binary(env, &output);
|
|
} while (0);
|
|
|
|
sodium_memzero(bp, crypto_scalarmult_curve25519_BYTES);
|
|
|
|
return result;
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_curve25519_scalarmult_base(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ERL_NIF_TERM result;
|
|
ErlNifBinary secret, output;
|
|
|
|
if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &secret))
|
|
|| (secret.size != crypto_scalarmult_curve25519_BYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
do
|
|
{
|
|
if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &output)) {
|
|
result = nacl_error_tuple(env, "alloc_failed");
|
|
continue;
|
|
}
|
|
|
|
if (crypto_scalarmult_curve25519_base(output.data, secret.data) < 0) {
|
|
result = nacl_error_tuple(env, "scalarmult_curve25519_base_failed");
|
|
continue;
|
|
}
|
|
|
|
result = enif_make_binary(env, &output);
|
|
} while (0);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Ed 25519 */
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) {
|
|
return nacl_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));
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data) != 0) {
|
|
return nacl_error_tuple(env, "ed25519_public_to_curve25519_failed");
|
|
}
|
|
|
|
return enif_make_binary(env, &curve25519_pk);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data) != 0) {
|
|
return nacl_error_tuple(env, "ed25519_secret_to_curve25519_failed");
|
|
}
|
|
|
|
return enif_make_binary(env, &curve25519_sk);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES);
|
|
}
|
|
|
|
/* Public-key cryptography */
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_NONCEBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_ZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_BOXZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_PUBLICKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_SECRETKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_BEFORENMBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_BEFORENMBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_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_box_PUBLICKEYBYTES, &pk)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_box_SECRETKEYBYTES, &sk)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_box_keypair(pk.data, sk.data);
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary padded_msg, nonce, pk, sk, result;
|
|
|
|
if (
|
|
(argc != 4) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &pk)) ||
|
|
(!enif_inspect_binary(env, argv[3], &sk))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(nonce.size != crypto_box_NONCEBYTES) ||
|
|
(pk.size != crypto_box_PUBLICKEYBYTES) ||
|
|
(sk.size != crypto_box_SECRETKEYBYTES) ||
|
|
(padded_msg.size < crypto_box_ZEROBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(padded_msg.size, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( 0 != crypto_box(result.data, padded_msg.data, padded_msg.size, nonce.data, pk.data, sk.data) ) {
|
|
return nacl_error_tuple(env, "box_error");
|
|
}
|
|
|
|
return enif_make_sub_binary(
|
|
env,
|
|
enif_make_binary(env, &result),
|
|
crypto_box_BOXZEROBYTES,
|
|
padded_msg.size - crypto_box_BOXZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary padded_ciphertext, nonce, pk, sk, result;
|
|
|
|
if (
|
|
(argc != 4) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &pk)) ||
|
|
(!enif_inspect_binary(env, argv[3], &sk))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(nonce.size != crypto_box_NONCEBYTES) ||
|
|
(pk.size != crypto_box_PUBLICKEYBYTES) ||
|
|
(sk.size != crypto_box_SECRETKEYBYTES) ||
|
|
(padded_ciphertext.size < crypto_box_BOXZEROBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(padded_ciphertext.size, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (0 != crypto_box_open(result.data, padded_ciphertext.data, padded_ciphertext.size, nonce.data, pk.data, sk.data)) {
|
|
enif_release_binary(&result);
|
|
return nacl_error_tuple(env, "failed_verification");
|
|
}
|
|
|
|
return enif_make_sub_binary(
|
|
env,
|
|
enif_make_binary(env, &result),
|
|
crypto_box_ZEROBYTES,
|
|
padded_ciphertext.size - crypto_box_ZEROBYTES);
|
|
}
|
|
|
|
/* Precomputed crypto boxes */
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary k, pk, sk;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_binary(env, argv[0], &pk)) ||
|
|
(!enif_inspect_binary(env, argv[1], &sk)) ||
|
|
(pk.size != crypto_box_PUBLICKEYBYTES) ||
|
|
(sk.size != crypto_box_SECRETKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( 0 != crypto_box_beforenm(k.data, pk.data, sk.data) ) {
|
|
// error
|
|
return nacl_error_tuple(env, "error_gen_shared_secret");
|
|
}
|
|
|
|
return enif_make_binary(env, &k);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary result, m, nonce, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k)) ||
|
|
(m.size < crypto_box_ZEROBYTES) ||
|
|
(nonce.size != crypto_box_NONCEBYTES) ||
|
|
(k.size != crypto_box_BEFORENMBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(m.size, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data);
|
|
|
|
return enif_make_sub_binary(
|
|
env,
|
|
enif_make_binary(env, &result),
|
|
crypto_box_BOXZEROBYTES,
|
|
m.size - crypto_box_BOXZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_open_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary result, m, nonce, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k)) ||
|
|
(m.size < crypto_box_BOXZEROBYTES) ||
|
|
(nonce.size != crypto_box_NONCEBYTES) ||
|
|
(k.size != crypto_box_BEFORENMBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(m.size, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data, k.data)) {
|
|
enif_release_binary(&result);
|
|
return nacl_error_tuple(env, "failed_verification");
|
|
}
|
|
|
|
return enif_make_sub_binary(
|
|
env,
|
|
enif_make_binary(env, &result),
|
|
crypto_box_ZEROBYTES,
|
|
m.size - crypto_box_ZEROBYTES);
|
|
}
|
|
|
|
/* Signing */
|
|
static
|
|
ERL_NIF_TERM enif_crypto_sign_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_PUBLICKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_sign_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_sign_SECRETKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_sign_SECRETKEYBYTES, &sk)) {
|
|
return nacl_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));
|
|
}
|
|
|
|
/*
|
|
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
|
|
const unsigned char *m, unsigned long long mlen,
|
|
const unsigned char *sk);
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_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);
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_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 nacl_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);
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_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 nacl_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);
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_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, "true");
|
|
} else {
|
|
return enif_make_atom(env, "false");
|
|
}
|
|
}
|
|
|
|
/* Sealed box functions */
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_SEALBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_box_SEALBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_seal(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary key, msg, ciphertext;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &msg)) ||
|
|
(!enif_inspect_binary(env, argv[1], &key))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(msg.size + crypto_box_SEALBYTES, &ciphertext)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_box_seal(
|
|
ciphertext.data,
|
|
msg.data,
|
|
msg.size,
|
|
key.data);
|
|
|
|
return enif_make_binary(env, &ciphertext);
|
|
}
|
|
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_box_seal_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary pk, sk, ciphertext, msg;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &ciphertext)) ||
|
|
(!enif_inspect_binary(env, argv[1], &pk)) ||
|
|
(!enif_inspect_binary(env, argv[2], &sk))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (ciphertext.size < crypto_box_SEALBYTES) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(ciphertext.size - crypto_box_SEALBYTES, &msg)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (crypto_box_seal_open(
|
|
msg.data,
|
|
ciphertext.data,
|
|
ciphertext.size,
|
|
pk.data,
|
|
sk.data) != 0) {
|
|
enif_release_binary(&msg);
|
|
return nacl_error_tuple(env, "failed_verification");
|
|
}
|
|
|
|
return enif_make_binary(env, &msg);
|
|
}
|
|
|
|
/* Secret key cryptography */
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_secretbox_NONCEBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_secretbox_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_secretbox_ZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_chacha20_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_stream_chacha20_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_chacha20_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_stream_chacha20_NONCEBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_stream_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_stream_NONCEBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_auth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_auth_BYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_auth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_auth_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_shorthash_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_shorthash_BYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_shorthash_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_shorthash_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_onetimeauth_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_onetimeauth_BYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_onetimeauth_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_onetimeauth_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary key, nonce, padded_msg, padded_ciphertext;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &key))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(key.size != crypto_secretbox_KEYBYTES) ||
|
|
(nonce.size != crypto_secretbox_NONCEBYTES) ||
|
|
(padded_msg.size < crypto_secretbox_ZEROBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_secretbox(
|
|
padded_ciphertext.data,
|
|
padded_msg.data, padded_msg.size,
|
|
nonce.data,
|
|
key.data);
|
|
|
|
return enif_make_sub_binary(env,
|
|
enif_make_binary(env, &padded_ciphertext),
|
|
crypto_secretbox_BOXZEROBYTES,
|
|
padded_msg.size - crypto_secretbox_BOXZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_secretbox_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary key, nonce, padded_ciphertext, padded_msg;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) ||
|
|
(!enif_inspect_binary(env, argv[1], &nonce)) ||
|
|
(!enif_inspect_binary(env, argv[2], &key))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(key.size != crypto_secretbox_KEYBYTES) ||
|
|
(nonce.size != crypto_secretbox_NONCEBYTES) ||
|
|
(padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (crypto_secretbox_open(
|
|
padded_msg.data,
|
|
padded_ciphertext.data,
|
|
padded_ciphertext.size,
|
|
nonce.data,
|
|
key.data) != 0) {
|
|
enif_release_binary(&padded_msg);
|
|
return nacl_error_tuple(env, "failed_verification");
|
|
}
|
|
|
|
return enif_make_sub_binary(
|
|
env,
|
|
enif_make_binary(env, &padded_msg),
|
|
crypto_secretbox_ZEROBYTES,
|
|
padded_ciphertext.size - crypto_secretbox_ZEROBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_chacha20(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary c, n, k;
|
|
ErlNifUInt64 clen;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_get_uint64(env, argv[0], &clen)) ||
|
|
(!enif_inspect_binary(env, argv[1], &n)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_stream_chacha20_KEYBYTES) ||
|
|
(n.size != crypto_stream_chacha20_NONCEBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(clen, &c)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_stream_chacha20(c.data, c.size, n.data, k.data);
|
|
|
|
return enif_make_binary(env, &c);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_chacha20_xor(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary c, m, n, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &n)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_stream_chacha20_KEYBYTES) ||
|
|
(n.size != crypto_stream_chacha20_NONCEBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(m.size, &c)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_stream_chacha20_xor(c.data, m.data, m.size, n.data, k.data);
|
|
|
|
return enif_make_binary(env, &c);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary c, n, k;
|
|
ErlNifUInt64 clen;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_get_uint64(env, argv[0], &clen)) ||
|
|
(!enif_inspect_binary(env, argv[1], &n)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_stream_KEYBYTES) ||
|
|
(n.size != crypto_stream_NONCEBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(clen, &c)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_stream(c.data, c.size, n.data, k.data);
|
|
|
|
return enif_make_binary(env, &c);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_stream_xor(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary c, m, n, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &n)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_stream_KEYBYTES) ||
|
|
(n.size != crypto_stream_NONCEBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(m.size, &c)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_stream_xor(c.data, m.data, m.size, n.data, k.data);
|
|
|
|
return enif_make_binary(env, &c);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_auth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary a,m,k;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (k.size != crypto_auth_KEYBYTES) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_auth_BYTES, &a)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_auth(a.data, m.data, m.size, k.data);
|
|
|
|
return enif_make_binary(env, &a);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_auth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary a, m, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_binary(env, argv[0], &a)) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_auth_KEYBYTES) ||
|
|
(a.size != crypto_auth_BYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (0 == crypto_auth_verify(a.data, m.data, m.size, k.data)) {
|
|
return enif_make_atom(env, "true");
|
|
} else {
|
|
return enif_make_atom(env, "false");
|
|
}
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_shorthash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary a,m,k;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (k.size != crypto_shorthash_KEYBYTES) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_shorthash_BYTES, &a)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_shorthash(a.data, m.data, m.size, k.data);
|
|
|
|
return enif_make_binary(env, &a);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_onetimeauth(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary a,m,k;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &m)) ||
|
|
(!enif_inspect_binary(env, argv[1], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (k.size != crypto_onetimeauth_KEYBYTES) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_onetimeauth_BYTES, &a)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_onetimeauth(a.data, m.data, m.size, k.data);
|
|
|
|
return enif_make_binary(env, &a);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_onetimeauth_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary a, m, k;
|
|
|
|
if (
|
|
(argc != 3) ||
|
|
(!enif_inspect_binary(env, argv[0], &a)) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[1], &m)) ||
|
|
(!enif_inspect_binary(env, argv[2], &k))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (
|
|
(k.size != crypto_onetimeauth_KEYBYTES) ||
|
|
(a.size != crypto_onetimeauth_BYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (0 == crypto_onetimeauth_verify(a.data, m.data, m.size, k.data)) {
|
|
return enif_make_atom(env, "true");
|
|
} else {
|
|
return enif_make_atom(env, "false");
|
|
}
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
|
|
{
|
|
unsigned req_size;
|
|
ErlNifBinary result;
|
|
|
|
if ((argc != 1) || (!enif_get_uint(env, argv[0], &req_size))) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(req_size, &result)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
randombytes(result.data, result.size);
|
|
|
|
return enif_make_binary(env, &result);
|
|
}
|
|
|
|
/* Key exchange */
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_kx_SECRETKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_kx_PUBLICKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_SESSIONKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_kx_SESSIONKEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_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_kx_PUBLICKEYBYTES, &pk)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_kx_SECRETKEYBYTES, &sk)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_kx_keypair(pk.data, sk.data);
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk));
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_server_session_keys(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
|
|
{
|
|
ErlNifBinary rx, tx, server_pk, server_sk, client_pk;
|
|
|
|
if ((argc != 3) ||
|
|
(!enif_inspect_binary(env, argv[0], &server_pk)) ||
|
|
(!enif_inspect_binary(env, argv[1], &server_sk)) ||
|
|
(!enif_inspect_binary(env, argv[2], &client_pk)) ||
|
|
(server_pk.size != crypto_kx_PUBLICKEYBYTES) ||
|
|
(server_sk.size != crypto_kx_SECRETKEYBYTES) ||
|
|
(client_pk.size != crypto_kx_PUBLICKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( 0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data, server_sk.data, client_pk.data) ) {
|
|
// suspicious client public key
|
|
return nacl_error_tuple(env, "invalid_client_public_key");
|
|
}
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx));
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_kx_client_session_keys(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
|
|
{
|
|
ErlNifBinary rx, tx, client_pk, client_sk, server_pk;
|
|
|
|
if ((argc != 3) ||
|
|
(!enif_inspect_binary(env, argv[0], &client_pk)) ||
|
|
(!enif_inspect_binary(env, argv[1], &client_sk)) ||
|
|
(!enif_inspect_binary(env, argv[2], &server_pk)) ||
|
|
(client_pk.size != crypto_kx_PUBLICKEYBYTES) ||
|
|
(client_sk.size != crypto_kx_SECRETKEYBYTES) ||
|
|
(server_pk.size != crypto_kx_PUBLICKEYBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( 0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data, client_sk.data, server_pk.data) ) {
|
|
// suspicious server public key
|
|
return nacl_error_tuple(env, "invalid_server_public_key");
|
|
}
|
|
|
|
return enif_make_tuple2(env, enif_make_binary(env, &rx), enif_make_binary(env, &tx));
|
|
}
|
|
|
|
/* Various other helper functions */
|
|
static
|
|
void uint64_pack(unsigned char *y, ErlNifUInt64 x)
|
|
{
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
*y++ = x; x >>= 8;
|
|
}
|
|
|
|
static
|
|
ErlNifUInt64 uint64_unpack(const unsigned char *x)
|
|
{
|
|
ErlNifUInt64 result;
|
|
|
|
result = x[7];
|
|
result <<= 8; result |= x[6];
|
|
result <<= 8; result |= x[5];
|
|
result <<= 8; result |= x[4];
|
|
result <<= 8; result |= x[3];
|
|
result <<= 8; result |= x[2];
|
|
result <<= 8; result |= x[1];
|
|
result <<= 8; result |= x[0];
|
|
return result;
|
|
}
|
|
|
|
static
|
|
int crypto_block(unsigned char *out, const unsigned char *in, const unsigned char *k)
|
|
{
|
|
ErlNifUInt64 v0 = uint64_unpack(in + 0);
|
|
ErlNifUInt64 v1 = uint64_unpack(in + 8);
|
|
ErlNifUInt64 k0 = uint64_unpack(k + 0);
|
|
ErlNifUInt64 k1 = uint64_unpack(k + 8);
|
|
ErlNifUInt64 k2 = uint64_unpack(k + 16);
|
|
ErlNifUInt64 k3 = uint64_unpack(k + 24);
|
|
ErlNifUInt64 sum = 0;
|
|
ErlNifUInt64 delta = 0x9e3779b97f4a7c15;
|
|
int i;
|
|
for (i = 0;i < 32;++i) {
|
|
sum += delta;
|
|
v0 += ((v1<<7) + k0) ^ (v1 + sum) ^ ((v1>>12) + k1);
|
|
v1 += ((v0<<16) + k2) ^ (v0 + sum) ^ ((v0>>8) + k3);
|
|
}
|
|
uint64_pack(out + 0,v0);
|
|
uint64_pack(out + 8,v1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[])
|
|
{
|
|
ErlNifBinary in, out, key;
|
|
|
|
if (
|
|
(argc != 2) ||
|
|
(!enif_inspect_binary(env, argv[0], &in)) ||
|
|
(!enif_inspect_binary(env, argv[1], &key)) ||
|
|
(in.size != 16) || (key.size != 32)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_alloc_binary(in.size, &out)) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
crypto_block(out.data, in.data, key.data);
|
|
|
|
return enif_make_binary(env, &out);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_pwhash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary h, p, s;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &p)) ||
|
|
(!enif_inspect_binary(env, argv[1], &s)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Check Salt size
|
|
if(s.size != crypto_pwhash_SALTBYTES) {
|
|
return nacl_error_tuple(env, "invalid_salt_size");
|
|
}
|
|
|
|
// Allocate memory for return binary
|
|
if( !enif_alloc_binary(crypto_box_SEEDBYTES, &h) ) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( crypto_pwhash(h.data, h.size, (char *)p.data, p.size, s.data,
|
|
crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) {
|
|
/* out of memory */
|
|
enif_release_binary(&h);
|
|
return nacl_error_tuple(env, "out_of_memory");
|
|
}
|
|
|
|
ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
|
|
ERL_NIF_TERM ret = enif_make_binary(env, &h);
|
|
|
|
return enif_make_tuple2(env, ok, ret);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_pwhash_str(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary h, p;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 1) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &p)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Allocate memory for return binary
|
|
if( !enif_alloc_binary(crypto_pwhash_STRBYTES, &h) ) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
if( crypto_pwhash_str((char *)h.data, (char *)p.data, p.size,
|
|
crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0) {
|
|
/* out of memory */
|
|
enif_release_binary(&h);
|
|
return nacl_error_tuple(env, "out_of_memory");
|
|
}
|
|
|
|
ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
|
|
ERL_NIF_TERM ret = enif_make_binary(env, &h);
|
|
|
|
return enif_make_tuple2(env, ok, ret);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary h, p;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 2) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[0], &h)) ||
|
|
(!enif_inspect_iolist_as_binary(env, argv[1], &p)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
ERL_NIF_TERM retVal = enif_make_atom(env, ATOM_TRUE);
|
|
if( crypto_pwhash_str_verify((char *)h.data, (char *)p.data, p.size) != 0) {
|
|
/* wrong password */
|
|
retVal = enif_make_atom(env, ATOM_FALSE);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/*
|
|
* AEAD ChaCha20 Poly1305
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_NPUBBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_ABYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_ABYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_encrypt(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ERL_NIF_TERM result;
|
|
ErlNifBinary key, nonce, ad, message, ciphertext;
|
|
|
|
if ((argc != 4) || (!enif_inspect_binary(env, argv[0], &key))
|
|
|| (!enif_inspect_binary(env, argv[1], &nonce))
|
|
|| (!enif_inspect_binary(env, argv[2], &ad))
|
|
|| (!enif_inspect_binary(env, argv[3], &message))
|
|
|| (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
|
|
|| (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
do
|
|
{
|
|
if (!enif_alloc_binary(message.size + crypto_aead_chacha20poly1305_ietf_ABYTES, &ciphertext)) {
|
|
result = nacl_error_tuple(env, "alloc_failed");
|
|
continue;
|
|
}
|
|
|
|
if (crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext.data, NULL, message.data, message.size,
|
|
ad.data, ad.size, NULL, nonce.data, key.data) < 0) {
|
|
result = nacl_error_tuple(env, "aead_chacha20poly1305_ietf_encrypt_failed");
|
|
continue;
|
|
}
|
|
|
|
result = enif_make_binary(env, &ciphertext);
|
|
} while (0);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_aead_chacha20poly1305_decrypt(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ERL_NIF_TERM result;
|
|
ErlNifBinary key, nonce, ad, message, ciphertext;
|
|
|
|
if ((argc != 4) || (!enif_inspect_binary(env, argv[0], &key))
|
|
|| (!enif_inspect_binary(env, argv[1], &nonce))
|
|
|| (!enif_inspect_binary(env, argv[2], &ad))
|
|
|| (!enif_inspect_binary(env, argv[3], &ciphertext))
|
|
|| (ciphertext.size < crypto_aead_chacha20poly1305_ietf_ABYTES)
|
|
|| (key.size != crypto_aead_chacha20poly1305_ietf_KEYBYTES)
|
|
|| (nonce.size != crypto_aead_chacha20poly1305_ietf_NPUBBYTES)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
do
|
|
{
|
|
if (!enif_alloc_binary(ciphertext.size - crypto_aead_chacha20poly1305_ietf_ABYTES, &message)) {
|
|
result = nacl_error_tuple(env, "alloc_failed");
|
|
continue;
|
|
}
|
|
|
|
if (crypto_aead_chacha20poly1305_ietf_decrypt(message.data, NULL, NULL, ciphertext.data, ciphertext.size,
|
|
ad.data, ad.size, nonce.data, key.data) < 0) {
|
|
result = nacl_error_tuple(env, "aead_chacha20poly1305_ietf_decrypt_failed");
|
|
continue;
|
|
}
|
|
|
|
result = enif_make_binary(env, &message);
|
|
} while (0);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/*
|
|
* Generic hash
|
|
*/
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_BYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_BYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_BYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_BYTES_MIN);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_BYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_BYTES_MAX);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_KEYBYTES);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MIN(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_KEYBYTES_MIN);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_KEYBYTES_MAX(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
return enif_make_int64(env, crypto_generichash_KEYBYTES_MAX);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary hash, message, key;
|
|
|
|
unsigned hashSize;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 3) ||
|
|
(!enif_get_uint(env, argv[0], &hashSize)) ||
|
|
(!enif_inspect_binary(env, argv[1], &message)) ||
|
|
(!enif_inspect_binary(env, argv[2], &key)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
|
|
if( (hashSize < crypto_generichash_BYTES_MIN) ||
|
|
(hashSize > crypto_generichash_BYTES_MAX) ) {
|
|
return nacl_error_tuple(env, "invalid_hash_size");
|
|
}
|
|
|
|
// validate key size
|
|
unsigned char *k = key.data;
|
|
if( 0 == key.size ) {
|
|
k = NULL;
|
|
} else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) {
|
|
return nacl_error_tuple(env, "invalid_key_size");
|
|
}
|
|
|
|
// allocate memory for hash
|
|
if( !enif_alloc_binary(hashSize, &hash) ) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
// calculate hash
|
|
if( 0 != crypto_generichash(hash.data, hash.size, message.data, message.size, k, key.size) ) {
|
|
enif_release_binary(&hash);
|
|
return nacl_error_tuple(env, "hash_error");
|
|
}
|
|
|
|
ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
|
|
ERL_NIF_TERM ret = enif_make_binary(env, &hash);
|
|
|
|
return enif_make_tuple2(env, ok, ret);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_init(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary key;
|
|
|
|
unsigned hashSize;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 2) ||
|
|
(!enif_get_uint(env, argv[0], &hashSize)) ||
|
|
(!enif_inspect_binary(env, argv[1], &key)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
|
|
if( (hashSize < crypto_generichash_BYTES_MIN) ||
|
|
(hashSize > crypto_generichash_BYTES_MAX) ) {
|
|
return nacl_error_tuple(env, "invalid_hash_size");
|
|
}
|
|
|
|
// validate key size
|
|
unsigned char *k = key.data;
|
|
if( 0 == key.size ) {
|
|
k = NULL;
|
|
} else if( key.size < crypto_generichash_KEYBYTES_MIN || key.size > crypto_generichash_KEYBYTES_MAX ) {
|
|
return nacl_error_tuple(env, "invalid_key_size");
|
|
}
|
|
|
|
// Create a resource for hash state
|
|
crypto_generichash_state *state = (crypto_generichash_state *)enif_alloc_resource(generichash_state_type, crypto_generichash_statebytes());
|
|
if( !state ) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
// Call the library function
|
|
if( 0 != crypto_generichash_init(state, k, key.size, hashSize) ) {
|
|
return nacl_error_tuple(env, "hash_init_error");
|
|
}
|
|
|
|
|
|
// Create return values
|
|
ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate");
|
|
ERL_NIF_TERM e2 = argv[0];
|
|
ERL_NIF_TERM e3 = enif_make_resource(env, state);
|
|
|
|
|
|
// release dynamically allocated memory to erlang to mange
|
|
enif_release_resource(state);
|
|
|
|
// return a tuple
|
|
return enif_make_tuple3(env, e1, e2, e3);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_update(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary message;
|
|
|
|
unsigned hashSize;
|
|
|
|
crypto_generichash_state *state;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 3) ||
|
|
(!enif_get_uint(env, argv[0], &hashSize)) ||
|
|
(!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) ||
|
|
(!enif_inspect_binary(env, argv[2], &message)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
|
|
if( (hashSize < crypto_generichash_BYTES_MIN) ||
|
|
(hashSize > crypto_generichash_BYTES_MAX) ) {
|
|
return nacl_error_tuple(env, "invalid_hash_size");
|
|
}
|
|
|
|
// Update hash state
|
|
if( 0 != crypto_generichash_update(state, message.data, message.size) ) {
|
|
return nacl_error_tuple(env, "hash_update_error");
|
|
}
|
|
|
|
|
|
// Generate return value
|
|
ERL_NIF_TERM e1 = enif_make_atom(env, "hashstate");
|
|
ERL_NIF_TERM e2 = argv[0];
|
|
ERL_NIF_TERM e3 = enif_make_resource(env, state);
|
|
|
|
// return a tuple
|
|
return enif_make_tuple3(env, e1, e2, e3);
|
|
}
|
|
|
|
static
|
|
ERL_NIF_TERM enif_crypto_generichash_final(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
|
|
ErlNifBinary hash;
|
|
|
|
unsigned hashSize;
|
|
|
|
crypto_generichash_state *state;
|
|
|
|
// Validate the arguments
|
|
if( (argc != 2) ||
|
|
(!enif_get_uint(env, argv[0], &hashSize)) ||
|
|
(!enif_get_resource(env, argv[1], generichash_state_type, (void **)&state)) ) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
// Verify that hash size is crypto_generichash_BYTES/crypto_generichash_BYTES_MIN/crypto_generichash_BYTES_MAX
|
|
if( (hashSize < crypto_generichash_BYTES_MIN) ||
|
|
(hashSize > crypto_generichash_BYTES_MAX) ) {
|
|
return nacl_error_tuple(env, "invalid_hash_size");
|
|
}
|
|
|
|
// allocate memory for hash
|
|
if( !enif_alloc_binary(hashSize, &hash) ) {
|
|
return nacl_error_tuple(env, "alloc_failed");
|
|
}
|
|
|
|
// calculate hash
|
|
if( 0 != crypto_generichash_final(state, hash.data, hash.size) ) {
|
|
enif_release_binary(&hash);
|
|
return nacl_error_tuple(env, "hash_error");
|
|
}
|
|
|
|
ERL_NIF_TERM ok = enif_make_atom(env, ATOM_OK);
|
|
ERL_NIF_TERM ret = enif_make_binary(env, &hash);
|
|
|
|
return enif_make_tuple2(env, ok, ret);
|
|
}
|
|
|
|
/* Tie the knot to the Erlang world */
|
|
static ErlNifFunc nif_funcs[] = {
|
|
{"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES},
|
|
{"crypto_box_ZEROBYTES", 0, enif_crypto_box_ZEROBYTES},
|
|
{"crypto_box_BOXZEROBYTES", 0, enif_crypto_box_BOXZEROBYTES},
|
|
{"crypto_box_PUBLICKEYBYTES", 0, enif_crypto_box_PUBLICKEYBYTES},
|
|
{"crypto_box_SECRETKEYBYTES", 0, enif_crypto_box_SECRETKEYBYTES},
|
|
{"crypto_box_BEFORENMBYTES", 0, enif_crypto_box_BEFORENMBYTES},
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_keypair", 0, enif_crypto_box_keypair),
|
|
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box", 4, enif_crypto_box),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_open", 4, enif_crypto_box_open),
|
|
|
|
{"crypto_box_beforenm", 2, enif_crypto_box_beforenm},
|
|
{"crypto_box_afternm_b", 3, enif_crypto_box_afternm},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_afternm", 3, enif_crypto_box_afternm),
|
|
{"crypto_box_open_afternm_b", 3, enif_crypto_box_open_afternm},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_open_afternm", 3, enif_crypto_box_open_afternm),
|
|
|
|
{"crypto_sign_PUBLICKEYBYTES", 0, enif_crypto_sign_PUBLICKEYBYTES},
|
|
{"crypto_sign_SECRETKEYBYTES", 0, enif_crypto_sign_SECRETKEYBYTES},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign_keypair", 0, enif_crypto_sign_keypair),
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign", 2, enif_crypto_sign),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign_open", 2, enif_crypto_sign_open),
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign_detached", 2, enif_crypto_sign_detached),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached),
|
|
|
|
{"crypto_box_SEALBYTES", 0, enif_crypto_box_SEALBYTES},
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal", 2, enif_crypto_box_seal),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_box_seal_open", 3, enif_crypto_box_seal_open),
|
|
|
|
{"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES},
|
|
{"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES},
|
|
{"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES},
|
|
{"crypto_secretbox_KEYBYTES", 0, enif_crypto_secretbox_KEYBYTES},
|
|
{"crypto_secretbox_b", 3, enif_crypto_secretbox},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox", 3, enif_crypto_secretbox),
|
|
{"crypto_secretbox_open_b", 3, enif_crypto_secretbox_open},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3, enif_crypto_secretbox_open),
|
|
|
|
{"crypto_stream_chacha20_KEYBYTES", 0, enif_crypto_stream_chacha20_KEYBYTES},
|
|
{"crypto_stream_chacha20_NONCEBYTES", 0, enif_crypto_stream_chacha20_NONCEBYTES},
|
|
{"crypto_stream_chacha20_b", 3, enif_crypto_stream_chacha20},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20", 3, enif_crypto_stream_chacha20),
|
|
{"crypto_stream_chacha20_xor_b", 3, enif_crypto_stream_chacha20_xor},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_stream_chacha20_xor", 3, enif_crypto_stream_chacha20_xor),
|
|
|
|
{"crypto_stream_KEYBYTES", 0, enif_crypto_stream_KEYBYTES},
|
|
{"crypto_stream_NONCEBYTES", 0, enif_crypto_stream_NONCEBYTES},
|
|
{"crypto_stream_b", 3, enif_crypto_stream},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_stream", 3, enif_crypto_stream),
|
|
{"crypto_stream_xor_b", 3, enif_crypto_stream_xor},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_stream_xor", 3, enif_crypto_stream_xor),
|
|
|
|
{"crypto_auth_BYTES", 0, enif_crypto_auth_BYTES},
|
|
{"crypto_auth_KEYBYTES", 0, enif_crypto_auth_KEYBYTES},
|
|
{"crypto_auth_b", 2, enif_crypto_auth},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_auth", 2, enif_crypto_auth),
|
|
{"crypto_auth_verify_b", 3, enif_crypto_auth_verify},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_auth_verify", 3, enif_crypto_auth_verify),
|
|
|
|
{"crypto_shorthash_BYTES", 0, enif_crypto_shorthash_BYTES},
|
|
{"crypto_shorthash_KEYBYTES", 0, enif_crypto_shorthash_KEYBYTES},
|
|
{"crypto_shorthash", 2, enif_crypto_shorthash},
|
|
|
|
{"crypto_onetimeauth_BYTES", 0, enif_crypto_onetimeauth_BYTES},
|
|
{"crypto_onetimeauth_KEYBYTES", 0, enif_crypto_onetimeauth_KEYBYTES},
|
|
{"crypto_onetimeauth_b", 2, enif_crypto_onetimeauth},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth", 2, enif_crypto_onetimeauth),
|
|
{"crypto_onetimeauth_verify_b", 3, enif_crypto_onetimeauth_verify},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_onetimeauth_verify", 3, enif_crypto_onetimeauth_verify),
|
|
|
|
{"crypto_hash_b", 1, enif_crypto_hash},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_hash", 1, enif_crypto_hash),
|
|
{"crypto_verify_16", 2, enif_crypto_verify_16},
|
|
{"crypto_verify_32", 2, enif_crypto_verify_32},
|
|
{"sodium_memzero", 1, enif_sodium_memzero},
|
|
|
|
{"crypto_pwhash", 2, enif_crypto_pwhash},
|
|
{"crypto_pwhash_str", 1, enif_crypto_pwhash_str},
|
|
{"crypto_pwhash_str_verify", 2, enif_crypto_pwhash_str_verify},
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult_base", 1, enif_crypto_curve25519_scalarmult_base),
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair),
|
|
{"crypto_sign_ed25519_public_to_curve25519", 1, enif_crypto_sign_ed25519_public_to_curve25519},
|
|
{"crypto_sign_ed25519_secret_to_curve25519", 1, enif_crypto_sign_ed25519_secret_to_curve25519},
|
|
{"crypto_sign_ed25519_PUBLICKEYBYTES", 0, enif_crypto_sign_ed25519_PUBLICKEYBYTES},
|
|
{"crypto_sign_ed25519_SECRETKEYBYTES", 0, enif_crypto_sign_ed25519_SECRETKEYBYTES},
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("randombytes", 1, enif_randombytes),
|
|
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_kx_keypair", 0, enif_crypto_kx_keypair),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_kx_client_session_keys", 3, enif_crypto_kx_client_session_keys),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_kx_server_session_keys", 3, enif_crypto_kx_server_session_keys),
|
|
{"crypto_kx_PUBLICKEYBYTES", 0, enif_crypto_kx_PUBLICKEYBYTES},
|
|
{"crypto_kx_SECRETKEYBYTES", 0, enif_crypto_kx_SECRETKEYBYTES},
|
|
{"crypto_kx_SESSIONKEYBYTES", 0, enif_crypto_kx_SESSIONKEYBYTES},
|
|
|
|
{"scramble_block_16", 2, enif_scramble_block_16},
|
|
|
|
{"crypto_aead_chacha20poly1305_KEYBYTES", 0, enif_crypto_aead_chacha20poly1305_KEYBYTES},
|
|
{"crypto_aead_chacha20poly1305_NPUBBYTES", 0, enif_crypto_aead_chacha20poly1305_NPUBBYTES},
|
|
{"crypto_aead_chacha20poly1305_ABYTES", 0, enif_crypto_aead_chacha20poly1305_ABYTES},
|
|
{"crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX", 0, enif_crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX},
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_aead_chacha20poly1305_encrypt", 4, enif_crypto_aead_chacha20poly1305_encrypt),
|
|
erl_nif_dirty_job_cpu_bound_macro("crypto_aead_chacha20poly1305_decrypt", 4, enif_crypto_aead_chacha20poly1305_decrypt),
|
|
|
|
{"crypto_generichash_BYTES", 0, enif_crypto_generichash_BYTES},
|
|
{"crypto_generichash_BYTES_MIN", 0, enif_crypto_generichash_BYTES_MIN},
|
|
{"crypto_generichash_BYTES_MAX", 0, enif_crypto_generichash_BYTES_MAX},
|
|
{"crypto_generichash_KEYBYTES", 0, enif_crypto_generichash_KEYBYTES},
|
|
{"crypto_generichash_KEYBYTES_MIN", 0, enif_crypto_generichash_KEYBYTES_MIN},
|
|
{"crypto_generichash_KEYBYTES_MAX", 0, enif_crypto_generichash_KEYBYTES_MAX},
|
|
{"crypto_generichash", 3, enif_crypto_generichash},
|
|
{"crypto_generichash_init", 2, enif_crypto_generichash_init},
|
|
{"crypto_generichash_update", 3, enif_crypto_generichash_update},
|
|
{"crypto_generichash_final", 2, enif_crypto_generichash_final}
|
|
|
|
};
|
|
|
|
ERL_NIF_INIT(enacl_nif, nif_funcs, enif_crypto_load, NULL, NULL, NULL);
|