This commit is contained in:
2026-08-20 21:15:06 +09:00
parent 3666884cc7
commit 3651fc253d
3 changed files with 151 additions and 267 deletions
@@ -137,15 +137,28 @@ public final class Ed25519 {
Scratch sc = new Scratch();
Ge A = decompress(publicKey, sc);
if (A == null) return false;
Ge R = decompress(R_bytes, sc);
if (R == null) return false;
byte[] kHash = sha512(R_bytes, publicKey, message);
reduceScalar(kHash);
byte[] k = Arrays.copyOfRange(kHash, 0, 32);
Ge R_expected = new Ge();
ge_double_scalarmul_vartime(R_expected, S_bytes, A, k, sc);
byte[] R_check = compress(R_expected, sc);
boolean ok = Arrays.equals(R_bytes, R_check);
Ge sB = scalarMulBase(S_bytes, sc);
Ge kA = scalarMul(A, k, sc);
Ge RHS = new Ge();
ge_add(RHS, R, kA, sc);
byte[] LHS_bytes = compress(sB, sc);
byte[] RHS_bytes = compress(RHS, sc);
boolean ok = Arrays.equals(LHS_bytes, RHS_bytes);
A.wipe();
R_expected.wipe();
R.wipe();
sB.wipe();
kA.wipe();
RHS.wipe();
sc.wipe();
CryptoUtils.wipe(kHash);
CryptoUtils.wipe(k);
@@ -269,23 +282,35 @@ public final class Ed25519 {
Ge p = new Ge();
fe_frombytes(p.Y, b);
fe_1(p.Z);
// u = y^2 - 1
fe_sq(s.a, p.Y, s.t19);
fe_mul(s.b, s.a, D, s.t19);
fe_sub(s.a, s.a, p.Z);
// v = dy^2 + 1
fe_sq(s.b, p.Y, s.t19);
fe_mul(s.b, s.b, D, s.t19);
fe_add(s.b, s.b, p.Z);
// check root of u/v
fe_invert(s.c, s.b, s);
fe_mul(s.a, s.a, s.c, s.t19);
fe_pow22523(s.b, s.a, s);
fe_sq(s.c, s.b, s.t19);
fe_sub(s.c, s.c, s.a);
if (fe_isnonzero(s.c)) {
fe_mul(s.b, s.b, I, s.t19);
fe_sq(s.c, s.b, s.t19);
fe_sub(s.c, s.c, s.a);
if (fe_isnonzero(s.c)) return null;
fe_mul(s.c, s.a, s.c, s.t19); // x2 = u/v
fe_pow22523(s.a, s.c, s); // a = x2^((p-5)/8)
fe_mul(s.a, s.c, s.a, s.t19); // x = x2 * a = x2^((p+3)/8)
fe_sq(s.b, s.a, s.t19);
fe_sub(s.b, s.b, s.c); // x^2 - x2
if (fe_isnonzero(s.b)) {
fe_mul(s.a, s.a, I, s.t19);
fe_sq(s.b, s.a, s.t19);
fe_sub(s.b, s.b, s.c);
if (fe_isnonzero(s.b)) return null;
}
if (fe_isnegative(s.b) != ((b[31] >> 7) & 1)) fe_neg(p.X, s.b);
else fe_copy(p.X, s.b);
if (fe_isnegative(s.a) != ((b[31] >> 7) & 1)) fe_neg(p.X, s.a);
else fe_copy(p.X, s.a);
fe_mul(p.T, p.X, p.Y, s.t19);
return p;
}
@@ -409,7 +434,7 @@ public final class Ed25519 {
public static byte[] fe_contract(long[] h) {
long[] val = Arrays.copyOf(h, 10);
for (int p = 0; p < 3; p++) {
for (int p = 0; p < 2; p++) {
for (int i = 0; i < 9; i++) {
long c = val[i] >> (i % 2 == 0 ? 26 : 25);
val[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL);
+16 -6
View File
@@ -341,14 +341,24 @@ public class Testinator {
private static String ed25519(String workingPath) throws IOException {
Path testPath = Path.of(workingPath, "ed25519.test");
Path resPath = Path.of(workingPath, "ed25519.java.txt");
List<String> seeds = Files.readAllLines(testPath);
List<String> lines = Files.readAllLines(testPath);
List<String> results = new ArrayList<>();
for (String seedHex : seeds) {
if (seedHex.trim().isEmpty()) continue;
byte[] seed = CryptoUtils.hexToBin(seedHex);
byte[] pub = Ed25519.publicKey(seed);
results.add(CryptoUtils.binToHex(pub));
for (String line : lines) {
if (line.trim().isEmpty()) continue;
String[] parts = line.split("\\|");
byte[] seed = CryptoUtils.hexToBin(parts[0]);
byte[] msg = CryptoUtils.hexToBin(parts[1]);
byte[] pub = Ed25519.publicKey(seed);
byte[] sig = Ed25519.sign(seed, msg);
boolean verify = Ed25519.verify(pub, msg, sig);
results.add(CryptoUtils.binToHex(pub) + "|" + CryptoUtils.binToHex(sig) + "|" + verify);
CryptoUtils.wipe(seed);
CryptoUtils.wipe(msg);
CryptoUtils.wipe(pub);
CryptoUtils.wipe(sig);
}
Files.write(resPath, results);
return resPath.toString();
+92 -243
View File
@@ -19,29 +19,24 @@
%%% Logic
mods() ->
#{"base64" => fun base64/0,
"base58" => fun base58/0,
"base58_check" => fun base58_check/0,
"rlp" => fun rlp/0,
"rlp_stream" => fun rlp_stream/0,
"rlp_fail" => fun rlp_fail/0,
"gaju_format" => fun gaju_format/0,
"keccak256" => fun keccak256/0,
"blake2b" => fun blake2b/0,
"ed25519" => fun ed25519/0,
"api_encode" => fun api_encode/0,
"id_serialization" => fun id_serialization/0,
"fe_parity" => fun fe_parity/0,
"reduce_parity" => fun reduce_parity/0,
"smb_parity" => fun smb_parity/0,
"femul_parity" => fun femul_parity/0,
"debug_femul" => fun debug_femul/0,
"frombytes_parity" => fun frombytes_parity/0,
"ge_parity" => fun ge_parity/0,
"gen_consts" => fun gen_consts/0,
"check_product_limbs" => fun check_product_limbs/0,
"check_limbs" => fun check_limbs/0,
"gen_coeffs" => fun gen_coeffs/0}.
#{"base64" => fun base64/0,
"base58" => fun base58/0,
"base58_check" => fun base58_check/0,
"rlp" => fun rlp/0,
"rlp_stream" => fun rlp_stream/0,
"rlp_fail" => fun rlp_fail/0,
"gaju_format" => fun gaju_format/0,
"keccak256" => fun keccak256/0,
"blake2b" => fun blake2b/0,
"ed25519" => fun ed25519/0,
"api_encode" => fun api_encode/0,
"id_serialization" => fun id_serialization/0,
"fe_parity" => fun fe_parity/0,
"reduce_parity" => fun reduce_parity/0,
"smb_parity" => fun smb_parity/0,
"femul_parity" => fun femul_parity/0,
"frombytes_parity" => fun frombytes_parity/0,
"ge_parity" => fun ge_parity/0}.
start([]) ->
@@ -132,7 +127,7 @@ base64() ->
{ok, EDecB} = file:read_file(TestFile),
{ok, JDecB} = file:read_file(JDec),
EBinHash = crypto:hash(sha512, EDecB),
JBinHash = crypto:hash(sha512, JEncB),
JBinHash = crypto:hash(sha512, JDecB),
EHash =:= JHash andalso EBinHash =:= JBinHash.
@@ -300,36 +295,21 @@ ed25519() ->
Temp = temp_dir(),
TestFile = filename:join(Temp, "ed25519.test"),
ok = filelib:ensure_dir(TestFile),
Seeds = [rand:bytes(32) || _ <- lists:seq(1, 10)],
ok = file:write_file(TestFile, unicode:characters_to_binary([[bin_to_hex(S), "\n"] || S <- Seeds])),
Expected = [ed25519_pub(S) || S <- Seeds],
Cases = [{rand:bytes(32), rand:bytes(rand:uniform(100))} || _ <- lists:seq(1, 20)],
ok = file:write_file(TestFile, [[bin_to_hex(S), "|", bin_to_hex(M), "\n"] || {S, M} <- Cases]),
Sequence =
fun({S, M}) ->
#{public := Pub} = ecu_eddsa:sign_seed_keypair(S),
Sig = ecu_eddsa:sign_detached(M, S),
V = ecu_eddsa:sign_verify_detached(Sig, M, Pub),
lists:flatten(io_lib:format("~s|~s|~p", [bin_to_hex(Pub), bin_to_hex(Sig), V]))
end,
Expected = lists:map(Sequence, Cases),
Run = "bin/run ed25519 " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
case length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)) of
true -> true;
false ->
lists:foreach(fun({E, J}) ->
if E =/= J -> io:format("E: ~ts~nJ: ~ts~n", [E, J]); true -> ok end
end, lists:zip(Expected, JResults)),
false
end.
ed25519_pub(Seed) ->
Hash = crypto:hash(sha512, Seed),
<<K:32/binary, _/binary>> = Hash,
% Clamp K
<<K0, KMid:30/binary, K31>> = K,
CK0 = K0 band 248,
CK31 = (K31 band 127) bor 64,
ClampedK = <<CK0, KMid/binary, CK31>>,
Pub = ecu_ed25519:scalar_mul_base(ClampedK),
bin_to_hex(ecu_ed25519:compress(Pub)).
JResults = [trim(L) || L <- string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all)],
same_same(Expected, JResults).
api_encode() ->
@@ -340,15 +320,12 @@ api_encode() ->
Lines = [io_lib:format("~ts|~ts", [string:uppercase(atom_to_list(T)), bin_to_hex(B)]) || {T, B} <- Cases],
ok = filelib:ensure_dir(TestFile),
ok = file:write_file(TestFile, unicode:characters_to_binary([[L, "\n"] || L <- Lines])),
Expected = [flatten(gmser_api_encoder:encode(T, B)) || {T, B} <- Cases],
Run = "bin/run api_encode " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)).
same_same(Expected, JResults).
type_size(signature) -> 64;
type_size(_) -> 32.
@@ -362,40 +339,39 @@ id_serialization() ->
Lines = [io_lib:format("~b|~ts", [Tag, bin_to_hex(B)]) || {Tag, B} <- Cases],
ok = filelib:ensure_dir(TestFile),
ok = file:write_file(TestFile, unicode:characters_to_binary([[L, "\n"] || L <- Lines])),
Expected = [bin_to_hex(gmser_id:encode(gmser_id:create(tag_to_erl(T), B))) || {T, B} <- Cases],
Run = "bin/run id_serialization " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)).
same_same(Expected, JResults).
fe_parity() ->
Temp = temp_dir(),
TestFile = filename:join(Temp, "fe.test"),
ok = filelib:ensure_dir(TestFile),
% Mask to 255 bits to avoid bit-255 sign bit ambiguity in fe_frombytes
Inputs = [begin <<B:256/little>> = rand:bytes(32), bin_to_hex(<< (B band ((1 bsl 255) - 1)):256/little >>) end || _ <- lists:seq(1, 100)],
RandomLittleFingers =
fun() ->
<<B:256/little>> = rand:bytes(32),
bin_to_hex(<<(B band ((1 bsl 255) - 1)):256/little>>)
end,
Inputs = [RandomLittleFingers() || _ <- lists:seq(1, 100)],
ok = file:write_file(TestFile, unicode:characters_to_binary([[I, "\n"] || I <- Inputs])),
Run = "bin/run fe_parity " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
P = ecu_ed25519:p(),
Expected = [begin <<B:256/little>> = hex_to_bin(I), bin_to_hex(pack_p(B rem P)) end || I <- Inputs],
case length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)) of
true -> true;
false ->
lists:foreach(fun({E, J}) ->
if E =/= J -> io:format("E: ~ts~nJ: ~ts~n", [E, J]); true -> ok end
end, lists:zip(Expected, JResults)),
false
end.
Pee =
fun(I) ->
<<B:256/little>> = hex_to_bin(I),
bin_to_hex(pack_p(B rem P))
end,
Expected = lists:map(Pee, Inputs),
same_same(Expected, JResults).
reduce_parity() ->
Temp = temp_dir(),
@@ -403,22 +379,13 @@ reduce_parity() ->
ok = filelib:ensure_dir(TestFile),
Inputs = [rand:bytes(64) || _ <- lists:seq(1, 100)],
ok = file:write_file(TestFile, unicode:characters_to_binary([[bin_to_hex(I), "\n"] || I <- Inputs])),
Run = "bin/run reduce_parity " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
Expected = [bin_to_hex(ecu_ed25519:scalar_reduce(I)) || I <- Inputs],
case length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)) of
true -> true;
false ->
lists:foreach(fun({E, J}) ->
if E =/= J -> io:format("E: ~ts~nJ: ~ts~n", [E, J]); true -> ok end
end, lists:zip(Expected, JResults)),
false
end.
same_same(Expected, JResults).
smb_parity() ->
Temp = temp_dir(),
@@ -426,22 +393,13 @@ smb_parity() ->
ok = filelib:ensure_dir(TestFile),
Inputs = [<<(rand:bytes(31))/binary, 0>> || _ <- lists:seq(1, 10)],
ok = file:write_file(TestFile, unicode:characters_to_binary([[bin_to_hex(I), "\n"] || I <- Inputs])),
Run = "bin/run smb_parity " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
Expected = [bin_to_hex(ecu_ed25519:compress(ecu_ed25519:scalar_mul_base_noclamp(I))) || I <- Inputs],
case length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)) of
true -> true;
false ->
lists:foreach(fun({E, J}) ->
if E =/= J -> io:format("E: ~ts~nJ: ~ts~n", [E, J]); true -> ok end
end, lists:zip(Expected, JResults)),
false
end.
same_same(Expected, JResults).
femul_parity() ->
Temp = temp_dir(),
@@ -452,46 +410,37 @@ femul_parity() ->
Inputs = [{Gen(), Gen()} || _ <- lists:seq(1, 10)],
Lines = [bin_to_hex(A) ++ "|" ++ bin_to_hex(B) || {A, B} <- Inputs],
ok = file:write_file(TestFile, unicode:characters_to_binary([[L, "\n"] || L <- Lines])),
Run = "bin/run femul_parity " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
ExpectedBin = [bin_to_hex(pack_p(ecu_ed25519:f_mul(binary:decode_unsigned(A, little), binary:decode_unsigned(B, little)))) || {A, B} <- Inputs],
same_same3(Inputs, ExpectedBin, JResults).
case length(ExpectedBin) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(ExpectedBin, JResults)) of
true -> true;
false ->
lists:foreach(fun({{A, B}, E, J}) ->
if E =/= J ->
io:format("A: ~ts~nB: ~ts~nE: ~ts~nJ: ~ts~n", [bin_to_hex(A), bin_to_hex(B), E, J]);
true -> ok end
end, lists:zip3(Inputs, ExpectedBin, JResults)),
false
end.
debug_femul() ->
A_bin = <<1, 0:248>>,
B_bin = <<1, 1, 0:240>>,
A = binary:decode_unsigned(A_bin, little),
B = binary:decode_unsigned(B_bin, little),
Res = ecu_ed25519:f_mul(A, B),
io:format("A (hex): ~ts~n", [bin_to_hex(A_bin)]),
io:format("B (hex): ~ts~n", [bin_to_hex(B_bin)]),
io:format("Res (hex): ~ts~n", [bin_to_hex(pack_p(Res))]),
same_same3(AB, L1, L2) ->
same_same3(AB, L1, L2, true).
same_same3([_ | R1], [S | R2], [S | R3], Result) ->
same_same3(R1, R2, R3, Result);
same_same3([{A, B} | R1], [E | R2], [J | R3], _) ->
ok = io:format("A: ~ts~nB: ~ts~nE: ~ts~nJ: ~ts~n", [bin_to_hex(A), bin_to_hex(B), E, J]),
same_same3(R1, R2, R3, false);
same_same3([], [], [], Result) ->
Result.
same_same(L1, L2) ->
same_same(L1, L2, true).
same_same([S | R1], [S | R2], Result) ->
same_same(R1, R2, Result);
same_same([E | R1], [J | R2], _) ->
ok = io:format("Mismatch!\nE: ~ts\nJ: ~ts\n", [E, J]),
same_same(R1, R2, false);
same_same([], [], Result) ->
Result.
Limbs = fun F(Val, Idx) when Idx < 10 ->
Size = if Idx rem 2 =:= 0 -> 26; true -> 25 end,
L = Val band ((1 bsl Size) - 1),
[L | F(Val bsr Size, Idx + 1)];
F(_, _) -> []
end,
io:format("A limbs: ~w~n", [Limbs(A, 0)]),
io:format("B limbs: ~w~n", [Limbs(B, 0)]),
io:format("Res limbs: ~w~n", [Limbs(Res, 0)]),
true.
frombytes_parity() ->
Temp = temp_dir(),
@@ -499,33 +448,31 @@ frombytes_parity() ->
ok = filelib:ensure_dir(TestFile),
Inputs = [rand:bytes(32) || _ <- lists:seq(1, 10)],
ok = file:write_file(TestFile, unicode:characters_to_binary([[bin_to_hex(I), "\n"] || I <- Inputs])),
Run = "bin/run frombytes_parity " ++ Temp,
Out = trim(os:cmd(Run)),
{ok, JOutContent} = file:read_file(trim(Out)),
JResults = string:split(trim(unicode:characters_to_list(JOutContent)), "\n", all),
Expected = [
begin
Limb =
fun(I) ->
Val = binary:decode_unsigned(I, little),
Limbs = fun F(V, Idx) when Idx < 10 ->
Size = if Idx rem 2 =:= 0 -> 26; true -> 25 end,
Limbs =
fun
F(V, Idx) when Idx < 10 ->
Size =
case Idx rem 2 =:= 0 of
true -> 26;
false -> 25
end,
L = V band ((1 bsl Size) - 1),
[L | F(V bsr Size, Idx + 1)];
F(_, _) -> []
end,
F(_, _) ->
[]
end,
string:join([integer_to_list(L) || L <- Limbs(Val, 0)], ",")
end || I <- Inputs],
end,
Expected = lists:map(Limb, Inputs),
same_same(Expected, JResults).
case length(Expected) =:= length(JResults) andalso
lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)) of
true -> true;
false ->
lists:foreach(fun({E, J}) ->
if E =/= J -> io:format("E: ~ts~nJ: ~ts~n", [E, J]); true -> ok end
end, lists:zip(Expected, JResults)),
false
end.
ge_parity() ->
A_bin = <<1, 2, 3, 0:232>>,
@@ -533,49 +480,14 @@ ge_parity() ->
P1_erl = ecu_ed25519:scalar_mul_base_noclamp(A_bin),
P2_erl = ecu_ed25519:scalar_mul_base_noclamp(B_bin),
P3_erl = ecu_ed25519:p_add(P1_erl, P2_erl),
E_comp = bin_to_hex(ecu_ed25519:compress(P3_erl)),
Run = "bin/run ge_parity " ++ bin_to_hex(A_bin) ++ " " ++ bin_to_hex(B_bin),
Out = trim(os:cmd(Run)),
case string:split(Out, "|||") of
[JX | _] -> E_comp =:= trim(JX);
_ -> false
_ -> false
end.
gen_consts() ->
GetLimbs = fun(Val) ->
Limbs = fun F(V, Idx) when Idx < 10 ->
Size = if Idx rem 2 =:= 0 -> 26; true -> 25 end,
L = V band ((1 bsl Size) - 1),
[L | F(V bsr Size, Idx + 1)];
F(_, _) -> []
end,
Limbs(Val, 0)
end,
P = (1 bsl 255) - 19,
Y = (4 * ecu_ed25519:f_inv(5)) rem P,
% X^2 = (y^2 - 1) / (d*y^2 + 1)
Y2 = (Y * Y) rem P,
D = (P - 121665) * ecu_ed25519:f_inv(121666) rem P,
Num = (Y2 - 1 + P) rem P,
Den = (D * Y2 + 1) rem P,
X2 = (Num * ecu_ed25519:f_inv(Den)) rem P,
X = ecu_ed25519:f_pow(X2, (P + 3) bsr 3), % compute square root
T = (X * Y) rem P,
I = ecu_ed25519:f_pow(2, (P - 1) bsr 2),
io:format("BX: ~w~n", [GetLimbs(X)]),
io:format("BY: ~w~n", [GetLimbs(Y)]),
io:format("BXY: ~w~n", [GetLimbs(T)]),
io:format("D: ~w~n", [GetLimbs(D)]),
io:format("D2: ~w~n", [GetLimbs((2 * D) rem P)]),
io:format("I: ~w~n", [GetLimbs(I)]),
true.
pack_p(V) ->
P = (1 bsl 255) - 19,
V_pos = if V < 0 -> V + P; true -> V rem P end,
@@ -583,69 +495,6 @@ pack_p(V) ->
Size = byte_size(Enc),
if Size < 32 -> <<Enc/binary, 0:(8*(32-Size))>>; true -> Enc end.
check_limbs() ->
% Random values
A_bin = <<11, 22, 33, 0:232>>,
B_bin = <<44, 55, 66, 0:232>>,
A = binary:decode_unsigned(A_bin, little),
B = binary:decode_unsigned(B_bin, little),
Res = A * B,
Limbs = fun F(Val, Idx) when Idx < 19 ->
Size = if Idx rem 2 =:= 0 -> 26; true -> 25 end,
L = Val band ((1 bsl Size) - 1),
[L | F(Val bsr Size, Idx + 1)];
F(_, _) -> []
end,
io:format("Expected product limbs: ~w~n", [Limbs(Res, 0)]),
true.
check_product_limbs() ->
A_bin = <<11, 22, 33, 44, 0:224>>,
B_bin = <<55, 66, 77, 88, 0:224>>,
A = binary:decode_unsigned(A_bin, little),
B = binary:decode_unsigned(B_bin, little),
GetLimbs = fun(Val) ->
Limbs = fun F(V, Idx) when Idx < 10 ->
Size = if Idx rem 2 =:= 0 -> 26; true -> 25 end,
L = V band ((1 bsl Size) - 1),
[L | F(V bsr Size, Idx + 1)];
F(_, _) -> []
end,
list_to_tuple(Limbs(Val, 0))
end,
{A0, A1, A2, A3, A4, A5, A6, A7, A8, A9} = GetLimbs(A),
{B0, B1, B2, B3, B4, B5, B6, B7, B8, B9} = GetLimbs(B),
T0 = A0*B0,
T1 = A0*B1 + A1*B0,
T2 = 2*A1*B1 + A0*B2 + A2*B0,
T3 = A1*B2 + A2*B1 + A0*B3 + A3*B0,
T4 = A2*B2 + 2*(A1*B3 + A3*B1) + A0*B4 + A4*B0,
io:format("T0: ~b, T1: ~b, T2: ~b, T3: ~b, T4: ~b~n", [T0, T1, T2, T3, T4]),
true.
gen_coeffs() ->
W = [0, 26, 51, 77, 102, 128, 153, 179, 204, 230],
lists:foreach(fun(TargetIdx) ->
io:format("Limb ~b: ", [TargetIdx]),
_ = [
begin
Weight = lists:nth(I+1, W) + lists:nth(J+1, W),
TargetWeight = lists:nth(TargetIdx+1, W),
if Weight >= TargetWeight andalso Weight < TargetWeight + 26 ->
Diff = Weight - TargetWeight,
io:format("A~b*B~b*~b + ", [I, J, 1 bsl Diff]);
true -> ok
end
end || I <- lists:seq(0, 9), J <- lists:seq(0, 9)],
io:format("~n")
end, lists:seq(0, 18)),
true.
tag_to_erl(1) -> account;
tag_to_erl(2) -> name;
tag_to_erl(3) -> commitment;