Add EdDSA, EcDSA + benchmarks and some other improvments

This commit is contained in:
Hans Svensson 2022-01-24 11:23:25 +01:00
parent e0ab81c99d
commit 3cee7296b2
8 changed files with 409 additions and 20 deletions

View File

@ -99,18 +99,28 @@ prop_scalar_mul_noclamp() ->
equals(E, ecu_ed25519:compress(P)) equals(E, ecu_ed25519:compress(P))
end). end).
xprop_scalar_enacl() -> prop_arithmetics1() ->
?FORALL(S, gen_scalar(), ?FORALL({P1, P2, P3}, {gen_point(), gen_point(), gen_point()},
begin begin
_P = enacl:crypto_ed25519_scalarmult_base(S), Res1 = ecu_ed25519:p_add(P1, ecu_ed25519:p_add(P2, P3)),
true Res2 = ecu_ed25519:p_add(ecu_ed25519:p_add(P1, P2), P3),
equal_pts(Res1, Res2)
end). end).
xprop_scalar_ecu() -> prop_arithmetics2() ->
?FORALL(S, gen_scalar(), ?FORALL({P1, P2}, {gen_point(), gen_point()},
begin begin
_P = ecu_ed25519:scalar_mul_base(S), Res1 = ecu_ed25519:p_sub(ecu_ed25519:p_add(P1, P2), P2),
true equal_pts(P1, Res1)
end).
prop_dbl() ->
?FORALL(P, gen_point(),
begin
A = ecu_ed25519:p_add(P, P),
B = ecu_ed25519:p_dbl(P),
?WHENFAIL(eqc:format("~p\n /=\n~p\n", [ecu_ed25519:to_affine(A), ecu_ed25519:to_affine(B)]),
ecu_ed25519:pt_eq(A, B))
end). end).
even(<<B:31/bytes, _:1, B2:7>>) -> <<B/bytes, 0:1, B2:7>>. even(<<B:31/bytes, _:1, B2:7>>) -> <<B/bytes, 0:1, B2:7>>.

70
eqc/eddsa_eqc.erl Normal file
View File

@ -0,0 +1,70 @@
%%% Author : Hans Svensson
%%% Description :
%%% Created : 19 Jan 2022 by Hans Svensson
-module(eddsa_eqc).
-compile([export_all, nowarn_export_all]).
-include_lib("eqc/include/eqc.hrl").
%% Let's use enacl/libsodium as the oracle
-define(N, 16#1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED).
-define(KP, #{public => <<161,254,128,151,126,253,139,99,47,29,229,140,67,224,50,78,70,156,225,182,242,171,89,114,47,163,254,192,59,35,148,234>>, secret => <<102,73,74,74,245,130,53,139,149,247,67,138,211,86,72,227,20,43,6,39,134,133,215,10,3,159,123,152,144,208,176,138,161,254,128,151,126,253,139,99,47,29,229,140,67,224,50,78,70,156,225,182,242,171,89,114,47,163,254,192,59,35,148,234>>}).
gen_large_n() ->
?LET(<<X:512>>, binary(64), 1 + (X rem (?N - 1))).
gen_scalar() ->
?LET(N, gen_large_n(), <<N:256/little>>).
gen_point() ->
?LET(S, gen_scalar(), enacl:crypto_ed25519_scalarmult_base_noclamp(S)).
prop_keypair_seed() ->
?FORALL(Seed, binary(32),
begin
KP1 = enacl:sign_seed_keypair(Seed),
KP2 = ecu_eddsa:sign_seed_keypair(Seed),
equals(KP1, KP2)
end).
prop_sign() ->
?FORALL({Priv, Msg}, {binary(32), binary(48)},
begin
#{secret := SK} = enacl:sign_seed_keypair(Priv),
Sig1 = enacl:sign(Msg, SK),
Sig2 = ecu_eddsa:sign(Msg, SK),
equals(Sig1, Sig2)
end).
prop_sign_open() ->
?FORALL({Priv, Msg}, {noshrink(binary(32)), noshrink(binary(48))},
begin
#{secret := SK, public := Pub} = enacl:sign_seed_keypair(Priv),
Sig = enacl:sign(Msg, SK),
Res1 = enacl:sign_open(Sig, Pub),
Res2 = ecu_eddsa:sign_open(Sig, Pub),
equals(Res1, Res2)
end).
prop_sign_detached() ->
?FORALL({Priv, Msg}, {binary(32), binary(48)},
begin
#{secret := SK} = enacl:sign_seed_keypair(Priv),
Sig1 = enacl:sign_detached(Msg, SK),
Sig2 = ecu_eddsa:sign_detached(Msg, SK),
equals(Sig1, Sig2)
end).
prop_sign_verify_detached() ->
?FORALL({Priv, Msg}, {noshrink(binary(32)), noshrink(binary(48))},
begin
#{secret := SK, public := Pub} = enacl:sign_seed_keypair(Priv),
Sig = enacl:sign_detached(Msg, SK),
Res1 = enacl:sign_verify_detached(Sig, Msg, Pub),
Res2 = ecu_eddsa:sign_verify_detached(Sig, Msg, Pub),
equals(Res1, Res2)
end).

View File

@ -7,6 +7,7 @@
{deps, {deps,
[{sha3, {git, "https://github.com/aeternity/erlang-sha3", {ref, "b5f27a2"}}}]}. [{sha3, {git, "https://github.com/aeternity/erlang-sha3", {ref, "b5f27a2"}}}]}.
{profiles, [{test, [{deps, [{enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "01dd0c2"}}}]}]}, {profiles, [{test, [{deps, [{enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "01dd0c2"}}},
{ecrecover, {git, "https://github.com/aeternity/ecrecover.git", {ref, "74b7816"}}}]}]},
{eqc, [{deps, [{enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "01dd0c2"}}}]}]} {eqc, [{deps, [{enacl, {git, "https://github.com/aeternity/enacl.git", {ref, "01dd0c2"}}}]}]}
]}. ]}.

