Add secp256k1

This commit is contained in:
Hans Svensson 2022-01-13 15:38:49 +01:00
parent 864c42ed8c
commit c739230911
4 changed files with 212 additions and 1 deletions

View File

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

21
src/ecu_misc.erl Normal file
View File

@ -0,0 +1,21 @@
%%% File : ecu_misc.erl
%%% Author : Hans Svensson
%%% Description : Misc. functionality
%%% Created : 13 Jan 2022 by Hans Svensson
-module(ecu_misc).
-export([eea/2]).
%% 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)).

117
src/ecu_secp256k1.erl Normal file
View File

@ -0,0 +1,117 @@
%%% 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).
-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) ->
scalar_mul(K, {?X, ?Y}).
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(_, 0) -> 1;
pow(A, 1) -> A;
pow(A, B) -> pow(A, B, 1).
pow(_, 0, R) -> R;
pow(A, B, R) when B rem 2 == 0 -> pow(A * A, B bsr 1, R);
pow(A, B, R) -> pow(?MUL(A, A), B bsr 1, ?MUL(R, A)).
%% 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) ->
{1, S, _T} = ecu_misc:eea(A, ?P),
(S + ?P) rem ?P.
%% 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
%% }.

73
test/secp256k1_tests.erl Normal file
View File

@ -0,0 +1,73 @@
%%% File : secp256k1_tests.erl
%%% Author : Hans Svensson
%%% Description :
%%% Created : 22 Dec 2021 by Hans Svensson
-module(secp256k1_tests).
-compile([export_all, nowarn_export_all]).
-include_lib("eunit/include/eunit.hrl").
on_curve_test() ->
%% https://chuckbatson.wordpress.com/2014/11/26/secp256k1-test-vectors/
KnownPts =
[{16#79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
16#483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8},
{16#C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5,
16#1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A},
{16#F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,
16#388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672},
{16#E493DBF1C10D80F3581E4904930B1404CC6C13900EE0758474FA94ABE8C4CD13,
16#51ED993EA0D455B75642E2098EA51448D967AE33BFBDFE40CFE97BDC47739922},
{16#2F8BDE4D1A07209355B4A7250A5C5128E88B84BDDC619AB7CBA8D569B240EFE4,
16#D8AC222636E5E3D6D4DBA9DDA6C9C426F788271BAB0D6840DCA87D3AA6AC62D6}],
[ ?assert(ecu_secp256k1:on_curve(Pt)) || Pt <- KnownPts ],
?assert(not ecu_secp256k1:on_curve({42, 723})).
scalar_mul_test() ->
%% https://chuckbatson.wordpress.com/2014/11/26/secp256k1-test-vectors/
KnownPts =
[{1,
16#79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
16#483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8},
{2,
16#C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5,
16#1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A},
{3,
16#F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,
16#388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672},
{4,
16#E493DBF1C10D80F3581E4904930B1404CC6C13900EE0758474FA94ABE8C4CD13,
16#51ED993EA0D455B75642E2098EA51448D967AE33BFBDFE40CFE97BDC47739922},
{5,
16#2F8BDE4D1A07209355B4A7250A5C5128E88B84BDDC619AB7CBA8D569B240EFE4,
16#D8AC222636E5E3D6D4DBA9DDA6C9C426F788271BAB0D6840DCA87D3AA6AC62D6},
{20,
16#4CE119C96E2FA357200B559B2F7DD5A5F02D5290AFF74B03F3E471B273211C97,
16#12BA26DCB10EC1625DA61FA10A844C676162948271D96967450288EE9233DC3A},
{112233445566778899,
16#A90CC3D3F3E146DAADFC74CA1372207CB4B725AE708CEF713A98EDD73D99EF29,
16#5A79D6B289610C68BC3B47F3D72F9788A26A06868B4D8E433E1E2AD76FB7DC76},
{112233445566778899112233445566778899,
16#E5A2636BCFD412EBF36EC45B19BFB68A1BC5F8632E678132B885F7DF99C5E9B3,
16#736C1CE161AE27B405CAFD2A7520370153C2C861AC51D6C1D5985D9606B45F39}
],
[ begin
{X, Y} = ecu_secp256k1:scalar_mul_base(K),
?assertEqual({x, K, Ex}, {x, K, X}),
?assertEqual({y, K, Ey}, {y, K, Y})
end || {K, Ex, Ey} <- KnownPts ].
compression_test() ->
Test = fun(P) ->
CP = ecu_secp256k1:compress(P),
DP = ecu_secp256k1:decompress(CP),
%% ?debugFmt("\nP : ~200p\nCP: ~200p\nDP: ~200p", [P, CP, DP]),
?assertEqual(P, DP)
end,
[ Test(ecu_secp256k1:scalar_mul_base(K)) || K <- lists:seq(10, 100) ].