diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index f2c752c..c15b3e8 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -153,6 +153,34 @@ ERL_NIF_TERM enif_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc, ERL_NIF 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[]) { @@ -1313,6 +1341,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 */ @@ -1606,6 +1726,7 @@ static ErlNifFunc nif_funcs[] = { {"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}, @@ -1624,6 +1745,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/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 5a91693..2e296f0 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -822,6 +822,26 @@ badargs(Thunk) -> error:badarg -> true end. +%% AEAD ChaCha20Poly1305 +prop_aead_chacha20poly1305() -> + ?FORALL({Key, Msg, AD, Nonce}, + {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), largeint()}, + begin + EncryptMsg = enacl:aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg), + equals(enacl:aead_chacha20poly1305_decrypt(Key, Nonce, AD, EncryptMsg), Msg) + end). + +prop_aead_chacha20poly1305_fail() -> + ?FORALL({Key, Msg, AD, Nonce}, + {binary(32), binary(), ?LET(ADBytes, choose(0,16), binary(ADBytes)), largeint()}, + begin + EncryptMsg = enacl:aead_chacha20poly1305_encrypt(Key, Nonce, AD, Msg), + case enacl:aead_chacha20poly1305_decrypt(Key, Nonce, AD, <<0:8, EncryptMsg/binary>>) of + {error, _} -> true; + _ -> false + end + end). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Joel Test Blobs diff --git a/src/enacl.erl b/src/enacl.erl index 0162830..9456cfd 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, @@ -76,7 +83,8 @@ %% Curve 25519. -export([ - curve25519_scalarmult/1, curve25519_scalarmult/2 + curve25519_scalarmult/1, curve25519_scalarmult/2, + curve25519_scalarmult_base/1 ]). %% Ed 25519. @@ -898,6 +906,13 @@ curve25519_scalarmult(Secret, BasePoint) -> curve25519_scalarmult(#{ secret := Secret, base_point := BasePoint }) -> curve25519_scalarmult(Secret, BasePoint). +%% @doc curve25519_scalarmult_base/1 compute the corresponding public key for a +%% given secret key. +%% @end. +-spec curve25519_scalarmult_base(Secret :: binary()) -> binary(). +curve25519_scalarmult_base(Secret) -> + enacl_nif:crypto_curve25519_scalarmult_base(Secret). + %% Ed 25519 Crypto %% --------------- %% @doc crypto_sign_ed25519_keypair/0 creates a new Ed 25519 Public/Secret keypair. @@ -997,7 +1012,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..798ef53 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, @@ -91,7 +98,8 @@ %% Curve25519 -export([ - crypto_curve25519_scalarmult/2 + crypto_curve25519_scalarmult/2, + crypto_curve25519_scalarmult_base/1 ]). %% Ed 25519 @@ -240,6 +248,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). @@ -259,6 +274,7 @@ crypto_onetimeauth_verify(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_no crypto_onetimeauth_verify_b(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_not_loaded). crypto_curve25519_scalarmult(_Secret, _BasePoint) -> erlang:nif_error(nif_not_loaded). +crypto_curve25519_scalarmult_base(_Secret) -> erlang:nif_error(nif_not_loaded). crypto_sign_ed25519_keypair() -> erlang:nif_error(nif_not_loaded). crypto_sign_ed25519_public_to_curve25519(_PublicKey) -> erlang:nif_error(nif_not_loaded).