View File

@ -4,7 +4,7 @@
{registered, []}, {registered, []},
{applications, [kernel, stdlib]}, {applications, [kernel, stdlib]},
{env,[]}, {env,[]},
{modules, [ecu_secp256k1, ecu_misc]}, {modules, [ecu_secp256k1, ecu_ed25519, ecu_ecdsa, ecu_eddsa, ecu_crypto, ecu_misc]},
{licenses, ["MIT"]}, {licenses, ["MIT"]},
{links, []} {links, []}
]}. ]}.

View File

@ -36,7 +36,8 @@
-export([on_curve/1, p/0, n/0, pt_eq/2, -export([on_curve/1, p/0, n/0, pt_eq/2,
scalar_mul/2, scalar_mul_base/1, scalar_mul/2, scalar_mul_base/1,
scalar_mul_noclamp/2, scalar_mul_base_noclamp/1, scalar_mul_noclamp/2, scalar_mul_base_noclamp/1,
p_add/2, scalar_reduce/1,
p_add/2, p_sub/2, p_neg/1, p_dbl/1,
compress/1, decompress/1, compress/1, decompress/1,
f_add/2, f_mul/2, f_sub/2, f_div/2, f_inv/1, f_add/2, f_mul/2, f_sub/2, f_div/2, f_inv/1,
s_add/2, s_mul/2, s_sub/2, s_div/2, s_inv/1]). s_add/2, s_mul/2, s_sub/2, s_div/2, s_inv/1]).
@ -92,12 +93,18 @@ decompress(<<Y0:256/little>>) ->
false -> to_ext_hom({?P - X, Y}) false -> to_ext_hom({?P - X, Y})
end. end.
p_neg({X, Y}) -> {?P - X, Y};
p_neg({X, Y, Z, T}) -> {?P - X, Y, Z, ?P - T};
p_neg(P) -> p_neg(to_ext_hom(P)).
p_sub(P1, P2) -> p_add(P1, p_neg(P2)).
-spec p_add(X :: pt(), Y :: pt()) -> pt_hom_ext(). -spec p_add(X :: pt(), Y :: pt()) -> pt_hom_ext().
p_add({X1, Y1, Z1, T1}, {X2, Y2, Z2, T2}) -> p_add({X1, Y1, Z1, T1}, {X2, Y2, Z2, T2}) ->
A = ?MUL(?SUB(Y1, X1), ?SUB(Y2, X2)), A = ?MUL(?SUB(Y1, X1), ?SUB(Y2, X2)),
B = ?MUL(?ADD(Y1, X1), ?ADD(Y2, X2)), B = ?MUL(?ADD(Y1, X1), ?ADD(Y2, X2)),
C = ?MUL(?MUL(T1, T2), ?MUL(2, ?D)), C = ?MUL(?MUL(T1, T2), 2 * ?D),
D = ?MUL(2, ?MUL(Z1, Z2)), D = ?MUL(2 * Z1, Z2),
E = ?SUB(B, A), E = ?SUB(B, A),
F = ?SUB(D, C), F = ?SUB(D, C),
G = ?ADD(D, C), G = ?ADD(D, C),
@ -106,9 +113,23 @@ p_add({X1, Y1, Z1, T1}, {X2, Y2, Z2, T2}) ->
p_add(P1, P2) -> p_add(P1, P2) ->
p_add(to_ext_hom(P1), to_ext_hom(P2)). p_add(to_ext_hom(P1), to_ext_hom(P2)).
p_dbl({X, Y, Z, _T}) ->
A = ?MUL(X, X),
B = ?MUL(Y, Y),
C = ?MUL(2 * Z, Z),
D = ?P - A,
XY = X + Y,
E = ?SUB(?MUL(XY, XY), ?ADD(A, B)),
G = ?ADD(D, B),
F = ?SUB(G, C),
H = ?SUB(D, B),
{?MUL(E, F), ?MUL(G, H), ?MUL(F, G), ?MUL(E, H)};
p_dbl(P) ->
p_dbl(to_ext_hom(P)).
-spec scalar_mul_base(Scalar :: scalar() | binary()) -> pt_hom_ext(). -spec scalar_mul_base(Scalar :: scalar() | binary()) -> pt_hom_ext().
scalar_mul_base(<<K:256/little>>) -> scalar_mul_base(<<K:256/little>>) ->
scalar_mul_base(K); scalar_mul_(clamp(K), ?GE);
scalar_mul_base(K) when is_integer(K), K >= 0, K < ?N -> scalar_mul_base(K) when is_integer(K), K >= 0, K < ?N ->
scalar_mul_(clamp(K), ?GE). scalar_mul_(clamp(K), ?GE).
@ -152,21 +173,24 @@ f_inv(A) ->
f_pow(A, ?P - 2). f_pow(A, ?P - 2).
%% Arithmetics in curve group order N %% Arithmetics in curve group order N
s_add(A, B) -> (A + B) rem ?N. s_add(<<A:256/little>>, <<B:256/little>>) -> <<((A + B) rem ?N):256/little>>.
s_mul(A, B) -> (A * B) rem ?N. s_mul(<<A:256/little>>, <<B:256/little>>) -> <<((A * B) rem ?N):256/little>>.
s_sub(A, B) -> (A - B + ?N) rem ?N. s_sub(<<A:256/little>>, <<B:256/little>>) -> <<((A - B + ?N) rem ?N):256/little>>.
s_div(A, B) -> s_mul(A, s_inv(B)). s_div(A, B) -> s_mul(A, s_inv(B)).
s_inv(A) -> s_inv(<<A:256/little>>) ->
{1, S, _T} = ecu_misc:eea(A, ?N), {1, S, _T} = ecu_misc:eea(A, ?N),
(S + ?N) rem ?N. <<((S + ?N) rem ?N):256/little>>.
scalar_reduce(<<S:512/little>>) ->
<<(S rem ?N):256/little>>.
%% --- internal functions %% --- internal functions
scalar_mul_(0, _P) -> {0, 1, 1, 0}; scalar_mul_(0, _P) -> {0, 1, 1, 0};
scalar_mul_(1, P) -> P; scalar_mul_(1, P) -> P;
scalar_mul_(K, P) -> scalar_mul_(K, P) ->
case K rem 2 of case K rem 2 of
0 -> scalar_mul_(K div 2, p_add(P, P)); 0 -> scalar_mul_(K div 2, p_dbl(P));
1 -> p_add(P, scalar_mul_(K - 1, P)) 1 -> p_add(P, scalar_mul_(K - 1, P))
end. end.

