From 92109eb354da2bf12277e66e7af13281c20d29c5 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Wed, 12 Aug 2015 23:11:41 +0200 Subject: [PATCH] Improve and verify sign_*_detached functions. Provide non-dirty-scheduler variants for small strings, accurately bump reductions for these strings. While here, provide EQC test cases for the two functions. --- c_src/enacl_nif.c | 7 ++++-- eqc_test/enacl_eqc.erl | 56 ++++++++++++++++++++++++++++++++++++------ src/enacl.erl | 18 +++++++++++--- src/enacl_nif.erl | 7 +++++- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 947205e..6c101b4 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -1020,8 +1020,11 @@ 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_sign_detached_b", 2, enif_crypto_sign_detached}, + {"crypto_sign_detached", 2, enif_crypto_sign_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_sign_verify_detached_b", 3, enif_crypto_sign_verify_detached}, + {"crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"crypto_box_SEALBYTES", 0, enif_crypto_box_SEALBYTES}, {"crypto_box_seal", 2, enif_crypto_box_seal, ERL_NIF_DIRTY_JOB_CPU_BOUND}, diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 148b32f..f36c2a1 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,13 +2,6 @@ -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)), @@ -295,6 +288,22 @@ sign_keypair_secret_valid(_) -> false. sign_keypair_valid(KP) -> sign_keypair_public_valid(KP) andalso sign_keypair_secret_valid(KP). +prop_sign_detached() -> + ?FORALL({Msg, KeyPair}, + {fault_rate(1, 40, g_iodata()), + fault_rate(1, 40, sign_keypair())}, + begin + case v_iodata(Msg) andalso sign_keypair_secret_valid(KeyPair) of + true -> + #{ secret := Secret } = KeyPair, + enacl:sign_detached(Msg, Secret), + true; + false -> + #{ secret := Secret } = KeyPair, + badargs(fun() -> enacl:sign_detached(Msg, Secret) end) + end + end). + prop_sign() -> ?FORALL({Msg, KeyPair}, {fault_rate(1, 40, g_iodata()), @@ -322,17 +331,50 @@ signed_message_good(M) -> pk -> {{invalid, SM}, binary(byte_size(PK))} end)}]). +signed_message_good_d(M) -> + #{ public := PK, secret := SK} = enacl:sign_keypair(), + Sig = enacl:sign_detached(M, SK), + frequency([ + {3, return({{valid, Sig}, PK})}, + {1, ?LET(X, elements([sm, pk]), + case X of + sm -> {{invalid, binary(byte_size(Sig))}, PK}; + pk -> {{invalid, Sig}, binary(byte_size(PK))} + end)}]). + signed_message_bad() -> Sz = enacl:sign_keypair_public_size(), {binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}. +signed_message_bad_d() -> + Sz = enacl:sign_keypair_public_size(), + {binary(), oneof([a, int(), ?SUCHTHAT(B, binary(Sz), byte_size(B) /= Sz)])}. + signed_message(M) -> fault(signed_message_bad(), signed_message_good(M)). +signed_message_d(M) -> + fault(signed_message_bad(), signed_message_good(M)). + signed_message_valid({valid, _}, _) -> true; signed_message_valid({invalid, _}, _) -> true; signed_message_valid(_, _) -> false. +prop_sign_detached_open() -> + ?FORALL(Msg, g_iodata(), + ?FORALL({SignMsg, PK}, signed_message_d(Msg), + case v_iodata(Msg) andalso signed_message_valid(SignMsg, PK) of + true -> + case SignMsg of + {valid, Sig} -> + equals({ok, Msg}, enacl:sign_verify_detached(Sig, Msg, PK)); + {invalid, Sig} -> + equals({error, failed_verification}, enacl:sign_verify_detached(Sig, Msg, PK)) + end; + false -> + badargs(fun() -> enacl:sign_verify_detached(SignMsg, Msg, PK) end) + end)). + prop_sign_open() -> ?FORALL(Msg, g_iodata(), ?FORALL({SignMsg, PK}, signed_message(Msg), diff --git a/src/enacl.erl b/src/enacl.erl index b29adbf..1eaa094 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -395,7 +395,13 @@ sign_open(SM, PK) -> M :: iodata(), SK :: binary(), DS :: binary(). -sign_detached(M, SK) -> enacl_nif:crypto_sign_detached(M, SK). +sign_detached(M, SK) -> + case iolist_size(M) of + K when K =< ?SIGN_SIZE -> + bump(enacl_nif:crypto_sign_detached_b(M, SK), ?SIGN_REDUCTIONS, ?SIGN_SIZE, K); + _ -> + enacl_nif:crypto_sign_detached(M, SK) + end. %% @doc sign_verify_detached/3 verifies the given signature against the given %% message for the given public key. @@ -408,8 +414,14 @@ sign_detached(M, SK) -> enacl_nif:crypto_sign_detached(M, SK). M :: iodata(), PK :: binary(). sign_verify_detached(SIG, M, PK) -> - case enacl_nif:crypto_sign_verify_detached(SIG, M, PK) of - true -> {ok, M}; + SignRes = case iolist_size(M) of + K when K =< ?SIGN_SIZE -> + bump(enacl_nif:crypto_sign_verify_detached_b(SIG, M, PK), ?SIGN_REDUCTIONS, ?SIGN_SIZE, K); + _ -> + enacl_nif:crypto_sign_detached(SIG, M, PK) + end, + case SignRes of + true -> {ok, M}; false -> {error, failed_verification} end. diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index b86892c..a66c9f0 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -34,7 +34,9 @@ crypto_sign_open_b/2, crypto_sign_detached/2, + crypto_sign_detached_b/2, crypto_sign_verify_detached/3, + crypto_sign_verify_detached_b/3, crypto_box_seal/2, crypto_box_seal_open/3, @@ -153,7 +155,10 @@ 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_sign_detached_b(_M, _SK) -> erlang:nif_error(nif_not_loaded). + +crypto_sign_verify_detached(_Sig, _M, _PK) -> erlang:nif_error(nif_not_loaded). +crypto_sign_verify_detached_b(_Sig, _M, _PK) -> erlang:nif_error(nif_not_loaded). crypto_box_seal(_Msg, _PK) -> erlang:nif_error(nif_not_loaded). crypto_box_seal_open(_CipherText, _PK, _SK) -> erlang:nif_error(nif_not_loaded).