From 9dfbe8cc90aca1e70a934ec8854ac689cb073853 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 1 Mar 2018 08:42:31 +0100 Subject: [PATCH] Expose AEAD ChaCha20 Poly1305 functionality Note: We expose the modern IETF version but still name it plain aead_chacha20poly1305. --- c_src/enacl_nif.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++ src/enacl.erl | 62 +++++++++++++++++++++++++++++ src/enacl_nif.erl | 14 +++++++ 3 files changed, 175 insertions(+) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index a6e54b5..a6d422b 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -1313,6 +1313,98 @@ ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, ERL_NIF_TER 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 */ @@ -1624,6 +1716,13 @@ static ErlNifFunc nif_funcs[] = { {"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}, diff --git a/src/enacl.erl b/src/enacl.erl index 76e834c..10ecc1c 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -54,6 +54,13 @@ stream_chacha20/3, stream_chacha20_xor/3, + aead_chacha20poly1305_encrypt/4, + aead_chacha20poly1305_decrypt/4, + aead_chacha20poly1305_KEYBYTES/0, + aead_chacha20poly1305_NONCEBYTES/0, + aead_chacha20poly1305_ABYTES/0, + aead_chacha20poly1305_MESSAGEBYTES_MAX/0, + stream_key_size/0, stream_nonce_size/0, stream/3, @@ -997,7 +1004,62 @@ kx_public_key_size() -> kx_secret_key_size() -> enacl_nif:crypto_kx_SECRETKEYBYTES(). +%% AEAD ChaCha20 Poly1305 +%% ---------------------- +%% @doc aead_chacha20poly1305_encrypt/4 encrypts `Message` with additional data +%% `AD` using `Key` and `Nonce`. Returns the encrypted message followed by +%% `aead_chacha20poly1305_ABYTES/0` bytes of MAC. +%% @end +-spec aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg) -> binary() | {error, term()} + when Key :: binary(), + Nonce :: pos_integer(), + AD :: binary(), + Msg :: binary(). +aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg) -> + NonceBin = <<0:32, Nonce:64/little-unsigned-integer>>, + enacl_nif:crypto_aead_chacha20poly1305_encrypt(Key, NonceBin, AD, Msg). +%% @doc aead_chacha20poly1305_decrypt/4 decrypts ciphertext `CT` with additional +%% data `AD` using `Key` and `Nonce`. Note: `CipherText` should contain +%% `aead_chacha20poly1305_ABYTES/0` bytes that is the MAC. Returns the decrypted +%% message. +%% @end +-spec aead_chacha20poly1305_decrypt(Key, Nonce, AD, CT) -> binary() | {error, term()} + when Key :: binary(), + Nonce :: pos_integer(), + AD :: binary(), + CT :: binary(). +aead_chacha20poly1305_decrypt(Key, Nonce, AD, CT) -> + NonceBin = <<0:32, Nonce:64/little-unsigned-integer>>, + enacl_nif:crypto_aead_chacha20poly1305_decrypt(Key, NonceBin, AD, CT). + +%% @doc aead_chacha20poly1305_KEYBYTES/0 returns the number of bytes +%% of the key used in AEAD ChaCha20 Poly1305 encryption/decryption. +%% @end +-spec aead_chacha20poly1305_KEYBYTES() -> pos_integer(). +aead_chacha20poly1305_KEYBYTES() -> + enacl_nif:crypto_aead_chacha20poly1305_KEYBYTES(). + +%% @doc aead_chacha20poly1305_NONCEBYTES/0 returns the number of bytes +%% of the Nonce in AEAD ChaCha20 Poly1305 encryption/decryption. +%% @end +-spec aead_chacha20poly1305_NONCEBYTES() -> pos_integer(). +aead_chacha20poly1305_NONCEBYTES() -> + enacl_nif:crypto_aead_chacha20poly1305_NPUBBYTES(). + +%% @doc aead_chacha20poly1305_ABYTES/0 returns the number of bytes +%% of the MAC in AEAD ChaCha20 Poly1305 encryption/decryption. +%% @end +-spec aead_chacha20poly1305_ABYTES() -> pos_integer(). +aead_chacha20poly1305_ABYTES() -> + enacl_nif:crypto_aead_chacha20poly1305_ABYTES(). + +%% @doc aead_chacha20poly1305_MESSAGEBYTES_MAX/0 returns the max number of bytes +%% allowed in a message in AEAD ChaCha20 Poly1305 encryption/decryption. +%% @end +-spec aead_chacha20poly1305_MESSAGEBYTES_MAX() -> pos_integer(). +aead_chacha20poly1305_MESSAGEBYTES_MAX() -> + enacl_nif:crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX(). %% Obtaining random bytes diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 2b0f85b..8e807b1 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -67,6 +67,13 @@ crypto_stream_xor/3, crypto_stream_xor_b/3, + crypto_aead_chacha20poly1305_encrypt/4, + crypto_aead_chacha20poly1305_decrypt/4, + crypto_aead_chacha20poly1305_KEYBYTES/0, + crypto_aead_chacha20poly1305_NPUBBYTES/0, + crypto_aead_chacha20poly1305_ABYTES/0, + crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX/0, + crypto_auth_BYTES/0, crypto_auth_KEYBYTES/0, @@ -240,6 +247,13 @@ crypto_stream_b(_Bytes, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_stream_xor(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). crypto_stream_xor_b(_M, _Nonce, _Key) -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_encrypt(_Key, _Nonce, _AD, _Message) -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_decrypt(_Key, _Nonce, _AD, _Message) -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_KEYBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_NPUBBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_ABYTES() -> erlang:nif_error(nif_not_loaded). +crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX() -> erlang:nif_error(nif_not_loaded). + crypto_auth_BYTES() -> erlang:nif_error(nif_not_loaded). crypto_auth_KEYBYTES() -> erlang:nif_error(nif_not_loaded). crypto_auth(_Msg, _Key) -> erlang:nif_error(nif_not_loaded).