diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index acf1ba6..2333c18 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -14,6 +14,7 @@ #include "randombytes.h" #include "secret.h" #include "sign.h" +#include "secretstream.h" #ifdef ERL_NIF_DIRTY_JOB_CPU_BOUND #define erl_nif_dirty_job_cpu_bound_macro(a, b, c) \ @@ -35,6 +36,10 @@ static int enacl_crypto_load(ErlNifEnv *env, void **priv_data, return -1; } + if (!enacl_init_secretstream_ctx(env)) { + return -1; + } + return sodium_init(); } @@ -372,8 +377,24 @@ static ErlNifFunc nif_funcs[] = { erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_update", 2, enacl_crypto_generichash_update), erl_nif_dirty_job_cpu_bound_macro("crypto_generichash_final", 1, - enacl_crypto_generichash_final) + enacl_crypto_generichash_final), + {"crypto_secretstream_xchacha20poly1305_ABYTES", 0, enacl_crypto_secretstream_xchacha20poly1305_ABYTES}, + {"crypto_secretstream_xchacha20poly1305_HEADERBYTES", 0, enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES}, + {"crypto_secretstream_xchacha20poly1305_KEYBYTES", 0, enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES}, + {"crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", 0, enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX}, + {"crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", 0, enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE}, + {"crypto_secretstream_xchacha20poly1305_TAG_PUSH", 0, enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH}, + {"crypto_secretstream_xchacha20poly1305_TAG_REKEY", 0, enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY}, + {"crypto_secretstream_xchacha20poly1305_TAG_FINAL", 0, enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL}, + {"crypto_secretstream_xchacha20poly1305_keygen", 0, enacl_crypto_secretstream_xchacha20poly1305_keygen}, + {"crypto_secretstream_xchacha20poly1305_init_push", 1, enacl_crypto_secretstream_xchacha20poly1305_init_push}, + {"crypto_secretstream_xchacha20poly1305_init_pull", 2, enacl_crypto_secretstream_xchacha20poly1305_init_pull}, + {"crypto_secretstream_xchacha20poly1305_rekey", 1, enacl_crypto_secretstream_xchacha20poly1305_rekey}, + erl_nif_dirty_job_cpu_bound_macro("crypto_secretstream_xchacha20poly1305_push", 4, + enacl_crypto_secretstream_xchacha20poly1305_push), + erl_nif_dirty_job_cpu_bound_macro("crypto_secretstream_xchacha20poly1305_pull", 3, + enacl_crypto_secretstream_xchacha20poly1305_pull) }; ERL_NIF_INIT(enacl_nif, nif_funcs, enacl_crypto_load, NULL, NULL, NULL); diff --git a/c_src/secretstream.c b/c_src/secretstream.c new file mode 100644 index 0000000..c29ec1b --- /dev/null +++ b/c_src/secretstream.c @@ -0,0 +1,461 @@ +#include +#include + +#include "enacl.h" +#include "secretstream.h" + +typedef struct enacl_secretstream_ctx { + ErlNifMutex *mtx; + crypto_secretstream_xchacha20poly1305_state *state; // The underlying secretstream state + int alive; // Is the context still valid for updates/finalization +} enacl_secretstream_ctx; + +ErlNifResourceType *enacl_secretstream_ctx_rtype = NULL; + +static void enacl_secretstream_ctx_dtor(ErlNifEnv *env, + enacl_secretstream_ctx *); + +int enacl_init_secretstream_ctx(ErlNifEnv *env) { + enacl_secretstream_ctx_rtype = + enif_open_resource_type(env, NULL, "enacl_secretstream_context", + (ErlNifResourceDtor *)enacl_secretstream_ctx_dtor, + ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, NULL); + + if (enacl_secretstream_ctx_rtype == NULL) + return 0; + + return 1; +} + +static void enacl_secretstream_ctx_dtor(ErlNifEnv *env, + enacl_secretstream_ctx *obj) { + if (!obj->alive) { + return; + } + + if (obj->state) + sodium_memzero(obj->state, crypto_secretstream_xchacha20poly1305_statebytes()); + enif_free(obj->state); + + if (obj->mtx != NULL) + enif_mutex_destroy(obj->mtx); + + return; +} + +/* + * Secretstream + */ + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_ABYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_ABYTES); + +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_HEADERBYTES); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_KEYBYTES); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_PUSH); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_REKEY); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + + return enif_make_int64(env, crypto_secretstream_xchacha20poly1305_TAG_FINAL); +} + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_keygen( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + + ErlNifBinary key; + + if (argc != 0) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_KEYBYTES, &key)) { + return enacl_internal_error(env); + } + + crypto_secretstream_xchacha20poly1305_keygen(key.data); + + return enif_make_binary(env, &key); +} + +/* + int crypto_secretstream_xchacha20poly1305_init_push + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char out[crypto_secretstream_xchacha20poly1305_HEADERBYTES], + const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES]) +*/ +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ) { + ERL_NIF_TERM ret; + ErlNifBinary key, header; + enacl_secretstream_ctx *obj = NULL; + + if ((argc != 1) || (!enif_inspect_binary(env, argv[0], &key))) { + goto bad_arg; + } + + if (key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) { + goto bad_arg; + } + + if (!enif_alloc_binary(crypto_secretstream_xchacha20poly1305_HEADERBYTES, &header)) { + ret = enacl_internal_error(env); + goto done; + } + + if((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype, + sizeof(enacl_secretstream_ctx))) == NULL) { + ret = enacl_internal_error(env); + goto release_header; + } + obj->alive = 0; + obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes()); + + if (obj->state == NULL) { + goto release; + } + obj->alive = 1; + + if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) { + goto free; + } + + crypto_secretstream_xchacha20poly1305_init_push(obj->state, header.data, key.data); + + ret = enif_make_tuple2(env, + enif_make_binary(env, &header), + 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_secretstream_xchacha20poly1305_statebytes()); + enif_free(obj->state); + obj->state = NULL; + } +release: + // This also frees the mutex via the destructor + enif_release_resource(obj); +release_header: + enif_release_binary(&header); +done: + return ret; +} + +/* +crypto_secretstream_xchacha20poly1305_init_pull + (crypto_secretstream_xchacha20poly1305_state *state, + const unsigned char in[crypto_secretstream_xchacha20poly1305_HEADERBYTES], + const unsigned char k[crypto_secretstream_xchacha20poly1305_KEYBYTES]) +*/ +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ) { + ERL_NIF_TERM ret; + ErlNifBinary header, key; + enacl_secretstream_ctx *obj = NULL; + + if (argc != 2) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[0], &header)) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[1], &key)) { + goto bad_arg; + } + + if ((key.size != crypto_secretstream_xchacha20poly1305_KEYBYTES) || + (header.size != crypto_secretstream_xchacha20poly1305_HEADERBYTES)) + { + goto bad_arg; + } + + if((obj = enif_alloc_resource(enacl_secretstream_ctx_rtype, + sizeof(enacl_secretstream_ctx))) == NULL) { + ret = enacl_internal_error(env); + goto done; + } + + obj->alive = 0; + obj->state = enif_alloc(crypto_secretstream_xchacha20poly1305_statebytes()); + + if (obj->state == NULL) { + goto release; + } + obj->alive = 1; + + if ((obj->mtx = enif_mutex_create("enacl.secretstream")) == NULL) { + goto free; + } + + crypto_secretstream_xchacha20poly1305_init_pull(obj->state, header.data, key.data); + + 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_secretstream_xchacha20poly1305_statebytes()); + enif_free(obj->state); + obj->state = NULL; + } +release: + // This also frees the mutex via the destructor + enif_release_resource(obj); +done: + return ret; +} + +/* +void +crypto_secretstream_xchacha20poly1305_rekey + (crypto_secretstream_xchacha20poly1305_state *state) +*/ +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_rekey( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ) { + ERL_NIF_TERM ret; + enacl_secretstream_ctx *obj = NULL; + + if (argc != 1) { + goto bad_arg; + } + + if(!enif_get_resource(env, argv[0], + (ErlNifResourceType *) enacl_secretstream_ctx_rtype, + (void **)&obj)) { + goto bad_arg; + } + + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + goto err; + } + + crypto_secretstream_xchacha20poly1305_rekey(obj->state); + + ret = enif_make_atom(env, ATOM_OK); + + goto done; + +bad_arg: + return enif_make_badarg(env); +err: + ret = enacl_error_finalized(env); +done: + enif_mutex_unlock(obj->mtx); + return ret; +} + +/* +int +crypto_secretstream_xchacha20poly1305_push + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char *out, unsigned long long *outlen_p, + const unsigned char *m, unsigned long long mlen, + const unsigned char *ad, unsigned long long adlen, unsigned char tag) +*/ +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_push( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ) { + ERL_NIF_TERM ret; + ErlNifBinary m, ad, out; + ErlNifUInt64 tag; + enacl_secretstream_ctx *obj = NULL; + + if (argc != 4) { + goto bad_arg; + } + + if(!enif_get_resource(env, argv[0], + (ErlNifResourceType *) enacl_secretstream_ctx_rtype, + (void **)&obj)) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[1], &m)) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[2], &ad)) { + goto bad_arg; + } + + if(!enif_get_uint64(env, argv[3], &tag)) { + goto bad_arg; + } + + if (!enif_alloc_binary(m.size + crypto_secretstream_xchacha20poly1305_ABYTES, &out)) { + return enacl_internal_error(env); + } + + enif_mutex_lock(obj->mtx); + if (!obj->alive) { + goto err; + } + + crypto_secretstream_xchacha20poly1305_push(obj->state, out.data, NULL, m.data, m.size, ad.data, ad.size, tag); + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) { + if(obj->state) { + obj->alive = 0; + sodium_memzero(obj->state, crypto_secretstream_xchacha20poly1305_statebytes()); + enif_free(obj->state); + obj->state = NULL; + } + } + + ret = enif_make_binary(env, &out); + + goto done; + +bad_arg: + return enif_make_badarg(env); +err: + ret = enacl_error_finalized(env); + enif_release_binary(&out); +done: + enif_mutex_unlock(obj->mtx); + return ret; +} + +/* + crypto_secretstream_xchacha20poly1305_pull + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p, + const unsigned char *in, unsigned long long inlen, + const unsigned char *ad, unsigned long long adlen) + */ +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_pull( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ) { + ERL_NIF_TERM ret; + ErlNifBinary m, in, ad; + unsigned char tag; + enacl_secretstream_ctx *obj = NULL; + + if (argc != 3) { + goto bad_arg; + } + + if(!enif_get_resource(env, argv[0], + (ErlNifResourceType *) enacl_secretstream_ctx_rtype, + (void **)&obj)) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[1], &in)) { + goto bad_arg; + } + + if(in.size < crypto_secretstream_xchacha20poly1305_ABYTES) { + goto bad_arg; + } + + if(!enif_inspect_binary(env, argv[2], &ad)) { + goto bad_arg; + } + + if(in.size < crypto_secretstream_xchacha20poly1305_ABYTES) { + goto bad_arg; + } + + if (!enif_alloc_binary(in.size - crypto_secretstream_xchacha20poly1305_ABYTES, &m)) { + return enacl_internal_error(env); + } + + enif_mutex_lock(obj->mtx); + if(!obj->alive) { + goto err; + } + + if (0 != crypto_secretstream_xchacha20poly1305_pull(obj->state, m.data, NULL, &tag, in.data, in.size, ad.data, ad.size)) { + ret = enacl_error_tuple(env, "failed_verification"); + goto release; + } + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL) { + if(obj->state) { + obj->alive = 0; + sodium_memzero(obj->state, crypto_secretstream_xchacha20poly1305_statebytes()); + enif_free(obj->state); + obj->state = NULL; + } + } + + ret = enif_make_tuple2(env, + enif_make_binary(env, &m), + enif_make_int64(env, tag) + ); + + goto done; + +bad_arg: + return enif_make_badarg(env); +err: + ret = enacl_error_finalized(env); +release: + enif_release_binary(&m); +done: + enif_mutex_unlock(obj->mtx); + return ret; +} diff --git a/c_src/secretstream.h b/c_src/secretstream.h new file mode 100644 index 0000000..4f1f4bd --- /dev/null +++ b/c_src/secretstream.h @@ -0,0 +1,78 @@ +#ifndef ENACL_SECRETSTREAM_H +#define ENACL_SECRETSTREAM_H + +#include + +int enacl_init_secretstream_ctx(ErlNifEnv *env); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_ABYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_HEADERBYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_KEYBYTES( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_MESSAGE( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_PUSH( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_REKEY( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_TAG_FINAL( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_keygen( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_push( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_init_pull( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_rekey( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_push( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +ERL_NIF_TERM enacl_crypto_secretstream_xchacha20poly1305_pull( + ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[] + ); + +#endif diff --git a/src/enacl.erl b/src/enacl.erl index 7024e8f..92f2605 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -174,6 +174,25 @@ kx_SESSIONKEYBYTES/0 ]). +%% Secretstream operations. +-export([ + %% No Tests! + secretstream_xchacha20poly1305_ABYTES/0, + secretstream_xchacha20poly1305_HEADERBYTES/0, + secretstream_xchacha20poly1305_KEYBYTES/0, + secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0, + secretstream_xchacha20poly1305_TAG_MESSAGE/0, + secretstream_xchacha20poly1305_TAG_PUSH/0, + secretstream_xchacha20poly1305_TAG_REKEY/0, + secretstream_xchacha20poly1305_TAG_FINAL/0, + secretstream_xchacha20poly1305_keygen/0, + secretstream_xchacha20poly1305_init_push/1, + secretstream_xchacha20poly1305_push/4, + secretstream_xchacha20poly1305_init_pull/2, + secretstream_xchacha20poly1305_pull/3, + secretstream_xchacha20poly1305_rekey/1 + ]). + %% Internal verification of the system -export([verify/0]). @@ -224,6 +243,11 @@ -define(CRYPTO_GENERICHASH_KEYBYTES_MAX, 64). -define(CRYPTO_GENERICHASH_KEYBYTES, 32). +-define(CRYPTO_SECRETSTREAM_TAG_MESSAGE, 0). +-define(CRYPTO_SECRETSTREAM_TAG_PUSH, 1). +-define(CRYPTO_SECRETSTREAM_TAG_REKEY, 2). +-define(CRYPTO_SECRETSTREAM_TAG_FINAL, 3). + %% Size limits -define(MAX_32BIT_INT, 1 bsl 32). @@ -253,7 +277,11 @@ verify() -> {crypto_generichash_BYTES_MAX, ?CRYPTO_GENERICHASH_BYTES_MAX}, {crypto_generichash_KEYBYTES, ?CRYPTO_GENERICHASH_KEYBYTES}, {crypto_generichash_KEYBYTES_MIN, ?CRYPTO_GENERICHASH_KEYBYTES_MIN}, - {crypto_generichash_KEYBYTES_MAX, ?CRYPTO_GENERICHASH_KEYBYTES_MAX} + {crypto_generichash_KEYBYTES_MAX, ?CRYPTO_GENERICHASH_KEYBYTES_MAX}, + {crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, ?CRYPTO_SECRETSTREAM_TAG_MESSAGE}, + {crypto_secretstream_xchacha20poly1305_TAG_PUSH, ?CRYPTO_SECRETSTREAM_TAG_PUSH}, + {crypto_secretstream_xchacha20poly1305_TAG_REKEY, ?CRYPTO_SECRETSTREAM_TAG_REKEY}, + {crypto_secretstream_xchacha20poly1305_TAG_FINAL, ?CRYPTO_SECRETSTREAM_TAG_FINAL} ], run_verifiers(Verifiers). @@ -1246,7 +1274,172 @@ aead_xchacha20poly1305_ietf_ABYTES() -> aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX() -> enacl_nif:crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX(). +%% Secretstream +%% ---------------------- +%% @doc secretstream_xchacha20poly1305_ABYTES/0 returns the number of bytes +%% of the MAC used on secretstream encryption/decryption +%% @end +-spec secretstream_xchacha20poly1305_ABYTES() -> pos_integer(). +secretstream_xchacha20poly1305_ABYTES() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_ABYTES(). + +%% @doc secretstream_xchacha20poly1305_HEADERBYTES/0 returns the number +%% of bytes for header used in secretstream encryption/decryption. +%% @end +-spec secretstream_xchacha20poly1305_HEADERBYTES() -> pos_integer(). +secretstream_xchacha20poly1305_HEADERBYTES() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_HEADERBYTES(). + +%% @doc secretstream_xchacha20poly1305_KEYBYTES/0 returns the number +%% of bytes of the key used in secretstream encryption/decryption. +%% @end +-spec secretstream_xchacha20poly1305_KEYBYTES() -> pos_integer(). +secretstream_xchacha20poly1305_KEYBYTES() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_KEYBYTES(). + +%% @doc secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0 returns the max +%% number of bytes allowed in a message in secretstream encryption/decryption. +%% @end +-spec secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() -> pos_integer(). +secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX(). + +%% @doc secretstream_xchacha20poly1305_TAG_MESSAGE/0 returns integer value +%% of tag `message'. The most common tag, that doesn't add any information +%% about the nature of the message. +%% @end +-spec secretstream_xchacha20poly1305_TAG_MESSAGE() -> pos_integer(). +secretstream_xchacha20poly1305_TAG_MESSAGE() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(). + +%% @doc secretstream_xchacha20poly1305_TAG_PUSH/0 returns integer value +%% of tag `push'. +%% +%% This tag indicates that the message marks the end +%% of a set of messages, but not the end of the stream. +%% +%% For example, a huge JSON string sent as multiple chunks can use +%% this tag to indicate to the application that the string is complete +%% and that it can be decoded. But the stream itself is not closed, +%% and more data may follow. +%% @end +-spec secretstream_xchacha20poly1305_TAG_PUSH() -> pos_integer(). +secretstream_xchacha20poly1305_TAG_PUSH() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_PUSH(). + +%% @doc secretstream_xchacha20poly1305_TAG_REKEY/0 returns integer value +%% of tag `rekey'. Indicates that next messages will derive new keys. +%% @end +-spec secretstream_xchacha20poly1305_TAG_REKEY() -> pos_integer(). +secretstream_xchacha20poly1305_TAG_REKEY() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_REKEY(). + +%% @doc secretstream_xchacha20poly1305_TAG_FINAL/0 returns integer value +%% of tag `final'. Indicates that the message is the last message in +%% the secretstream. +%% @end +-spec secretstream_xchacha20poly1305_TAG_FINAL() -> pos_integer(). +secretstream_xchacha20poly1305_TAG_FINAL() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_FINAL(). + +%% @doc secretstream_xchacha20poly1305_keygen/0 returns new random key +%% for secretsteam encryption. +%% @end +-spec secretstream_xchacha20poly1305_keygen() -> binary(). +secretstream_xchacha20poly1305_keygen() -> + enacl_nif:crypto_secretstream_xchacha20poly1305_keygen(). + +%% @doc secretstream_xchacha20poly1305_init_push/1 +%% initializes a secretstream encryption context using given `key'. +%% Returns `Header' and reference to encryption context. +%% @end +-spec secretstream_xchacha20poly1305_init_push(Key) -> {binary(), reference()} + when Key :: binary(). +secretstream_xchacha20poly1305_init_push(Key) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_init_push(Key). + +-type secretstream_xchacha20poly1305_tag() :: message | rekey | final | push | pos_integer(). +%% @doc secretstream_xchacha20poly1305_push/4 returns encrypted chunk binary. +%% Updates a secretstream context referenced by `Ref' with `Message' data, +%% given `Tag' and additional data `AD'. +%% @end +-spec secretstream_xchacha20poly1305_push(Ref, Message, AD, Tag) -> binary() + when + Ref :: reference(), + Message :: binary(), + AD :: binary(), + Tag :: secretstream_xchacha20poly1305_tag(). +secretstream_xchacha20poly1305_push(Ref, Message, AD, Tag) -> + TagValue = secretstream_xchacha20poly1305_tag_value(Tag), + + enacl_nif:crypto_secretstream_xchacha20poly1305_push(Ref, Message, AD, TagValue). + +%% @doc secretstream_xchacha20poly1305_init_pull/3 +%% initializes a secretstream decryption context using `Header' and `Key'. +%% Returns reference to decryption context. +%% @end +-spec secretstream_xchacha20poly1305_init_pull(Header, Key) -> reference() + when + Header :: binary(), + Key :: binary(). +secretstream_xchacha20poly1305_init_pull(Header, Key) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_init_pull(Header, Key). + +%% @doc secretstream_xchacha20poly1305_pull/3 decrypts `CipherText' +%% with additional data `AD' in referenced decryption context `Ref'. +%% @end +-spec secretstream_xchacha20poly1305_pull(Ref, CipherText, AD) -> + {binary(), secretstream_xchacha20poly1305_tag()} | {error, failed_verification} + when + Ref :: reference(), + CipherText :: binary(), + AD :: binary(). +secretstream_xchacha20poly1305_pull(Ref, CipherText, AD) -> + {Message, TagValue} = enacl_nif:crypto_secretstream_xchacha20poly1305_pull(Ref, CipherText, AD), + {Message, secretstream_xchacha20poly1305_tag(TagValue)}. + +%% @doc secretstream_xchacha20poly1305_rekey/1 updates encryption/decryption context state. +%% This doesn't add any information about key update to stream. +%% If this function is used to create an encrypted stream, +%% the decryption process must call that function at the exact same stream location. +%% @end +-spec secretstream_xchacha20poly1305_rekey(Ref) -> ok + when Ref :: reference(). +secretstream_xchacha20poly1305_rekey(Ref) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_rekey(Ref). + +%% @doc secretstream_xchacha20poly1305_tag_value/1 returns integer value of tag. +%% @end +-spec secretstream_xchacha20poly1305_tag_value(TagName) -> pos_integer() + when TagName :: secretstream_xchacha20poly1305_tag(). +secretstream_xchacha20poly1305_tag_value(message) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_MESSAGE(); +secretstream_xchacha20poly1305_tag_value(rekey) -> + enacl_nif:crypto_secretstream_xcacha20poly1305_TAG_REKEY(); +secretstream_xchacha20poly1305_tag_value(push) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_PUSH(); +secretstream_xchacha20poly1305_tag_value(final) -> + enacl_nif:crypto_secretstream_xchacha20poly1305_TAG_FINAL(); +secretstream_xchacha20poly1305_tag_value(Other) -> + Other. + +%% @doc secretstream_xchacha20poly1305_tag/1 returns tag name +%% @end +-spec secretstream_xchacha20poly1305_tag(TagValue) -> secretstream_xchacha20poly1305_tag() + when TagValue :: pos_integer(). +secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_MESSAGE) -> + message; +secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_PUSH) -> + push; +secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_REKEY) -> + rekey; +secretstream_xchacha20poly1305_tag(?CRYPTO_SECRETSTREAM_TAG_FINAL) -> + final; +secretstream_xchacha20poly1305_tag(Other) -> + Other. + %% Obtaining random bytes +%% ---------------------- %% @doc randombytes/1 produces a stream of random bytes of the given size %% diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 1309a11..f2255f8 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -166,6 +166,24 @@ crypto_generichash_final/1 ]). +%% Secretstream +-export([ + crypto_secretstream_xchacha20poly1305_ABYTES/0, + crypto_secretstream_xchacha20poly1305_HEADERBYTES/0, + crypto_secretstream_xchacha20poly1305_KEYBYTES/0, + crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX/0, + crypto_secretstream_xchacha20poly1305_TAG_MESSAGE/0, + crypto_secretstream_xchacha20poly1305_TAG_PUSH/0, + crypto_secretstream_xchacha20poly1305_TAG_REKEY/0, + crypto_secretstream_xchacha20poly1305_TAG_FINAL/0, + crypto_secretstream_xchacha20poly1305_keygen/0, + crypto_secretstream_xchacha20poly1305_init_push/1, + crypto_secretstream_xchacha20poly1305_push/4, + crypto_secretstream_xchacha20poly1305_init_pull/2, + crypto_secretstream_xchacha20poly1305_pull/3, + crypto_secretstream_xchacha20poly1305_rekey/1 + ]). + %% Access to the RNG -export([ randombytes/1, @@ -205,6 +223,21 @@ crypto_generichash_init(_HashSize, _Key) -> erlang:nif_error(nif_not_loaded). crypto_generichash_update(_HashState, _Message) -> erlang:nif_error(nif_not_loaded). crypto_generichash_final(_HashState) -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_ABYTES() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_HEADERBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_KEYBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_TAG_MESSAGE() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_TAG_PUSH() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_TAG_REKEY() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_TAG_FINAL() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_keygen() -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_init_push(_Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_push(_Ref, _Message, _AD, _Tag) -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_init_pull(_Header, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_pull(_Ref, _CipherText, _AD) -> erlang:nif_error(nif_not_loaded). +crypto_secretstream_xchacha20poly1305_rekey(_Ref) -> erlang:nif_error(nif_not_loaded). + crypto_pwhash(_Password, _Salt, _Ops, _Mem) -> erlang:nif_error(nif_not_loaded). crypto_pwhash_str(_Password, _Ops, _Mem) -> erlang:nif_error(nif_not_loaded). crypto_pwhash_str_verify(_HashedPassword, _Password) -> erlang:nif_error(nif_not_loaded).