diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 2d63b0e..ffcc4ef 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -322,7 +322,7 @@ ERL_NIF_TERM enif_crypto_box_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const a static ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary k, pk, sk; - + if ( (argc != 2) || (!enif_inspect_binary(env, argv[0], &pk)) || @@ -331,20 +331,20 @@ ERL_NIF_TERM enif_crypto_box_beforenm(ErlNifEnv *env, int argc, ERL_NIF_TERM con (sk.size != crypto_box_SECRETKEYBYTES)) { return enif_make_badarg(env); } - + if (!enif_alloc_binary(crypto_box_BEFORENMBYTES, &k)) { return nacl_error_tuple(env, "alloc_failed"); } - + crypto_box_beforenm(k.data, pk.data, sk.data); - + return enif_make_binary(env, &k); } static ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary result, m, nonce, k; - + if ( (argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) || @@ -355,13 +355,13 @@ ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM cons (k.size != crypto_box_BEFORENMBYTES)) { return enif_make_badarg(env); } - + if (!enif_alloc_binary(m.size, &result)) { return nacl_error_tuple(env, "alloc_failed"); } - + crypto_box_afternm(result.data, m.data, m.size, nonce.data, k.data); - + return enif_make_sub_binary( env, enif_make_binary(env, &result), @@ -372,7 +372,7 @@ ERL_NIF_TERM enif_crypto_box_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM cons static ERL_NIF_TERM enif_crypto_box_open_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary result, m, nonce, k; - + if ( (argc != 3) || (!enif_inspect_iolist_as_binary(env, argv[0], &m)) || @@ -383,16 +383,16 @@ ERL_NIF_TERM enif_crypto_box_open_afternm(ErlNifEnv *env, int argc, ERL_NIF_TERM (k.size != crypto_box_BEFORENMBYTES)) { return enif_make_badarg(env); } - + if (!enif_alloc_binary(m.size, &result)) { return nacl_error_tuple(env, "alloc_failed"); } - + if (0 != crypto_box_open_afternm(result.data, m.data, m.size, nonce.data, k.data)) { enif_release_binary(&result); return nacl_error_tuple(env, "failed_verification"); } - + return enif_make_sub_binary( env, enif_make_binary(env, &result), @@ -432,6 +432,11 @@ ERL_NIF_TERM enif_crypto_sign_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM con return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk)); } +/* +int crypto_sign(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk); + */ static ERL_NIF_TERM enif_crypto_sign(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary m, sk, sm; @@ -457,6 +462,11 @@ ERL_NIF_TERM enif_crypto_sign(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[ return enif_make_sub_binary(env, enif_make_binary(env, &sm), 0, smlen); } +/* +int crypto_sign_open(unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + */ static ERL_NIF_TERM enif_crypto_sign_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary m, sm, pk; @@ -485,6 +495,65 @@ ERL_NIF_TERM enif_crypto_sign_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const } } +/* +int crypto_sign_detached(unsigned char *sig, unsigned long long *siglen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk); + */ +static +ERL_NIF_TERM enif_crypto_sign_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary m, sk, sig; + unsigned long long siglen; + + if ( + (argc != 2) || + (!enif_inspect_iolist_as_binary(env, argv[0], &m)) || + (!enif_inspect_binary(env, argv[1], &sk))) { + return enif_make_badarg(env); + } + + if (sk.size != crypto_sign_SECRETKEYBYTES) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(crypto_sign_BYTES, &sig)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + crypto_sign_detached(sig.data, &siglen, m.data, m.size, sk.data); + + return enif_make_sub_binary(env, enif_make_binary(env, &sig), 0, siglen); +} + +/* +int crypto_sign_verify_detached(const unsigned char *sig, + const unsigned char *m, + unsigned long long mlen, + const unsigned char *pk); + */ +static +ERL_NIF_TERM enif_crypto_sign_verify_detached(ErlNifEnv* env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary m, sig, pk; + + if ( + (argc != 3) || + (!enif_inspect_binary(env, argv[0], &sig)) || + (!enif_inspect_iolist_as_binary(env, argv[1], &m)) || + (!enif_inspect_binary(env, argv[2], &pk))) { + return enif_make_badarg(env); + } + + if (pk.size != crypto_sign_PUBLICKEYBYTES) { + return enif_make_badarg(env); + } + + if (0 == crypto_sign_verify_detached(sig.data, m.data, m.size, pk.data)) { + return enif_make_atom(env, "true"); + } else { + return enif_make_atom(env, "false"); + } +} + /* Secret key cryptography */ static @@ -770,17 +839,17 @@ ERL_NIF_TERM enif_randombytes(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[ { size_t req_size; ErlNifBinary result; - + if ((argc != 1) || (!enif_get_uint64(env, argv[0], &req_size))) { return enif_make_badarg(env); } - + if (!enif_alloc_binary(req_size, &result)) { return nacl_error_tuple(env, "alloc_failed"); } - + randombytes(result.data, result.size); - + return enif_make_binary(env, &result); } @@ -841,7 +910,7 @@ 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)) || @@ -849,13 +918,13 @@ ERL_NIF_TERM enif_scramble_block_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const (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); } @@ -886,6 +955,9 @@ static ErlNifFunc nif_funcs[] = { {"crypto_sign_open_b", 2, enif_crypto_sign_open}, {"crypto_sign_open", 2, enif_crypto_sign_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_sign_detached", 2, enif_crypto_sign_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached, 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}, @@ -931,7 +1003,7 @@ static ErlNifFunc nif_funcs[] = { {"randombytes_b", 1, enif_randombytes}, {"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 29fb3aa..148b32f 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,6 +2,13 @@ -include_lib("eqc/include/eqc.hrl"). -compile(export_all). +%% dummy test property +prop_append() -> + ?FORALL({Xs,Ys},{list(int()),list(int())}, + lists:reverse(Xs++Ys) + == + lists:reverse(Ys) ++ lists:reverse(Xs)). + non_byte_int() -> oneof([ ?LET(N, nat(), -(N+1)), @@ -10,7 +17,7 @@ non_byte_int() -> g_iolist() -> ?SIZED(Sz, g_iolist(Sz)). - + g_iolist(0) -> fault( oneof([ @@ -30,7 +37,7 @@ g_iolist(N) -> {1, g_iolist(0)}, {N, ?LAZY(list(oneof([char(), binary(), g_iolist(N div 4)])))} ])). - + g_iodata() -> fault( oneof([elements([a,b,c]), real()]), @@ -60,12 +67,12 @@ g_binary_bad(Sz) -> {1, int()}, {1, g_iodata()} ]). - + v_binary(Sz, N) when is_binary(N) -> byte_size(N) == Sz; v_binary(_, _) -> false. - + %% Typical generators based on the binaries nonce() -> g_binary(enacl:box_nonce_size()). nonce_valid(N) -> v_binary(enacl:box_nonce_size(), N). @@ -73,7 +80,7 @@ nonce_valid(N) -> v_binary(enacl:box_nonce_size(), N). %% Generator of natural numbers g_nat() -> fault(g_nat_bad(), nat()). - + g_nat_bad() -> oneof([ elements([a,b,c]), @@ -242,7 +249,7 @@ prop_afternm_correct() -> end end end). - + %% SIGNATURES %% ---------- @@ -279,12 +286,12 @@ sign_keypair_public_valid(#{ public := Public }) when is_binary(Public) -> byte_size(Public) == enacl:sign_keypair_public_size(); sign_keypair_public_valid(_) -> false. - + sign_keypair_secret_valid(#{ secret := Secret }) when is_binary(Secret) -> byte_size(Secret) == enacl:sign_keypair_secret_size(); sign_keypair_secret_valid(_) -> false. - + sign_keypair_valid(KP) -> sign_keypair_public_valid(KP) andalso sign_keypair_secret_valid(KP). @@ -340,7 +347,7 @@ prop_sign_open() -> false -> badargs(fun() -> enacl:sign_open(SignMsg, PK) end) end)). - + %% CRYPTO SECRET BOX %% ------------------------------- @@ -667,3 +674,32 @@ badargs(Thunk) -> catch error:badarg -> true end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Joel Test Blobs + +test_basic_signing() -> + #{ public := PK0, secret := SK0 } = enacl:sign_keypair(), + #{ public := PK1, secret := SK1 } = enacl:sign_keypair(), + MSG0 = <<"This is super s3Kr3t, srsly!">>, + [ + %% (+) Sign and open using valid keypair + case enacl:sign_open(enacl:sign(MSG0, SK0), PK0) of + {ok,MSG1} -> MSG0==MSG1; + _ -> false + end + , %% (-) Sign and open using invalid keypair + case enacl:sign_open(enacl:sign(MSG0, SK0), PK1) of + {error,failed_verification} -> true; + _ -> false + end + , %% (+) Detached mode sig and verify + { enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), MSG0, PK0) + , enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK1), MSG0, PK1) + } + , %% (-) Incorrect sigs/PKs/messages given during verify + { false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), MSG0, PK1) + , false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK1), MSG0, PK0) + , false == enacl:sign_verify_detached(enacl:sign_detached(MSG0, SK0), <<"bzzt">>, PK0) + } + ]. diff --git a/src/enacl.erl b/src/enacl.erl index 459d3c5..2f09c08 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -29,12 +29,14 @@ box_public_key_bytes/0, box_secret_key_bytes/0, box_beforenm_bytes/0, - + sign_keypair_public_size/0, sign_keypair_secret_size/0, sign_keypair/0, sign/2, - sign_open/2 + sign_open/2, + sign_detached/2, + sign_verify_detached/3 ]). %% Secret key crypto @@ -251,7 +253,7 @@ box_beforenm(PK, SK) -> R = enacl_nif:crypto_box_beforenm(PK, SK), erlang:bump_reductions(?BOX_BEFORENM_REDUCTIONS), R. - + %% @doc box_afternm/3 works like `box/4' but uses a precomputed key %% %% Calling `box_afternm(M, Nonce, K)' for a precomputed key `K = box_beforenm(PK, SK)' works exactly as @@ -323,7 +325,7 @@ box_beforenm_bytes() -> %% @private sign_keypair_public_size() -> enacl_nif:crypto_sign_PUBLICKEYBYTES(). - + %% @private sign_keypair_secret_size() -> enacl_nif:crypto_sign_SECRETKEYBYTES(). @@ -380,6 +382,29 @@ sign_open(SM, PK) -> end end. +%% @doc sign_detached/2 computes a digital signature given a message and a secret key. +%% +%% Given a message `M' and a secret key `SK' the function will compute the digital signature `DS'. +%% @end +-spec sign_detached(M, SK) -> DS + when + M :: iodata(), + SK :: binary(), + DS :: binary(). +sign_detached(M, SK) -> enacl_nif:crypto_sign_detached(M, SK). + +%% @doc sign_verify_detached/3 verifies the given signature against the given +%% message for the given public key. +%% +%% Given a signature `SIG', a message `M', and a public key `PK', the function computes +%% true iff the `SIG' is valid for `M' and `PK'. +-spec sign_verify_detached(SIG, M, PK) -> boolean() + when + SIG :: binary(), + M :: iodata(), + PK :: binary(). +sign_verify_detached(SIG, M, PK) -> enacl_nif:crypto_sign_verify_detached(SIG, M, PK). + %% @private -spec box_secret_key_bytes() -> pos_integer(). box_secret_key_bytes() -> @@ -433,7 +458,7 @@ secretbox_open(CipherText, Nonce, Key) -> Bin when is_binary(Bin) -> {ok, Bin} end end. - + %% @doc secretbox_nonce_size/0 returns the size of the secretbox nonce %% %% When encrypting with a secretbox, the nonce must have this size diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 7748856..df6796e 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -31,7 +31,11 @@ crypto_sign_b/2, crypto_sign_keypair/0, crypto_sign_open/2, - crypto_sign_open_b/2 + crypto_sign_open_b/2, + + crypto_sign_detached/2, + crypto_sign_verify_detached/3 + ]). %% Secret key crypto @@ -144,6 +148,9 @@ crypto_sign_b(_M, _SK) -> erlang:nif_error(nif_not_loaded). crypto_sign_open(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded). crypto_sign_open_b(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded). +crypto_sign_detached(_M, _SK) -> erlang:nif_error(nif_not_loaded). +crypto_sign_verify_detached(_SIG, _M, _PK) -> erlang:nif_error(nif_not_loaded). + crypto_secretbox_NONCEBYTES() -> erlang:nif_error(nif_not_loaded). crypto_secretbox_ZEROBYTES() -> erlang:nif_error(nif_not_loaded). crypto_secretbox_KEYBYTES() -> erlang:nif_error(nif_not_loaded).