From fa94eaf6f622392eb6021c36cbb1bc024af0128c Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 6 Jan 2022 18:26:21 +0100 Subject: [PATCH] Add access to secretbox_easy/easy_open functions They are just a simplification of the secretbox API, thus it does not provide any new functionality. But it helps mapping function names to libsodium documentation. --- c_src/enacl_nif.c | 6 +++++ c_src/secret.c | 58 ++++++++++++++++++++++++++++++++++++++++++ c_src/secret.h | 6 +++++ eqc_test/enacl_eqc.erl | 38 +++++++++++++++++++++++++++ src/enacl.erl | 46 +++++++++++++++++++++++++++++++++ src/enacl_nif.erl | 8 ++++++ 6 files changed, 162 insertions(+) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 268096a..5f67ab4 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -256,6 +256,12 @@ static ErlNifFunc nif_funcs[] = { {"crypto_secretbox_open_b", 3, enacl_crypto_secretbox_open}, erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open", 3, enacl_crypto_secretbox_open), + {"crypto_secretbox_easy_b", 3, enacl_crypto_secretbox_easy}, + erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_easy", 3, + enacl_crypto_secretbox_easy), + {"crypto_secretbox_open_easy_b", 3, enacl_crypto_secretbox_open_easy}, + erl_nif_dirty_job_cpu_bound_macro("crypto_secretbox_open_easy", 3, + enacl_crypto_secretbox_open_easy), {"crypto_stream_chacha20_KEYBYTES", 0, enacl_crypto_stream_chacha20_KEYBYTES}, diff --git a/c_src/secret.c b/c_src/secret.c index 627d444..8a681a4 100644 --- a/c_src/secret.c +++ b/c_src/secret.c @@ -137,6 +137,64 @@ ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc, return enif_make_tuple2(env, ret_ok, ret_bin); } +ERL_NIF_TERM enacl_crypto_secretbox_easy(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ErlNifBinary key, nonce, msg, cipherbox; + + if ((argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &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)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(msg.size + crypto_secretbox_MACBYTES, &cipherbox)) { + return enacl_internal_error(env); + } + + crypto_secretbox_easy(cipherbox.data, msg.data, msg.size, + nonce.data, key.data); + + return enif_make_binary(env, &cipherbox); +} + +ERL_NIF_TERM enacl_crypto_secretbox_open_easy(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ErlNifBinary key, nonce, cipherbox, msg; + + if ((argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &cipherbox)) || + (!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) || + (cipherbox.size < crypto_secretbox_MACBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(cipherbox.size - crypto_secretbox_MACBYTES, &msg)) { + return enacl_internal_error(env); + } + + if (crypto_secretbox_open_easy(msg.data, cipherbox.data, cipherbox.size, + nonce.data, key.data) != 0) { + enif_release_binary(&msg); + return enacl_error_tuple(env, "failed_verification"); + } + + ERL_NIF_TERM ret_ok = enif_make_atom(env, ATOM_OK); + ERL_NIF_TERM ret_bin = enif_make_binary(env, &msg); + return enif_make_tuple2(env, ret_ok, ret_bin); +} + ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary c, n, k; diff --git a/c_src/secret.h b/c_src/secret.h index 6c0a4c8..569c7a5 100644 --- a/c_src/secret.h +++ b/c_src/secret.h @@ -43,6 +43,12 @@ ERL_NIF_TERM enacl_crypto_secretbox(ErlNifEnv *env, int argc, ERL_NIF_TERM enacl_crypto_secretbox_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]); +ERL_NIF_TERM enacl_crypto_secretbox_easy(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_secretbox_open_easy(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + ERL_NIF_TERM enacl_crypto_stream_chacha20(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]); diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 6093a5b..cec95e6 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -550,6 +550,44 @@ prop_secretbox_failure_integrity() -> equals(Err, {error, failed_verification}) end). +secretbox_easy(Msg, Nonce, Key) -> + try enacl:secretbox_easy(Msg, Nonce, Key) + catch error:badarg -> badarg + end. + +secretbox_open_easy(Msg, Nonce, Key) -> + try enacl:secretbox_open_easy(Msg, Nonce, Key) + catch error:badarg -> badarg + end. + +prop_secretbox_easy_correct() -> + ?FORALL({Msg, Nonce, Key}, + {?FAULT_RATE(1, 40, g_iodata()), + ?FAULT_RATE(1, 40, nonce()), + ?FAULT_RATE(1, 40, secret_key())}, + begin + case v_iodata(Msg) andalso nonce_valid(Nonce) andalso secret_key_valid(Key) of + true -> + CipherText = enacl:secretbox_easy(Msg, Nonce, Key), + {ok, DecodedMsg} = enacl:secretbox_open_easy(CipherText, Nonce, Key), + equals(iolist_to_binary(Msg), DecodedMsg); + false -> + case secretbox_easy(Msg, Nonce, Key) of + badarg -> true; + Res -> + failure(secretbox_open_easy(Res, Nonce, Key)) + end + end + end). + +prop_secretbox_easy_failure_integrity() -> + ?FORALL({Msg, Nonce, Key}, {g_iodata(), nonce(), secret_key()}, + begin + CipherText = enacl:secretbox_easy(Msg, Nonce, Key), + Err = enacl:secretbox_open_easy([<<"x">>, CipherText], Nonce, Key), + equals(Err, {error, failed_verification}) + end). + %% AEAD ChaCha20Poly1305 %% ------------------------------------------------------------ %% * aead_chacha20poly1305_encrypt/4, diff --git a/src/enacl.erl b/src/enacl.erl index 378bb7b..3599c70 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -59,6 +59,8 @@ secretbox_NONCEBYTES/0, secretbox/3, secretbox_open/3, + secretbox_easy/3, + secretbox_open_easy/3, %% No Tests! stream_chacha20_KEYBYTES/0, @@ -837,6 +839,50 @@ secretbox_open(CipherText, Nonce, Key) -> enacl_nif:crypto_secretbox_open([?S_BOXZEROBYTES, CipherText], Nonce, Key) end. +%% @doc secretbox_easy/3 encrypts a message with a key (and nonce) +%% +%% Given a `Msg', a `Nonce' and a `Key' encrypt the message with the Key while taking the +%% nonce into consideration. `easy' refers to not having to take padding, etc. into +%% account. The function returns the Box obtained from the encryption. +%% @end +-spec secretbox_easy(Msg, Nonce, Key) -> Box + when + Msg :: iodata(), + Nonce :: binary(), + Key :: binary(), + Box :: binary(). +secretbox_easy(Msg, Nonce, Key) -> + case iolist_size(Msg) of + K when K =< ?SECRETBOX_SIZE -> + bump(enacl_nif:crypto_secretbox_easy_b(Msg, Nonce, Key), + ?SECRETBOX_REDUCTIONS, + ?SECRETBOX_SIZE, + K); + _ -> + enacl_nif:crypto_secretbox_easy(Msg, Nonce, Key) + end. + +%% @doc secretbox_open_easy/3 opens a sealed box. +%% +%% Given a boxed `CipherText' and given we know the used `Nonce' and `Key' we can open the box +%% to obtain the `Msg' within. `easy' refers to not having to take padding, etc. into +%% account. Returns either `{ok, Msg}' or `{error, failed_verification}'. +%% @end +-spec secretbox_open_easy(CipherText, Nonce, Key) -> {ok, Msg} | {error, failed_verification} + when + CipherText :: iodata(), + Nonce :: binary(), + Key :: binary(), + Msg :: binary(). +secretbox_open_easy(CipherText, Nonce, Key) -> + case iolist_size(CipherText) of + K when K =< ?SECRETBOX_SIZE -> + R = enacl_nif:crypto_secretbox_open_easy_b(CipherText, Nonce, Key), + bump(R, ?SECRETBOX_OPEN_REDUCTIONS, ?SECRETBOX_SIZE, K); + _ -> + enacl_nif:crypto_secretbox_open_easy(CipherText, Nonce, Key) + end. + %% @doc secretbox_NONCEBYTES()/0 returns the size of the secretbox nonce %% %% When encrypting with a secretbox, the nonce must have this size diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 3ecb962..4630202 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -57,6 +57,10 @@ crypto_secretbox_b/3, crypto_secretbox_open/3, crypto_secretbox_open_b/3, + crypto_secretbox_easy/3, + crypto_secretbox_easy_b/3, + crypto_secretbox_open_easy/3, + crypto_secretbox_open_easy_b/3, crypto_stream_chacha20_KEYBYTES/0, crypto_stream_chacha20_NONCEBYTES/0, @@ -303,6 +307,10 @@ crypto_secretbox(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_secretbox_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_secretbox_open(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_secretbox_open_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretbox_easy(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretbox_easy_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretbox_open_easy(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_secretbox_open_easy_b(_Msg, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_stream_chacha20_KEYBYTES() -> erlang:nif_error(nif_not_loaded). crypto_stream_chacha20_NONCEBYTES() -> erlang:nif_error(nif_not_loaded).