diff --git a/CHANGELOG.md b/CHANGELOG.md index 904b78d..a0b5343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,10 +30,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. following the style of the Erlang/OTP `crypto` library. While here, make sure we clean up correctly and that we don't accidentally mis-ref-count data. The code is a bit more goto heavy, but this style is surprisingly common in C code. +- Use sodium's dynamic memory allocators. These guarantee 64bit alignment, and also + provide guard pages around the allocation, somewhat protecting it. It adds some + page table pressure compared to the current code, but is easier to maintain and + much cleaner code. - The code now rejects updates to generichash states which were already finalized. - We now track the desired outlen of a generichash operation in the opaque NIF resource rather than on the Erlang side. This avoids some checks in the code, and streamlines a good deal of the interface. +- Split AEAD routines off from the main enacl_nif.c file ### Fixes - Fix a resource leak in generichash/sign init/update/final. diff --git a/c_src/aead.c b/c_src/aead.c new file mode 100644 index 0000000..58f432a --- /dev/null +++ b/c_src/aead.c @@ -0,0 +1,195 @@ +#include "aead.h" +#include "enacl.h" +#include "erl_nif.h" + +#include + +/* + * AEAD ChaCha20 Poly1305 + */ +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); +} + +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); +} + +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); +} + +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); +} + +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; +} + +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; +} + +/* + * AEAD XChaCha20 Poly1305 + */ +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_KEYBYTES); +} + +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); +} + +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_ABYTES); +} + +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, + crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); +} + +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_encrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + 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_xchacha20poly1305_ietf_KEYBYTES) || + (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(message.size + + crypto_aead_xchacha20poly1305_ietf_ABYTES, + &ciphertext)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_aead_xchacha20poly1305_ietf_encrypt( + ciphertext.data, NULL, message.data, message.size, ad.data, ad.size, + NULL, nonce.data, key.data) < 0) { + return nacl_error_tuple(env, "aead_xchacha20poly1305_ietf_encrypt_failed"); + } + + return enif_make_binary(env, &ciphertext); +} + +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_decrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + 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_xchacha20poly1305_ietf_ABYTES) || + (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES) || + (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(ciphertext.size - + crypto_aead_xchacha20poly1305_ietf_ABYTES, + &message)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_aead_xchacha20poly1305_ietf_decrypt( + message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data, + ad.size, nonce.data, key.data) < 0) { + return nacl_error_tuple(env, "aead_xchacha20poly1305_ietf_decrypt_failed"); + } + + return enif_make_binary(env, &message); +} diff --git a/c_src/aead.h b/c_src/aead.h new file mode 100644 index 0000000..a342c6d --- /dev/null +++ b/c_src/aead.h @@ -0,0 +1,46 @@ +#ifndef ENACL_AEAD_H +#define ENACL_AEAD_H + +#include "erl_nif.h" + +/* AEAD ChaCha20 Poly1305 */ +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_ABYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_encrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_chacha20poly1305_decrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +/* AEAD XChaCha20 Poly1305 */ +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_encrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); +ERL_NIF_TERM +enif_crypto_aead_xchacha20poly1305_decrypt(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +#endif diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 22bf062..31a4ade 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -3,6 +3,7 @@ #include #include +#include "aead.h" #include "enacl.h" #include "generichash.h" #include "hash.h" @@ -1544,196 +1545,6 @@ static ERL_NIF_TERM enif_crypto_pwhash_str_verify(ErlNifEnv *env, int argc, 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; -} - -/* - * AEAD XChaCha20 Poly1305 - */ -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_KEYBYTES(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_KEYBYTES); -} - -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_NPUBBYTES(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); -} - -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_ABYTES(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - return enif_make_int64(env, crypto_aead_xchacha20poly1305_ietf_ABYTES); -} - -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_MESSAGEBYTES_MAX(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - return enif_make_int64(env, - crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); -} - -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_encrypt(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - 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_xchacha20poly1305_ietf_KEYBYTES) || - (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)) { - return enif_make_badarg(env); - } - - if (!enif_alloc_binary(message.size + - crypto_aead_xchacha20poly1305_ietf_ABYTES, - &ciphertext)) { - return nacl_error_tuple(env, "alloc_failed"); - } - - if (crypto_aead_xchacha20poly1305_ietf_encrypt( - ciphertext.data, NULL, message.data, message.size, ad.data, ad.size, - NULL, nonce.data, key.data) < 0) { - return nacl_error_tuple(env, "aead_xchacha20poly1305_ietf_encrypt_failed"); - } - - return enif_make_binary(env, &ciphertext); -} - -static ERL_NIF_TERM -enif_crypto_aead_xchacha20poly1305_decrypt(ErlNifEnv *env, int argc, - ERL_NIF_TERM const argv[]) { - 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_xchacha20poly1305_ietf_ABYTES) || - (key.size != crypto_aead_xchacha20poly1305_ietf_KEYBYTES) || - (nonce.size != crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)) { - return enif_make_badarg(env); - } - - if (!enif_alloc_binary(ciphertext.size - - crypto_aead_xchacha20poly1305_ietf_ABYTES, - &message)) { - return nacl_error_tuple(env, "alloc_failed"); - } - - if (crypto_aead_xchacha20poly1305_ietf_decrypt( - message.data, NULL, NULL, ciphertext.data, ciphertext.size, ad.data, - ad.size, nonce.data, key.data) < 0) { - return nacl_error_tuple(env, "aead_xchacha20poly1305_ietf_decrypt_failed"); - } - - return enif_make_binary(env, &message); -} - /* Tie the knot to the Erlang world */ static ErlNifFunc nif_funcs[] = { {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES}, diff --git a/test/enacl_SUITE.erl b/test/enacl_SUITE.erl index 3470b3e..14326c1 100644 --- a/test/enacl_SUITE.erl +++ b/test/enacl_SUITE.erl @@ -79,7 +79,7 @@ generichash_chunked(_Config) -> {ok, Expected} = enacl:generichash_final(State), ok. -generichash_chunked(State, Msg, 0) -> State; +generichash_chunked(State, _Msg, 0) -> State; generichash_chunked(State, Msg, N) -> State2 = enacl:generichash_update(State, Msg), generichash_chunked(State2, Msg, N-1).