140
src/ecu_eddsa.erl Normal file
View File

@ -0,0 +1,140 @@
%%% File : ecu_eddsa.erl
%%% Author : Hans Svensson
%%% Description : eddsa functionality - when possible compatible with enacl.
%%% Created : 19 Jan 2022 by Hans Svensson
-module(ecu_eddsa).
-export([sign_keypair/0,
sign_seed_keypair/1,
sign/2,
sign_open/2,
sign_detached/2,
sign_verify_detached/3]).
%% @doc sign_keypair/0 creates a keypair for signing
%%
%% The keypair is returned as a map with keys 'public' and 'secret'.
%% @end
-spec sign_keypair() -> #{ atom() => binary() }.
sign_keypair() ->
Secret = crypto:strong_rand_bytes(32),
<<Seed:32/bytes, _/binary>> = crypto:hash(sha512, Secret),
Pub = ecu_ed25519:scalar_mul_base(Seed),
#{public => Pub, secret => <<Secret:32/binary, Pub:32/binary>>}.
%% @doc sign_seed_keypair/1 computes the signing keypair from a seed.
%%
%% The keypair is returned as a map with keys 'public' and 'secret'.
%% @end
-spec sign_seed_keypair(Seed :: <<_:32>>) -> #{ atom() => binary() }.
sign_seed_keypair(Secret) ->
<<Seed:32/bytes, _/binary>> = crypto:hash(sha512, Secret),
Pub = ecu_ed25519:compress(ecu_ed25519:scalar_mul_base(Seed)),
%% Pub = enacl:crypto_ed25519_scalarmult_base(Seed),
#{public => Pub, secret => <<Secret:32/binary, Pub:32/binary>>}.
%% @doc sign/2 signs a message with private/secret key.
%%
%% Given a message `Msg' and a secret key `SK' the function will sign the
%% message and return a signed message `SM'.
%% @end
-spec sign(Msg :: iodata(), SK :: <<_:32>> | <<_:64>>) -> SM :: binary().
sign(Msg, SK) ->
BinMsg = iolist_to_binary(Msg),
Sig = sign_detached(Msg, SK),
<<Sig/binary, BinMsg/binary>>.
%% @doc sign_open/2 opens a signed message.
%%
%% Given a signed message `SMsg' and a public key `PK', verify that the
%% message has the right signature. Returns either `{ok, Msg}' or
%% `{error, failed_verification}' depending on the correctness of the
%% signature.
%% @end
-spec sign_open(SMsg :: binary(), PK :: <<_:32>>) ->
{ok, Msg :: binary()} | {error, failed_verification}.
sign_open(<<Sig:64/binary, BinMsg/binary>>, PK) ->
<<R:32/bytes, Ss:32/bytes>> = Sig,
Ks0 = crypto:hash(sha512, <<R/bytes, PK/bytes, BinMsg/bytes>>),
Ks = ecu_ed25519:scalar_reduce(Ks0),
LHS = ecu_ed25519:scalar_mul_base_noclamp(Ss),
RHS = ecu_ed25519:p_add(R, ecu_ed25519:scalar_mul_noclamp(Ks, PK)),
case ecu_ed25519:pt_eq(LHS, RHS) of
true -> {ok, BinMsg};
false -> {error, failed_verification}
end.
%% @doc sign_detached/2 computes the signature of a message with private/secret
%% key.
%%
%% Given a message `Msg' and a secret key `SK' the function will compute the
%% digital signature `Sig'.
%% @end
-spec sign_detached(Msg :: iodata(), SK :: <<_:32>>) -> Sig :: binary().
sign_detached(Msg, SK) ->
BinMsg = iolist_to_binary(Msg),
<<Secret:32/binary, _/binary>> = SK,
%% Grab the Seed, also referred to as 'a' (clamped) and the Prefix
<<Seed0:32/bytes, Prefix:32/bytes>> = crypto:hash(sha512, Secret),
Seed = clamp(Seed0),
Pub = case SK of
<<_:32/binary, Pub0:32/binary>> ->
Pub0;
_ ->
ecu_ed25519:compress(ecu_ed25519:scalar_mul_base(Seed0))
end,
%% Compute r = H(prefix || msg)
Rs0 = crypto:hash(sha512, <<Prefix/bytes, BinMsg/bytes>>),
Rs = ecu_ed25519:scalar_reduce(Rs0),
%% Compute R = sG (and since we want the computation to be invertible use
%% the 'noclamp' version).
R = ecu_ed25519:compress(ecu_ed25519:scalar_mul_base_noclamp(Rs)),
%% Compute k = H(R' || Pub || msg)
Ks0 = crypto:hash(sha512, <<R/bytes, Pub/bytes, BinMsg/bytes>>),
Ks = ecu_ed25519:scalar_reduce(Ks0),
%% Compute s = (r + k * a) mod L
Ss = ecu_ed25519:s_add(Rs, ecu_ed25519:s_mul(Ks, Seed)),
%% Form the signature {R, s}
<<R/bytes, Ss/bytes>>.
%% @doc sign_verify_detached/3 verifies the given signature against the given
%% message for the given public key.
%%
%% Given a signature `Sig', a message `Msg', and a public key `PK', the
%% function computes true iff the `Sig' is valid for `Msg' and `PK'; and,
%% false otherwise.
%% @end
-spec sign_verify_detached(Sig :: <<_:64>>, Msg :: iodata(), PK :: <<_:32>>) -> boolean().
sign_verify_detached(Sig, Msg, PK) ->
BinMsg = iolist_to_binary(Msg),
<<R:32/bytes, Ss:32/bytes>> = Sig,
Ks0 = crypto:hash(sha512, <<R/bytes, PK/bytes, BinMsg/bytes>>),
Ks = ecu_ed25519:scalar_reduce(Ks0),
LHS = ecu_ed25519:scalar_mul_base_noclamp(Ss),
RHS = ecu_ed25519:p_add(R, ecu_ed25519:scalar_mul_noclamp(Ks, PK)),
ecu_ed25519:pt_eq(LHS, RHS).
%% Clamp a 32-byte little-endian integer - i.e clear the lowest three bits
%% of the first byte and clear the highest and set the second highest of
%% the last byte (i.e. making it divisible by 8 and
clamp(<<B0:8, B1_30:30/bytes, B31:8>>) ->
<<(B0 band 16#f8):8, B1_30/bytes, ((B31 band 16#7f) bor 16#40):8>>.

View File

@ -38,9 +38,13 @@ p() -> ?P.
n() -> ?N. n() -> ?N.
scalar_mul_base(<<K:256>>) ->
scalar_mul(K, {?X, ?Y});
scalar_mul_base(K) -> scalar_mul_base(K) ->
scalar_mul(K, {?X, ?Y}). scalar_mul(K, {?X, ?Y}).
scalar_mul(<<K:256>>, P) ->
scalar_mul(K, P);
scalar_mul(0, _P) -> scalar_mul(0, _P) ->
{0, 0}; {0, 0};
scalar_mul(1, P) -> scalar_mul(1, P) ->

140
test/benchmark_tests.erl Normal file
View File

@ -0,0 +1,140 @@
%%% File : benchmark_tests.erl
%%% Author : Hans Svensson
%%% Description :
%%% Created : 20 Jan 2022 by Hans Svensson
-module(benchmark_tests).
-compile([export_all, nowarn_export_all]).
-include_lib("eunit/include/eunit.hrl").
gen_scalar() ->
<<X:256>> = crypto:strong_rand_bytes(32),
1 + X rem (ecu_ed25519:n() - 1).
bench_point_add_test() ->
Pts = [ enacl:crypto_ed25519_scalarmult_base(<<(gen_scalar()):256/little>>) || _ <- lists:seq(1, 100) ],
PtsEnacl0 = lists:zip(Pts, tl(Pts) ++ [hd(Pts)]),
PtsEd255190 = [ {ecu_ed25519:to_ext_hom(P1), ecu_ed25519:to_ext_hom(P2)} || {P1, P2} <- lists:zip(Pts, tl(Pts) ++ [hd(Pts)]) ],
%% PtsEd255190 = lists:zip(Pts, tl(Pts) ++ [hd(Pts)]),
PtsEnacl = lists:append(lists:duplicate(1000, PtsEnacl0)),
PtsEd25519 = lists:append(lists:duplicate(100, PtsEd255190)),
{TimeEnacl, _} = timer:tc(fun() -> [enacl:crypto_ed25519_add(P1, P2) || {P1, P2} <- PtsEnacl], ok end),
{TimeEd25519, _} = timer:tc(fun() -> [ecu_ed25519:p_add(P1, P2) || {P1, P2} <- PtsEd25519], ok end),
%% {TimeEd25519, _} = timer:tc(fun() -> [ecu_ed25519:compress(ecu_ed25519:p_add(P1, P2)) || {P1, P2} <- PtsEd25519], ok end),
?debugFmt("", []),
stats("Point add", "enacl/libsodium", length(PtsEnacl), TimeEnacl),
stats("Point add", "ecu_ed25519 ", length(PtsEd25519), TimeEd25519),
diff(TimeEnacl / length(PtsEnacl), TimeEd25519 / length(PtsEd25519)),
ok.
bench_scalar_mul_base_test() ->
Scalars0 = [ <<(gen_scalar()):256/little>> || _ <- lists:seq(1, 100) ],
ScalarsSecp = lists:append(lists:duplicate(1, Scalars0)),
ScalarsEnacl = lists:append(lists:duplicate(100, Scalars0)),
ScalarsEd25519 = lists:append(lists:duplicate(30, Scalars0)),
{TimeSecp, _} = timer:tc(fun() -> [ecu_secp256k1:scalar_mul_base(S) || S <- ScalarsSecp], ok end),
{TimeEnacl, _} = timer:tc(fun() -> [enacl:crypto_ed25519_scalarmult_base(S) || S <- ScalarsEnacl], ok end),
{TimeEd25519, _} = timer:tc(fun() -> [ecu_ed25519:scalar_mul_base(S) || S <- ScalarsEd25519], ok end),
?debugFmt("", []),
stats("Scalar mul base", "ecu_secp256k1 ", length(ScalarsSecp), TimeSecp),
stats("Scalar mul base", "enacl/libsodium", length(ScalarsEnacl), TimeEnacl),
stats("Scalar mul base", "ecu_ed25519 ", length(ScalarsEd25519), TimeEd25519),
diff(TimeEnacl / length(ScalarsEnacl), TimeEd25519 / length(ScalarsEd25519)),
ok.
bench_scalar_mul_test() ->
Scalars0 = [ <<(gen_scalar()):256/little>> || _ <- lists:seq(1, 100) ],
ScalarsSecp = lists:append(lists:duplicate(1, Scalars0)),
ScalarsEnacl = lists:append(lists:duplicate(100, Scalars0)),
ScalarsEd25519 = lists:append(lists:duplicate(10, Scalars0)),
Test = fun(F, P0, Ss) -> lists:foldl(fun(S, P) -> F(S, P) end, P0, Ss) end,
{TimeSecp, _} = timer:tc(fun() -> Test(fun ecu_secp256k1:scalar_mul/2, ecu_secp256k1:scalar_mul_base(hd(ScalarsSecp)), tl(ScalarsSecp)) end),
{TimeEnacl, _} = timer:tc(fun() -> Test(fun enacl:crypto_ed25519_scalarmult/2, enacl:crypto_ed25519_scalarmult_base(hd(ScalarsEnacl)), tl(ScalarsEnacl)) end),
{TimeEd25519, _} = timer:tc(fun() -> Test(fun ecu_ed25519:scalar_mul/2, ecu_ed25519:scalar_mul_base(hd(ScalarsEd25519)), tl(ScalarsEd25519)) end),
?debugFmt("", []),
stats("Scalar mul", "ecu_secp256k1 ", length(ScalarsSecp), TimeSecp),
stats("Scalar mul", "enacl/libsodium", length(ScalarsEnacl), TimeEnacl),
stats("Scalar mul", "ecu_ed25519 ", length(ScalarsEd25519), TimeEd25519),
diff(TimeEnacl / length(ScalarsEnacl), TimeEd25519 / length(ScalarsEd25519)),
ok.
bench_sign_test() ->
KeyPairs = [ enacl:sign_keypair() || _ <- lists:seq(1, 10) ],
Messages = [ crypto:strong_rand_bytes(X) || X <- lists:seq(20, 49) ],
Data = [ {K, Msg} || {Msg, K} <- lists:zip(Messages, lists:append(lists:duplicate(3, KeyPairs))) ],
DataEnacl = lists:append(lists:duplicate(1000, Data)),
DataEd25519 = lists:append(lists:duplicate(20, Data)),
{TimeEnacl, _} = timer:tc(fun() -> [enacl:sign_detached(Msg, maps:get(secret, K)) || {K, Msg} <- DataEnacl], ok end),
{TimeEd25519, _} = timer:tc(fun() -> [ecu_eddsa:sign_detached(Msg, maps:get(secret, K)) || {K, Msg} <- DataEd25519], ok end),
?debugFmt("", []),
stats("Message sign", "enacl/libsodium", length(DataEnacl), TimeEnacl),
stats("Message sign", "ecu_ed25519 ", length(DataEd25519), TimeEd25519),
diff(TimeEnacl / length(DataEnacl), TimeEd25519 / length(DataEd25519)),
ok.
bench_verify_test() ->
KeyPairs = [ enacl:sign_keypair() || _ <- lists:seq(1, 10) ],
Messages = [ crypto:strong_rand_bytes(X) || X <- lists:seq(20, 49) ],
Data = [ {K, Msg, enacl:sign_detached(Msg, maps:get(secret, K))}
|| {Msg, K} <- lists:zip(Messages, lists:append(lists:duplicate(3, KeyPairs))) ],
DataEnacl = lists:append(lists:duplicate(1000, Data)),
DataEd25519 = lists:append(lists:duplicate(20, Data)),
{TimeEnacl, _} = timer:tc(fun() -> [enacl:sign_verify_detached(Sig, Msg, maps:get(public, K)) || {K, Msg, Sig} <- DataEnacl], ok end),
{TimeEd25519, _} = timer:tc(fun() -> [ecu_eddsa:sign_verify_detached(Sig, Msg, maps:get(public, K)) || {K, Msg, Sig} <- DataEd25519], ok end),
?debugFmt("", []),
stats("Message verify", "enacl/libsodium", length(DataEnacl), TimeEnacl),
stats("Message verify", "ecu_ed25519 ", length(DataEd25519), TimeEd25519),
diff(TimeEnacl / length(DataEnacl), TimeEd25519 / length(DataEd25519)),
ok.
gen_ecdsa_secp256k1_privkey() ->
<<P0:256>> = crypto:strong_rand_bytes(32),
P = (P0 rem (ecu_secp256k1:n() - 1)) + 1,
<<P:256>>.
bench_ecverify_test() ->
Data0 = [ {gen_ecdsa_secp256k1_privkey(), crypto:strong_rand_bytes(32)} || _ <- lists:seq(1, 10) ],
Data1 = [ {Pr, Msg, ecu_crypto:private_to_short(ethereum, Pr), ecu_crypto:eth_sign(Msg, Pr)} || {Pr, Msg} <- Data0 ],
RustData = lists:append(lists:duplicate(100, Data1)),
ECUData = lists:append(lists:duplicate(5, Data1)),
%% ensure loading
ecrecover:recover(<<0:256>>, <<123:520>>),
{TimeRust, _} = timer:tc(fun() -> [ ecrecover:recover(Msg, Sig) || {_, Msg, _, Sig} <- RustData ], ok end),
{TimeECU, _} = timer:tc(fun() -> [ ecu_crypto:eth_recover(Msg, Sig) || {_, Msg, _, Sig} <- ECUData ], ok end),
?debugFmt("", []),
stats("Message verify", "ecrecover (Rust)", length(RustData), TimeRust),
stats("Message verify", "ecu_crypto ", length(ECUData), TimeECU),
diff(TimeRust / length(RustData), TimeECU / length(ECUData)),
ok.
stats(What, Who, N, T) ->
?debugFmt("~s with ~s ~.2f us/op", [What, Who, T / N]).
diff(T1, T2) when T1 > T2 ->
diff(T2, T1);
diff(T1, T2) ->
?debugFmt("Speed difference x~.2f", [T2 / T1]).