Add EdDSA, EcDSA + benchmarks and some other improvments
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib]},
|
||||
{env,[]},
|
||||
{modules, [ecu_secp256k1, ecu_misc]},
|
||||
{modules, [ecu_secp256k1, ecu_ed25519, ecu_ecdsa, ecu_eddsa, ecu_crypto, ecu_misc]},
|
||||
{licenses, ["MIT"]},
|
||||
{links, []}
|
||||
]}.
|
||||
|
||||
+34
-10
@@ -36,7 +36,8 @@
|
||||
-export([on_curve/1, p/0, n/0, pt_eq/2,
|
||||
scalar_mul/2, scalar_mul_base/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,
|
||||
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]).
|
||||
@@ -92,12 +93,18 @@ decompress(<<Y0:256/little>>) ->
|
||||
false -> to_ext_hom({?P - X, Y})
|
||||
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().
|
||||
p_add({X1, Y1, Z1, T1}, {X2, Y2, Z2, T2}) ->
|
||||
A = ?MUL(?SUB(Y1, X1), ?SUB(Y2, X2)),
|
||||
B = ?MUL(?ADD(Y1, X1), ?ADD(Y2, X2)),
|
||||
C = ?MUL(?MUL(T1, T2), ?MUL(2, ?D)),
|
||||
D = ?MUL(2, ?MUL(Z1, Z2)),
|
||||
C = ?MUL(?MUL(T1, T2), 2 * ?D),
|
||||
D = ?MUL(2 * Z1, Z2),
|
||||
E = ?SUB(B, A),
|
||||
F = ?SUB(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(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().
|
||||
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_(clamp(K), ?GE).
|
||||
|
||||
@@ -152,21 +173,24 @@ f_inv(A) ->
|
||||
f_pow(A, ?P - 2).
|
||||
|
||||
%% Arithmetics in curve group order N
|
||||
s_add(A, B) -> (A + B) rem ?N.
|
||||
s_mul(A, B) -> (A * B) rem ?N.
|
||||
s_sub(A, B) -> (A - B + ?N) rem ?N.
|
||||
s_add(<<A:256/little>>, <<B:256/little>>) -> <<((A + B) rem ?N):256/little>>.
|
||||
s_mul(<<A:256/little>>, <<B:256/little>>) -> <<((A * B) rem ?N):256/little>>.
|
||||
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_inv(A) ->
|
||||
s_inv(<<A:256/little>>) ->
|
||||
{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
|
||||
scalar_mul_(0, _P) -> {0, 1, 1, 0};
|
||||
scalar_mul_(1, P) -> P;
|
||||
scalar_mul_(K, P) ->
|
||||
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))
|
||||
end.
|
||||
|
||||
|
||||
@@ -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 = s⋅G (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>>.
|
||||
@@ -38,9 +38,13 @@ p() -> ?P.
|
||||
|
||||
n() -> ?N.
|
||||
|
||||
scalar_mul_base(<<K:256>>) ->
|
||||
scalar_mul(K, {?X, ?Y});
|
||||
scalar_mul_base(K) ->
|
||||
scalar_mul(K, {?X, ?Y}).
|
||||
|
||||
scalar_mul(<<K:256>>, P) ->
|
||||
scalar_mul(K, P);
|
||||
scalar_mul(0, _P) ->
|
||||
{0, 0};
|
||||
scalar_mul(1, P) ->
|
||||
|
||||
Reference in New Issue
Block a user