From c08f83a755375341e3babad1f7da77dc72dd1169 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Fri, 21 Nov 2014 17:42:32 +0100 Subject: [PATCH] Implement cryptography for secret boxes. --- c_src/enacl_nif.c | 108 ++++++++++++++++++++++++++++++++++++++++- eqc_test/enacl_eqc.erl | 30 ++++++++++-- src/enacl.erl | 40 +++++++++++++-- src/enacl_nif.erl | 18 +++++++ 4 files changed, 187 insertions(+), 9 deletions(-) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 39b0676..607c262 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -124,7 +124,7 @@ ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const a return nacl_error_tuple(env, "alloc_failed"); } - if (crypto_box_open(result.data, padded_ciphertext.data, padded_ciphertext.size, nonce.data, pk.data, sk.data)) { + if (crypto_box_open(result.data, padded_ciphertext.data, padded_ciphertext.size, nonce.data, pk.data, sk.data) != 0) { enif_release_binary(&result); return nacl_error_tuple(env, "failed_verification"); } @@ -135,7 +135,103 @@ ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const a crypto_box_ZEROBYTES, padded_ciphertext.size - crypto_box_ZEROBYTES); } +/* Secret key cryptography */ +static +ERL_NIF_TERM enif_crypto_secretbox_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_secretbox_NONCEBYTES); +} + +static +ERL_NIF_TERM enif_crypto_secretbox_KEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_secretbox_KEYBYTES); +} + +static +ERL_NIF_TERM enif_crypto_secretbox_ZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_secretbox_ZEROBYTES); +} + +static +ERL_NIF_TERM enif_crypto_secretbox_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_secretbox_BOXZEROBYTES); +} + +static +ERL_NIF_TERM enif_crypto_secretbox(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary key, nonce, padded_msg, padded_ciphertext; + + if ( + (argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &padded_msg)) || + (!enif_inspect_iolist_as_binary(env, argv[1], &nonce)) || + (!enif_inspect_iolist_as_binary(env, argv[2], &key))) { + return enif_make_badarg(env); + } + + if ( + (key.size != crypto_secretbox_KEYBYTES) || + (nonce.size != crypto_secretbox_NONCEBYTES) || + (padded_msg.size < crypto_secretbox_ZEROBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(padded_msg.size, &padded_ciphertext)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + crypto_secretbox( + padded_ciphertext.data, + padded_msg.data, padded_msg.size, + nonce.data, + key.data); + + return enif_make_sub_binary(env, + enif_make_binary(env, &padded_ciphertext), + crypto_secretbox_BOXZEROBYTES, + padded_msg.size - crypto_secretbox_BOXZEROBYTES); +} + +static +ERL_NIF_TERM enif_crypto_secretbox_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary key, nonce, padded_ciphertext, padded_msg; + + if ( + (argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &padded_ciphertext)) || + (!enif_inspect_iolist_as_binary(env, argv[1], &nonce)) || + (!enif_inspect_iolist_as_binary(env, argv[2], &key))) { + return enif_make_badarg(env); + } + + if ( + (key.size != crypto_secretbox_KEYBYTES) || + (nonce.size != crypto_secretbox_NONCEBYTES) || + (padded_ciphertext.size < crypto_secretbox_BOXZEROBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(padded_ciphertext.size, &padded_msg)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_secretbox_open( + padded_msg.data, + padded_ciphertext.data, + padded_ciphertext.size, + nonce.data, + key.data) != 0) { + enif_release_binary(&padded_msg); + return nacl_error_tuple(env, "failed_verification"); + } + + return enif_make_sub_binary( + env, + enif_make_binary(env, &padded_msg), + crypto_secretbox_ZEROBYTES, + padded_ciphertext.size - crypto_secretbox_ZEROBYTES); +} + /* Tie the knot to the Erlang world */ static ErlNifFunc nif_funcs[] = { {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES}, @@ -144,7 +240,17 @@ static ErlNifFunc nif_funcs[] = { {"crypto_box_keypair", 0, enif_crypto_box_keypair}, {"crypto_box", 4, enif_crypto_box, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"crypto_box_open", 4, enif_crypto_box_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + + {"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES}, + {"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES}, + {"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES}, + {"crypto_secretbox_KEYBYTES", 0, enif_crypto_secretbox_KEYBYTES}, + {"crypto_secretbox", 3, enif_crypto_secretbox, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_secretbox_open", 3, enif_crypto_secretbox_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_hash", 1, enif_crypto_hash, ERL_NIF_DIRTY_JOB_CPU_BOUND} }; + + ERL_NIF_INIT(enacl_nif, nif_funcs, NULL, NULL, NULL, NULL); diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 321e8d5..3844912 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,13 +2,14 @@ -include_lib("eqc/include/eqc.hrl"). -compile(export_all). -%% CRYPTO BOX -%% --------------------------- - nonce() -> Sz = enacl:box_nonce_size(), binary(Sz). +%% CRYPTO BOX +%% --------------------------- + + prop_box_keypair() -> ?FORALL(_X, return(dummy), ok_box(enacl:box_keypair())). @@ -36,6 +37,29 @@ prop_box_failure_integrity() -> equals(Err, {error, failed_verification}) end). +%% CRYPTO SECRET BOX +%% ------------------------------- + +secret_key() -> + Sz = enacl:secretbox_key_size(), + binary(Sz). + +prop_secretbox_correct() -> + ?FORALL({Msg, Nonce, Key}, {binary(), nonce(), secret_key()}, + begin + CipherText = enacl:secretbox(Msg, Nonce, Key), + {ok, DecodedMsg} = enacl:secretbox_open(CipherText, Nonce, Key), + equals(Msg, DecodedMsg) + end). + +prop_secretbox_failure_integrity() -> + ?FORALL({Msg, Nonce, Key}, {binary(), nonce(), secret_key()}, + begin + CipherText = enacl:secretbox(Msg, Nonce, Key), + Err = enacl:secretbox_open([<<"x">>, CipherText], Nonce, Key), + equals(Err, {error, failed_verification}) + end). + %% HASHING %% --------------------------- diff_pair(Sz) -> diff --git a/src/enacl.erl b/src/enacl.erl index c36f7e9..459f517 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -1,5 +1,6 @@ -module(enacl). +%% Public key crypto -export([ box_keypair/0, box/4, @@ -7,21 +8,29 @@ box_nonce_size/0 ]). +%% Secret key crypto +-export([ + secretbox/3, + secretbox_open/3, + secretbox_nonce_size/0, + secretbox_key_size/0 +]). + -export([ hash/1 ]). hash(Bin) -> enacl_nif:crypto_hash(Bin). - + box_keypair() -> enacl_nif:crypto_box_keypair(). box(Msg, Nonce, PK, SK) -> - enacl_nif:crypto_box([zerobytes(), Msg], Nonce, PK, SK). + enacl_nif:crypto_box([p_zerobytes(), Msg], Nonce, PK, SK). box_open(CipherText, Nonce, PK, SK) -> - case enacl_nif:crypto_box_open([box_zerobytes(), CipherText], Nonce, PK, SK) of + case enacl_nif:crypto_box_open([p_box_zerobytes(), CipherText], Nonce, PK, SK) of {error, Err} -> {error, Err}; Bin when is_binary(Bin) -> {ok, Bin} end. @@ -29,9 +38,30 @@ box_open(CipherText, Nonce, PK, SK) -> box_nonce_size() -> enacl_nif:crypto_box_NONCEBYTES(). +secretbox(Msg, Nonce, Key) -> + enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key). + +secretbox_open(CipherText, Nonce, Key) -> + case enacl_nif:crypto_secretbox_open([s_box_zerobytes(), CipherText], Nonce, Key) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end. + +secretbox_nonce_size() -> + enacl_nif:crypto_secretbox_NONCEBYTES(). + +secretbox_key_size() -> + enacl_nif:crypto_secretbox_KEYBYTES(). + %% Helpers -zerobytes() -> +p_zerobytes() -> binary:copy(<<0>>, enacl_nif:crypto_box_ZEROBYTES()). -box_zerobytes() -> +p_box_zerobytes() -> binary:copy(<<0>>, enacl_nif:crypto_box_BOXZEROBYTES()). + +s_zerobytes() -> + binary:copy(<<0>>, enacl_nif:crypto_secretbox_ZEROBYTES()). + +s_box_zerobytes() -> + binary:copy(<<0>>, enacl_nif:crypto_secretbox_BOXZEROBYTES()). diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index f17271b..7b22a44 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -12,6 +12,16 @@ crypto_box_BOXZEROBYTES/0 ]). +%% Secret key crypto +-export([ + crypto_secretbox/3, + crypto_secretbox_open/3, + crypto_secretbox_NONCEBYTES/0, + crypto_secretbox_ZEROBYTES/0, + crypto_secretbox_BOXZEROBYTES/0, + crypto_secretbox_KEYBYTES/0 +]). + %% Miscellaneous helper functions -export([ crypto_hash/1 @@ -39,6 +49,14 @@ crypto_box_keypair() -> not_loaded(). crypto_box(_PaddedMsg, _Nonce, _PK, _SK) -> not_loaded(). crypto_box_open(_CipherText, _Nonce, _PK, _SK) -> not_loaded(). +crypto_secretbox_NONCEBYTES() -> not_loaded(). +crypto_secretbox_ZEROBYTES() -> not_loaded(). +crypto_secretbox_KEYBYTES() -> not_loaded(). +crypto_secretbox_BOXZEROBYTES() -> not_loaded(). + +crypto_secretbox(_CipherText, _Nonce, _Key) -> not_loaded(). +crypto_secretbox_open(_CipherText, _Nonce, _Key) -> not_loaded(). + crypto_hash(Input) when is_binary(Input) -> not_loaded().