diff --git a/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java b/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java index 2563efd..2f7f0fe 100644 --- a/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java +++ b/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java @@ -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); diff --git a/test/Testinator.java b/test/Testinator.java index 03201da..ef25b6c 100644 --- a/test/Testinator.java +++ b/test/Testinator.java @@ -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 seeds = Files.readAllLines(testPath); + List lines = Files.readAllLines(testPath); List 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(); diff --git a/test/src/gmt.erl b/test/src/gmt.erl index 4bc05fe..8079a00 100644 --- a/test/src/gmt.erl +++ b/test/src/gmt.erl @@ -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), - <> = Hash, - % Clamp K - <> = K, - CK0 = K0 band 248, - CK31 = (K31 band 127) bor 64, - ClampedK = <>, - 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 <> = rand:bytes(32), bin_to_hex(<< (B band ((1 bsl 255) - 1)):256/little >>) end || _ <- lists:seq(1, 100)], + RandomLittleFingers = + fun() -> + <> = 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 <> = 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) -> + <> = 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 -> <>; 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;