diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 8bbd417..5b63ffa 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -578,6 +578,81 @@ ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[ return enif_make_binary(env, &result); } +/* Various other helper functions */ + +void uint64_pack(unsigned char *y, ErlNifUInt64 x) +{ + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; + *y++ = x; x >>= 8; +} + +ErlNifUInt64 +uint64_unpack(const unsigned char *x) +{ + ErlNifUInt64 result; + + result = x[7]; + result <<= 8; result |= x[6]; + result <<= 8; result |= x[5]; + result <<= 8; result |= x[4]; + result <<= 8; result |= x[3]; + result <<= 8; result |= x[2]; + result <<= 8; result |= x[1]; + result <<= 8; result |= x[0]; + return result; +} + +int +crypto_block(unsigned char *out, const unsigned char *in, const unsigned char *k) +{ + ErlNifUInt64 v0 = uint64_unpack(in + 0); + ErlNifUInt64 v1 = uint64_unpack(in + 8); + ErlNifUInt64 k0 = uint64_unpack(k + 0); + ErlNifUInt64 k1 = uint64_unpack(k + 8); + ErlNifUInt64 k2 = uint64_unpack(k + 16); + ErlNifUInt64 k3 = uint64_unpack(k + 24); + ErlNifUInt64 sum = 0; + ErlNifUInt64 delta = 0x9e3779b97f4a7c15; + int i; + for (i = 0;i < 32;++i) { + sum += delta; + v0 += ((v1<<7) + k0) ^ (v1 + sum) ^ ((v1>>12) + k1); + v1 += ((v0<<16) + k2) ^ (v0 + sum) ^ ((v0>>8) + k3); + } + uint64_pack(out + 0,v0); + uint64_pack(out + 8,v1); + + return 0; +} + +static +ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) +{ + ErlNifBinary in, out, key; + + if ( + (argc != 2) || + (!enif_inspect_binary(env, argv[0], &in)) || + (!enif_inspect_binary(env, argv[1], &key)) || + (in.size != 16) || (key.size != 32)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(in.size, &out)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + crypto_block(out.data, in.data, key.data); + + return enif_make_binary(env, &out); +} + /* Tie the knot to the Erlang world */ static ErlNifFunc nif_funcs[] = { {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES}, @@ -635,7 +710,9 @@ static ErlNifFunc nif_funcs[] = { {"crypto_verify_32", 2, enif_crypto_verify_32}, {"randombytes_b", 1, enif_randombytes}, - {"randombytes", 1, enif_randombytes, ERL_NIF_DIRTY_JOB_CPU_BOUND} + {"randombytes", 1, enif_randombytes, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + + {"scramble_block_16", 2, enif_scramble_block_16} }; diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 689cc71..899e22d 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,21 +2,26 @@ -include_lib("eqc/include/eqc.hrl"). -compile(export_all). -nonce_good() -> - Sz = enacl:box_nonce_size(), - binary(Sz). +%% Generator for binaries of a given size with different properties and fault injection: +g_binary(Sz) -> + fault(g_binary_bad(Sz), g_binary_good(Sz)). -nonce_bad() -> - Sz = enacl:box_nonce_size(), - oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]). +g_binary_good(Sz) when Sz =< 32 -> binary(Sz); +g_binary_good(Sz) -> eqc_gen:largebinary(Sz). -nonce_valid(N) when is_binary(N) -> - Sz = enacl:box_nonce_size(), +g_binary_bad(Sz) -> + frequency([ + {5, ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)}, + {1, elements([a, b])}, + {1, int()} + ]). + +v_binary(Sz, N) when is_binary(N) -> byte_size(N) == Sz; -nonce_valid(_) -> false. +v_binary(_, _) -> false. -nonce() -> - fault(nonce_bad(), nonce_good()). +nonce() -> g_binary(enacl:box_nonce_size()). +nonce_valid(N) -> v_binary(enacl:box_nonce_size(), N). keypair_good() -> #{ public := PK, secret := SK} = enacl:box_keypair(), @@ -426,24 +431,13 @@ prop_onetime_auth_verify_correct() -> %% HASHING %% --------------------------- diff_pair(Sz) -> - ?SUCHTHAT({X, Y}, {binary(Sz), binary(Sz)}, + ?SUCHTHAT({X, Y}, {g_binary(Sz), g_binary(Sz)}, X /= Y). -data_bad() -> - oneof([return(a), nat()]). - -data_good(Sz) -> binary(Sz). - -data(Sz) -> - fault(data_bad(), data_good(Sz)). - -data_valid(B) when is_binary(B) -> true; -data_valid(_B) -> false. - prop_crypto_hash_eq() -> ?FORALL(Sz, oneof([1, 128, 1024, 1024*4]), - ?FORALL(X, data(Sz), - case data_valid(X) of + ?FORALL(X, g_binary(Sz), + case is_binary(X) of true -> equals(enacl:hash(X), enacl:hash(X)); false -> try diff --git a/src/enacl_ext.erl b/src/enacl_ext.erl new file mode 100644 index 0000000..b1f2615 --- /dev/null +++ b/src/enacl_ext.erl @@ -0,0 +1,25 @@ +%%% @doc module enacl_ext implements various enacl extensions. +%%%

None of the extensions listed here are part of the official NaCl library. +%%% Things may be removed without further notice if it suddenly ends up being +%%% better to do something differently than the solution given here. +%%%

+-module(enacl_ext). + +-export([ + scramble_block_16/2 +]). + +%% @doc scramble_block_16/2 scrambles (encrypt) a block under a given key +%% The rules are that the block is 16 bytes and the key is 32 bytes. The block +%% is scrambled by means of the (secret) key. This makes it impossible for an +%% attacker to understand the original input for the scrambling. The intention +%% of this method is to protect counters from leaking to the outside world, by +%% scrambling them before they leave the system. +%% +%% Scrambling is done by means of the TEA algorithm (Tiny Encryption Algorithm) +%% It has known weaknesses and should probably not be used long-term going +%% forward, but CurveCP currently uses it for nonce scrambling. +%% @end +-spec scramble_block_16(binary(), binary()) -> binary(). +scramble_block_16(Block, Key) -> + enacl_nif:scramble_block_16(Block, Key). diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 9cb3e47..5e68a6c 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -78,6 +78,11 @@ randombytes_b/1 ]). +%% Undocumented features :> +-export([ + scramble_block_16/2 +]). + -on_load(init/0). init() -> @@ -153,3 +158,4 @@ crypto_verify_32(_X, _Y) -> not_loaded(). randombytes(_RequestedSize) -> not_loaded(). randombytes_b(_RequestedSize) -> not_loaded(). +scramble_block_16(_Block, _Key) -> not_loaded().