[wip] generating keypair with ec_utils copied from zx's github

This commit is contained in:
2022-12-20 20:58:21 -07:00
parent 8cc46b6ac9
commit cbe487618c
8 changed files with 657 additions and 10 deletions
+4
View File
@@ -0,0 +1,4 @@
-module(ec_utils).
-vsn("1.0.0").
-export([]).
+61
View File
@@ -0,0 +1,61 @@
%%% File : ecu_crypto.erl
%%% Author : Hans Svensson
%%% Description :
%%% Created : 13 Jan 2022 by Hans Svensson
-module(ecu_crypto).
-vsn("1.0.0").
-export([private_to_short/2, public_to_short/2,
eth_sign/2, eth_recover/2, eth_verify/3, eth_msg_hash/1,
keccak256/1]).
private_to_short(bitcoin, PrivateKey) ->
public_to_short(bitcoin, aeu_ecdsa:private_to_public(secp256k1, PrivateKey));
private_to_short(ethereum, <<PrivateKey:256>>) ->
public_to_short(ethereum, ecu_secp256k1:scalar_mul_base(PrivateKey)).
public_to_short(bitcoin, PubKey = <<_:33/bytes>>) ->
crypto:hash(ripemd160, crypto:hash(sha256, PubKey));
public_to_short(bitcoin, PubKey) ->
crypto:hash(ripemd160, crypto:hash(sha256, ecu_secp256k1:compress(PubKey)));
public_to_short(ethereum, PubKey) ->
case PubKey of
<<_:33/bytes>> -> public_to_short(ethereum, ecu_secp256k1:decompress(PubKey));
<<4:8, X:256, Y:256>> -> public_to_short(ethereum, {X, Y});
{X, Y} ->
<<_:12/bytes, ShortPub:20/bytes>> = keccak256(<<X:256, Y:256>>),
ShortPub
end.
eth_sign(Msg, PrivateKey = <<_:32/bytes>>) ->
{BaseSig, YVal} = ecu_ecdsa:sign_secp256k1(eth_msg_hash(Msg), PrivateKey),
V = if YVal rem 2 == 0 -> 27;
true -> 28
end,
<<V:8, BaseSig/bytes>>.
eth_recover(Msg, Sig = <<_:65/bytes>>) ->
MsgHash = eth_msg_hash(Msg),
<<E:256>> = MsgHash,
<<V:8, R:256, S:256>> = Sig,
Z = E rem ecu_secp256k1:n(),
RInv = ecu_secp256k1:s_inv(R),
Rd = ecu_secp256k1:decompress(<<(V - 27 + 2):8, R:256>>),
[P1, P2] =
ecu_misc:pcomp(
[fun() -> ecu_secp256k1:scalar_mul(ecu_secp256k1:s_mul(RInv, S), Rd) end,
fun() -> ecu_secp256k1:scalar_mul_base(ecu_secp256k1:s_mul(RInv,Z)) end]),
{X, Y} = ecu_secp256k1:p_add(P1, ecu_secp256k1:p_neg(P2)),
<<_:12/bytes, RPub:20/bytes>> = keccak256(<<X:256, Y:256>>),
RPub.
eth_verify(Msg, PublicKey, Sig) ->
PublicKey == eth_recover(Msg, Sig).
eth_msg_hash(Msg0) ->
Msg = ["\x19Ethereum Signed Message:\n", integer_to_list(byte_size(Msg0)), Msg0],
keccak256(iolist_to_binary(Msg)).
keccak256(Bin) ->
sha3:hash(256, Bin).
+54
View File
@@ -0,0 +1,54 @@
%%% File : ecu_ecdsa.erl
%%% Author : Hans Svensson
%%% Description : ecdsa functionality
%%% Created : 13 Jan 2022 by Hans Svensson
-module(ecu_ecdsa).
-vsn("1.0.0").
-export([sign/3, verify/4,
sign_secp256k1/2,
private_to_public/2]).
private_to_public(secp256k1, <<PrivateKey:256>>) ->
ecu_secp256k1:compress(ecu_secp256k1:scalar_mul_base(PrivateKey)).
sign(secp256k1, MsgHash = <<_:32/bytes>>, PrivateKey = <<_:32/bytes>>) ->
{Sig, _YVal} = sign_secp256k1(MsgHash, PrivateKey),
Sig.
verify(secp256k1, MsgHash = <<_:32/bytes>>, PubKey = <<_:33/bytes>>, Sig = <<_:64/bytes>>) ->
verify(secp256k1, MsgHash, ecu_secp256k1:decompress(PubKey), Sig);
verify(secp256k1, MsgHash = <<_:32/bytes>>, PubKey = {_, _}, Sig = <<_:64/bytes>>) ->
<<E:256>> = MsgHash,
<<R:256, S:256>> = Sig,
Z = E rem ecu_secp256k1:n(),
W = ecu_secp256k1:s_inv(S),
[P1, P2] = ecu_misc:pcomp(
[fun() -> ecu_secp256k1:scalar_mul_base(ecu_secp256k1:s_mul(Z, W)) end,
fun() -> ecu_secp256k1:scalar_mul(ecu_secp256k1:s_mul(R, W), PubKey) end]),
{X, _Y} = ecu_secp256k1:p_add(P1, P2),
R == (X rem ecu_secp256k1:n()).
sign_secp256k1(MsgHash = <<_:32/bytes>>, PrivateKey = <<_:32/bytes>>) ->
<<E:256>> = MsgHash,
<<D:256>> = PrivateKey,
Z = E rem ecu_secp256k1:n(),
K = pick_k(secp256k1),
{X, Y} = ecu_secp256k1:scalar_mul_base(K),
R = X rem ecu_secp256k1:n(),
S = ecu_secp256k1:s_mul(ecu_secp256k1:s_inv(K),
ecu_secp256k1:s_add(Z, ecu_secp256k1:s_mul(R, D))),
if R == 0 orelse S == 0 ->
sign(secp256k1, MsgHash, PrivateKey);
true ->
{<<R:256, S:256>>, Y}
end.
%% --- internal functions
pick_k(secp256k1) ->
<<K:256>> = crypto:strong_rand_bytes(32),
case K == 0 orelse K >= ecu_secp256k1:n() of
true -> pick_k(secp256k1);
false -> K
end.
+215
View File
@@ -0,0 +1,215 @@
%%% File : ecu_ed25519.erl
%%% Author : Hans Svensson
%%% Description : Trying to whip together a pure Erlang ed25519
%%% Just for usage when speed isn't crucial...
%%% Created : 13 Jan 2022 by Hans Svensson
-module(ecu_ed25519).
-vsn("1.0.0").
-define(P, 16#7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED).
-define(N, 16#1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED).
-type pt_affine() :: {non_neg_integer(), non_neg_integer()}. %% {X, Y}
-type pt_hom_ext() :: {non_neg_integer(), non_neg_integer(),
non_neg_integer(), non_neg_integer()}. %% {X, Y, Z, T}
-type pt_compressed() :: <<_:32>>. %% Y coord + odd/even X.
-type pt() :: pt_affine() | pt_hom_ext() | pt_compressed().
%% -type fld_elem() :: 0..(?P-1).
-type scalar() :: 0..(?N-1).
-define(D, 16#52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3).
-define(X, 16#216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A).
-define(Y, 16#6666666666666666666666666666666666666666666666666666666666666658).
-define(XY, 16#67875F0FD78B766566EA4E8E64ABE37D20F09F80775152F56DDE8AB3A5B7DDA3).
-define(GA, {?X, ?Y}).
-define(GE, {?X, ?Y, 1, ?XY}).
-define(PPLUS3DIV8, 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE).
-define(TWOPOWPMINUS1DIV4, 16#2B8324804FC1DF0B2B4D00993DFBD7A72F431806AD2FE478C4EE1B274A0EA0B0).
-define(ADD(A, B), ((A + B) rem ?P)).
-define(MUL(A, B), ((A * B) rem ?P)).
-define(SUB(A, B), ((A - B + ?P) rem ?P)).
-define(DIV(A, B), f_div(A, B)).
-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,
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]).
-ifdef(TEST).
-compile([export_all, nowarn_export_all]).
-endif.
-spec pt_eq(P1 :: pt(), P2 :: pt()) -> boolean().
pt_eq({X1, Y1}, {X2, Y2}) ->
X1 == X2 andalso Y1 == Y2;
pt_eq(C1, C2) when is_binary(C1), is_binary(C2) ->
C1 == C2;
pt_eq({X1, Y1, Z1, _T1}, {X2, Y2, Z2, _T2}) ->
?SUB(?MUL(X1, Z2), ?MUL(X2, Z1)) == 0
andalso ?SUB(?MUL(Y1, Z2), ?MUL(Y2, Z1)) == 0;
pt_eq(P1, P2) ->
pt_eq(to_ext_hom(P1), to_ext_hom(P2)).
%% Libsodium has additional checks
%% - weak keys
%% - canonical representation
%% - main subgroup...
on_curve({X, Y}) ->
X2 = ?MUL(X, X),
Y2 = ?MUL(Y, Y),
LHS = ?SUB(Y2, X2),
RHS = ?ADD(1, ?MUL(?D, ?MUL(X2, Y2))),
0 == ?SUB(LHS, RHS);
on_curve(P) ->
on_curve(to_affine(P)).
p() -> ?P.
n() -> ?N.
-define(TWO_POW_255_MINUS_1, 16#7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).
-spec compress(P :: pt()) -> <<_:32>>.
compress(<<_:32/binary>> = P) -> P;
compress({_, _, _, _} = P) -> compress(to_affine(P));
compress({X, Y}) ->
V = (Y band ?TWO_POW_255_MINUS_1) bor ((X band 1) bsl 255),
<<V:256/little>>.
-spec decompress(<<_:32>>) -> pt_hom_ext().
decompress(<<Y0:256/little>>) ->
X0 = Y0 bsr 255,
Y = Y0 band ?TWO_POW_255_MINUS_1,
X = xrecover(Y),
case X rem 2 == X0 of
true -> to_ext_hom({X, Y});
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), 2 * ?D),
D = ?MUL(2 * Z1, Z2),
E = ?SUB(B, A),
F = ?SUB(D, C),
G = ?ADD(D, C),
H = ?ADD(A, B),
{?MUL(E, F), ?MUL(G, H), ?MUL(F, G), ?MUL(E, H)};
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_(clamp(K), ?GE);
scalar_mul_base(K) when is_integer(K), K >= 0, K < ?N ->
scalar_mul_(clamp(K), ?GE).
-spec scalar_mul(Scalar :: scalar() | binary(), Pt :: pt()) -> pt_hom_ext().
scalar_mul(<<K:256/little>>, P) ->
scalar_mul(K, P);
scalar_mul(K, P) ->
scalar_mul_(clamp(K), to_ext_hom(P)).
-spec scalar_mul_base_noclamp(Scalar :: scalar() | binary()) -> pt_hom_ext().
scalar_mul_base_noclamp(<<K:256/little>>) ->
scalar_mul_(K, ?GE);
scalar_mul_base_noclamp(K) when is_integer(K), K >= 0, K < ?N ->
scalar_mul_(K, ?GE).
-spec scalar_mul_noclamp(Scalar :: scalar() | binary(), Pt :: pt()) -> pt_hom_ext().
scalar_mul_noclamp(<<K:256/little>>, P) ->
scalar_mul_noclamp(K, P);
scalar_mul_noclamp(K, P) ->
scalar_mul_(K, to_ext_hom(P)).
to_ext_hom({_, _, _, _} = P) -> P;
to_ext_hom(<<_:32/binary>> = P) -> decompress(P);
to_ext_hom({X, Y}) -> {X, Y, 1, ?MUL(X, Y)}.
to_affine({_, _} = P) -> P;
to_affine(<<_:32/binary>> = P) -> to_affine(decompress(P));
to_affine({X, Y, Z, _}) ->
ZInv = f_inv(Z),
{?MUL(X, ZInv), ?MUL(Y, ZInv)}.
f_pow(A, B) -> ecu_misc:exp_mod(A, B, ?P).
%% Arithmetics in prime field P
f_add(A, B) -> (A + B) rem ?P.
f_mul(A, B) -> (A * B) rem ?P.
f_sub(A, B) -> (A - B + ?P) rem ?P.
f_div(A, B) -> f_mul(A, f_inv(B)).
f_inv(A) ->
f_pow(A, ?P - 2).
%% Arithmetics in curve group order 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:256/little>>) ->
{1, S, _T} = ecu_misc:eea(A, ?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_dbl(P));
1 -> p_add(P, scalar_mul_(K - 1, P))
end.
clamp(K) ->
((K band (bnot 7)) band (bnot (128 bsl 248))) bor (64 bsl 248).
xrecover(Y) ->
Y2 = ?MUL(Y, Y),
U = ?SUB(Y2, 1),
V = ?ADD(?MUL(?D, Y2), 1),
UdivV = ?DIV(U, V),
X0 = f_pow(UdivV, ?PPLUS3DIV8),
X0_2 = ?MUL(X0, X0),
case ?SUB(X0_2, UdivV) of
0 -> X0;
_ -> case ?ADD(X0_2, UdivV) of
0 -> ?MUL(X0, ?TWOPOWPMINUS1DIV4);
_ -> error(xrecover_failed)
end
end.
+141
View File
@@ -0,0 +1,141 @@
%%% 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).
-vsn("1.0.0").
-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>>.
+53
View File
@@ -0,0 +1,53 @@
%%% File : ecu_misc.erl
%%% Author : Hans Svensson
%%% Description : Misc. functionality
%%% Created : 13 Jan 2022 by Hans Svensson
-module(ecu_misc).
-vsn("1.0.0").
-export([eea/2, exp_mod/3,
hex_to_bin/1, bin_to_hex/1,
pcomp/1]).
%% A^B mod P
exp_mod(_A, 0, _P) -> 1;
exp_mod(A, B, P) when A > 0 ->
binary:decode_unsigned(crypto:mod_pow(A, B, P));
exp_mod(A, B, P) ->
X = exp_mod(-A, B, P),
case B rem 2 == 0 orelse X == 0 of
true -> X;
false -> P - X
end.
%% Extended Euclidean Algorithm
eea(A, B) when ((A < 1) or (B < 1)) ->
undefined;
eea(A, B) ->
eea(A, 1, 0, B, 0, 1).
eea(G, S, T, 0, _, _) ->
{G, S, T};
eea(G0, S0, T0, G1, S1, T1) ->
Q = G0 div G1,
eea(G1, S1, T1, G0 - (Q * G1), S0 - (Q * S1), T0 - (Q * T1)).
%% Very rudimentary parallel computation...
pcomp(Fs) ->
Parent = self(),
Pids = [ spawn(fun() -> Parent ! {self(), F()} end) || F <- Fs ],
[ receive {Pid, X} -> X after 500 -> error(timeout) end || Pid <- Pids ].
%% Hex encode/decode
-spec hex_to_bin(Input :: string()) -> binary().
hex_to_bin(S) ->
hex_to_bin(S, []).
hex_to_bin([], Acc) ->
list_to_binary(lists:reverse(Acc));
hex_to_bin([X,Y|T], Acc) ->
{ok, [V], []} = io_lib:fread("~16u", [X,Y]),
hex_to_bin(T, [V | Acc]).
-spec bin_to_hex(Input :: binary()) -> string().
bin_to_hex(Bin) ->
lists:flatten([io_lib:format("~2.16.0B", [X]) || X <- binary_to_list(Bin)]).
+115
View File
@@ -0,0 +1,115 @@
%%% File : ecu_secp256k1.erl
%%% Author : Hans Svensson
%%% Description : Trying to whip together a pure Erlang secp256k1
%%% Just for usage when speed isn't crucial...
%%% Created : 22 Dec 2021 by Hans Svensson
-module(ecu_secp256k1).
-vsn("1.0.0").
-define(P, 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F).
-define(A, 16#00).
-define(B, 16#07).
-define(X, 16#79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798).
-define(Y, 16#483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8).
-define(N, 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141).
-define(E, 16#7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE).
-define(ADD(A, B), ((A + B) rem ?P)).
-define(MUL(A, B), ((A * B) rem ?P)).
-define(SUB(A, B), ((A - B + ?P) rem ?P)).
-define(DIV(A, B), f_div(A, B)).
-export([on_curve/1, p/0, n/0,
scalar_mul/2, scalar_mul_base/1, p_add/2, p_neg/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]).
-ifdef(TEST).
-compile([export_all, nowarn_export_all]).
-endif.
on_curve({X, Y}) ->
%% y^2 = x^3 + 7
X3 = ?MUL(?MUL(X, X), X),
Y2 = ?MUL(Y, Y),
Y2 == ?ADD(X3, ?B).
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) ->
P;
scalar_mul(K, P) ->
case K rem 2 == 0 of
true -> scalar_mul(K div 2, p_add(P, P));
false -> p_add(P, scalar_mul(K - 1, P))
end.
compress({X, Y}) when Y rem 2 == 0 -> <<2:8, X:256>>;
compress({X, _}) -> <<3:8, X:256>>;
compress(<<4:8, X:256, Y:256>>) -> compress({X, Y}).
decompress(<<N:8, X:256>>) ->
Y0 = ?B + ?MUL(X, ?MUL(X, X)),
Y1 = pow(Y0, (?P + 1) div 4),
case Y1 rem 2 == N rem 2 of
true -> {X, Y1};
false -> {X, ?P - Y1}
end.
p_neg({X, Y}) -> {X, ?P - Y}.
p_add(P1, {0, 0}) -> P1;
p_add({0, 0}, P2) -> P2;
p_add({X, Y1}, {X, Y2}) when Y1 /= Y2 -> {0, 0};
p_add(P = {X1, Y1}, P) ->
M = ?DIV(?MUL(3, ?MUL(X1, X1)), ?MUL(2, Y1)),
X3 = ?SUB(?MUL(M, M), ?MUL(2, X1)),
Y3 = ?SUB(?MUL(M, ?SUB(X1, X3)), Y1),
{X3, Y3};
p_add({X1, Y1}, {X2, Y2}) ->
M = ?DIV(?SUB(Y2, Y1), ?SUB(X2, X1)),
X3 = ?SUB(?MUL(M, M), ?ADD(X1, X2)),
Y3 = ?SUB(?MUL(M, ?SUB(X1, X3)), Y1),
{X3, Y3}.
pow(A, B) -> ecu_misc:exp_mod(A, B, ?P).
%% Arithmetics in prime field P
f_add(A, B) -> (A + B) rem ?P.
f_mul(A, B) -> (A * B) rem ?P.
f_sub(A, B) -> (A - B + ?P) rem ?P.
f_div(A, B) -> f_mul(A, f_inv(B)).
f_inv(A) ->
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_div(A, B) -> s_mul(A, s_inv(B)).
s_inv(A) ->
{1, S, _T} = ecu_misc:eea(A, ?N),
(S + ?N) rem ?N.
%% curve() ->
%% #{ p => 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
%% a => 16#00, b => 16#07,
%% x => 16#79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
%% y => 16#483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
%% n => 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
%% e => 16#7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE
%% }.
+14 -10
View File
@@ -17,20 +17,17 @@ start(ArgV) ->
%%
%% WHEN back: get decompose to work
go(["help"]) ->
help();
go(["--help"]) ->
help();
go(["generate", "keypair"]) ->
error(nyi);
go(["decompose", TxStr]) ->
decompose(TxStr);
go(_) ->
error(invalid_subcommand).
go(["help"]) -> help();
go(["--help"]) -> help();
go(["decompose", TxStr]) -> decompose(TxStr);
go(["generate", "keypair"]) -> generate_keypair();
go(X) -> error({invalid_subcommand, X}).
help() ->
io:format("you can't help people who won't help themselves~n", []).
decompose(TxStr) ->
case vd:decompose(TxStr) of
{ok, X} ->
@@ -38,3 +35,10 @@ decompose(TxStr) ->
{error, Error} ->
io:format("ERROR: ~tp~n", [Error])
end.
generate_keypair() ->
#{public := PublicKey,
secret := SecretKey} = ecu_eddsa:sign_keypair(),
io:format("Public Key: ~w~n", [PublicKey]),
io:format("Secret Key: ~w~n", [SecretKey]).