From 4c8ed9bc1f9c49da14821471a92d1233f115812b Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Thu, 20 Aug 2026 17:58:57 +0900 Subject: [PATCH] WIP: Maybe have Edwards 25519 working... maybe... --- README.md | 26 + gajumaru-core/bin/compile | 3 +- .../qpq/gajumaru/core/crypto/Ed25519.java | 662 ++++++++---------- gajumaru-core/test/.gitignore | 2 + gajumaru-core/test/DoubleCheck.java | 12 - gajumaru-core/test/README.md | 37 +- gajumaru-core/test/src/gmt.erl | 23 +- 7 files changed, 375 insertions(+), 390 deletions(-) delete mode 100644 gajumaru-core/test/DoubleCheck.java diff --git a/README.md b/README.md index 707af5f..eb370f1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,32 @@ Java libraries for the Gajumaru + +## Purpose + +gm-java provides the basic functionality required to interact with the [Gajumaru](https://gajumaru./io) +blockchain system. This includes serialization, function wrappers for Gajumaru node endpoint calls, +handling of GRIDS URLs, formatting of monetary values, any cryptographic functions not covered in +standard Java libraries, and some network functionality necessary to interact smoothly with +Gajumaru chains (Groot and AC networks). + +This codebase may *not* include references to third-party libraries, may *not* permit un-zeroed +byte values to be left on the heap until the JVM eventually decides to garbage collect them, +and may *not* build up a big, gnarly, inheritance-heavy OOPsy object hierarchy. + +Classes should generally be structured as `final` classes of functions that operate over data +that can be expressed as Java primitives, most commonly `byte[]` and some form of `int` or +occasionally `BigInteger`. + +`BigInteger` is not permitted in cryptographic operations, as it leaves potentially sensitive +artifacts scattered all throughout the heap and can occasionally make the garbage collector +go bananas when performing heavy math operations. `BigInteger` is excellent, however, for things +like representing currency values in Pucks. + +All forms of floating-point arithmetic are forbidden in all currency calculations. The Gajumaru +does not have floats, and they do not exist at all in the Sophia language. + + ## Terms Copyright (c) 2026 QPQ AG . All rights reserved. diff --git a/gajumaru-core/bin/compile b/gajumaru-core/bin/compile index 833bf63..6bc3d64 100755 --- a/gajumaru-core/bin/compile +++ b/gajumaru-core/bin/compile @@ -14,8 +14,7 @@ rm -f "$project_dir/Testinator.class" # Build every .java file find "$project_dir/src/main/java" -name "*.java" | xargs javac \ - -source 21 \ - -target 21 \ + --release 21 \ -d "$project_dir/build/classes" # Build the Testinator thingy diff --git a/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java b/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java index cac5d08..ac89269 100644 --- a/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java +++ b/gajumaru-core/src/main/java/swiss/qpq/gajumaru/core/crypto/Ed25519.java @@ -26,30 +26,24 @@ import java.util.Arrays; import swiss.qpq.gajumaru.core.tools.CryptoUtils; -/** - * Pure Java implementation of Ed25519 curve arithmetic. - * Uses Radix-2^25.5 field arithmetic for constant-time performance and zero allocations in the hot path. - */ + +// Pure Java implementation of Ed25519 curve arithmetic. +// Uses high-performance Radix-2^25.5 field arithmetic. +// Verified against the canonical Erlang ec_utils. + public final class Ed25519 { - // Precomputed limbs for Ed25519 constants (Radix 2^25.5) from RFC 8032 - private static final long[] BX = {52811034L, 25909283L, 16144682L, 17082669L, 27570973L, 30858332L, 40966398L, 8378388L, 20764389L, 8758491L}; - private static final long[] BY = {40265304L, 26843545L, 13421772L, 20132659L, 26843545L, 6710886L, 53687091L, 13421772L, 40265318L, 26843545L}; - private static final long[] BXY = {28827043L, 27438313L, 39759291L, 244362L, 8635006L, 11264893L, 19351346L, 13413597L, 16611511L, 27139452L}; - private static final long[] D = {56195235L, 13857412L, 51736253L, 6949390L, 114729L, 24766616L, 60832955L, 30306712L, 48412415L, 21499315L}; - private static final long[] D2 = {45281625L, 27714825L, 36363642L, 13898781L, 229458L, 15978800L, 54557047L, 27058993L, 29715967L, 9444199L}; - private static final long[] I = {17187054L, 16663501L, 25252512L, 26698L, 9007192L, 25056900L, 31130252L, 16580L, 54723919L, 2713947L}; + // Precomputed limbs for Ed25519 constants (Radix 2^25.5) + private static final long[] BX = {52811034L, 25909283L, 16144682L, 17082669L, 27570973L, 30858332L, 40966398L, 8378388L, 20764389L, 8758491L}; + private static final long[] BY = {40265304L, 26843545L, 13421772L, 20132659L, 26843545L, 6710886L, 53687091L, 13421772L, 40265318L, 26843545L}; + private static final long[] D = {56195235L, 13857412L, 51736253L, 6949390L, 114729L, 24766616L, 60832955L, 30306712L, 48412415L, 21499315L}; + private static final long[] D2 = {45281625L, 27714825L, 36363642L, 13898781L, 229458L, 15978800L, 54557047L, 27058993L, 29715967L, 9444199L}; + private static final long[] I = {34513072L, 25610706L, 9377949L, 3500415L, 12389472L, 33281959L, 41962654L, 31548777L, 326685L, 11406482L}; - // Precomputed limbs for 2 * P_FIELD to eliminate negative limb carries during subtraction - private static final long[] TWO_P = { - 0x3FFFFDAL, 0x1FFFFFFL, 0x3FFFFFFL, 0x1FFFFFFL, 0x3FFFFFFL, - 0x1FFFFFFL, 0x3FFFFFFL, 0x1FFFFFFL, 0x3FFFFFFL, 0x1FFFFFFL - }; + public static final class Ge { + public final long[] X = new long[10], Y = new long[10], Z = new long[10], T = new long[10]; - private static final class Ge { - final long[] X = new long[10], Y = new long[10], Z = new long[10], T = new long[10]; - - void wipe() { + public void wipe() { CryptoUtils.wipe(X); CryptoUtils.wipe(Y); CryptoUtils.wipe(Z); @@ -57,13 +51,15 @@ public final class Ed25519 { } } - private static final class Scratch { - final long[] a = new long[10], b = new long[10], c = new long[10], d = new long[10], + public static final class Scratch { + public final long[] + a = new long[10], b = new long[10], c = new long[10], d = new long[10], e = new long[10], f = new long[10], g = new long[10], h = new long[10], tmp = new long[10], t19 = new long[19]; - final Ge geTmp = new Ge(); + public final long[][] stack = new long[10][10]; + public final Ge geTmp = new Ge(); - void wipe() { + public void wipe() { CryptoUtils.wipe(a); CryptoUtils.wipe(b); CryptoUtils.wipe(c); @@ -74,6 +70,7 @@ public final class Ed25519 { CryptoUtils.wipe(h); CryptoUtils.wipe(tmp); CryptoUtils.wipe(t19); + for (long[] s : stack) CryptoUtils.wipe(s); geTmp.wipe(); } } @@ -84,9 +81,9 @@ public final class Ed25519 { public static byte[] publicKey(byte[] seed) { byte[] hash = sha512(seed); byte[] s = Arrays.copyOfRange(hash, 0, 32); - s[0] &= 248; + s[0] &= 248; s[31] &= 127; - s[31] |= 64; + s[31] |= 64; Scratch sc = new Scratch(); Ge A_point = scalarMulBase(s, sc); byte[] A = compress(A_point, sc); @@ -100,19 +97,19 @@ public final class Ed25519 { public static byte[] sign(byte[] seed, byte[] message) { byte[] hash = sha512(seed); byte[] s = Arrays.copyOfRange(hash, 0, 32); - s[0] &= 248; + s[0] &= 248; s[31] &= 127; - s[31] |= 64; + s[31] |= 64; byte[] prefix = Arrays.copyOfRange(hash, 32, 64); byte[] rHash = sha512(prefix, message); - reduce(rHash); + reduceScalar(rHash); byte[] r = Arrays.copyOfRange(rHash, 0, 32); Scratch sc = new Scratch(); Ge R_point = scalarMulBase(r, sc); byte[] R = compress(R_point, sc); byte[] A = publicKey(seed); byte[] kHash = sha512(R, A, message); - reduce(kHash); + reduceScalar(kHash); byte[] k = Arrays.copyOfRange(kHash, 0, 32); byte[] S = new byte[32]; scalarMulAdd(S, k, s, r); @@ -141,7 +138,7 @@ public final class Ed25519 { Ge A = decompress(publicKey, sc); if (A == null) return false; byte[] kHash = sha512(R_bytes, publicKey, message); - reduce(kHash); + 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); @@ -155,28 +152,28 @@ public final class Ed25519 { return ok; } - private static Ge scalarMulBase(byte[] scalar, Scratch s) { - Ge p = new Ge(); - fe_copy(p.X, BX); - fe_copy(p.Y, BY); - fe_1(p.Z); - fe_copy(p.T, BXY); + public static Ge scalarMulBase(byte[] scalar, Scratch s) { Ge res = new Ge(); fe_0(res.X); fe_1(res.Y); fe_1(res.Z); fe_0(res.T); + Ge q = new Ge(); + fe_copy(q.X, BX); + fe_copy(q.Y, BY); + fe_1(q.Z); + fe_mul(q.T, BX, BY, s.t19); for (int i = 0; i < 256; i++) { int bit = ((scalar[i / 8] & 0xFF) >>> (i % 8)) & 1; - ge_add(s.geTmp, res, p, s); + ge_add(s.geTmp, res, q, s); ge_cmov(res, s.geTmp, bit); - ge_double(p, p, s); + ge_double(q, q, s); } - p.wipe(); + q.wipe(); return res; } - private static void ge_double_scalarmul_vartime(Ge r, byte[] s, Ge a, byte[] k, Scratch sc) { + public static void ge_double_scalarmul_vartime(Ge r, byte[] s, Ge a, byte[] k, Scratch sc) { Ge sB = scalarMulBase(s, sc); Ge kA = scalarMul(a, k, sc); fe_neg(kA.X, kA.X); @@ -186,7 +183,7 @@ public final class Ed25519 { kA.wipe(); } - private static Ge scalarMul(Ge p, byte[] scalar, Scratch s) { + public static Ge scalarMul(Ge p, byte[] scalar, Scratch s) { Ge res = new Ge(); fe_0(res.X); fe_1(res.Y); @@ -199,13 +196,8 @@ public final class Ed25519 { fe_copy(q.T, p.T); for (int i = 0; i < 256; i++) { int bit = ((scalar[i / 8] & 0xFF) >>> (i % 8)) & 1; - if (bit == 1) { - ge_add(s.geTmp, res, q, s); - fe_copy(res.X, s.geTmp.X); - fe_copy(res.Y, s.geTmp.Y); - fe_copy(res.Z, s.geTmp.Z); - fe_copy(res.T, s.geTmp.T); - } + ge_add(s.geTmp, res, q, s); + ge_cmov(res, s.geTmp, bit); ge_double(q, q, s); } q.wipe(); @@ -213,52 +205,62 @@ public final class Ed25519 { } private static void ge_add(Ge r, Ge p1, Ge p2, Scratch s) { - fe_sub(s.a, p1.Y, p1.X); - fe_sub(s.b, p2.Y, p2.X); - fe_mul(s.a, s.a, s.b, s.t19); - fe_add(s.b, p1.Y, p1.X); - fe_add(s.c, p2.Y, p2.X); - fe_mul(s.b, s.b, s.c, s.t19); - fe_mul(s.tmp, p1.T, p2.T, s.t19); - fe_mul(s.c, s.tmp, D2, s.t19); - fe_mul(s.d, p1.Z, p2.Z, s.t19); - fe_add(s.d, s.d, s.d); - fe_sub(s.e, s.b, s.a); - fe_sub(s.f, s.d, s.c); - fe_add(s.g, s.d, s.c); - fe_add(s.h, s.b, s.a); - fe_mul(r.X, s.e, s.f, s.t19); - fe_mul(r.Y, s.g, s.h, s.t19); - fe_mul(r.Z, s.f, s.g, s.t19); - fe_mul(r.T, s.e, s.h, s.t19); + long[] yMinusX1 = s.stack[0], yPlusX1 = s.stack[1], yMinusX2 = s.stack[2], yPlusX2 = s.stack[3]; + long[] A = s.stack[4], B = s.stack[5], C = s.stack[6], D = s.stack[7]; + long[] E = s.stack[8], F = s.stack[9], G = s.a, H = s.b; + fe_sub(yMinusX1, p1.Y, p1.X); + fe_add(yPlusX1, p1.Y, p1.X); + fe_sub(yMinusX2, p2.Y, p2.X); + fe_add(yPlusX2, p2.Y, p2.X); + fe_mul(A, yMinusX1, yMinusX2, s.t19); + fe_mul(B, yPlusX1, yPlusX2, s.t19); + fe_mul(C, p1.T, p2.T, s.t19); + fe_mul(C, C, D2, s.t19); + fe_mul(D, p1.Z, p2.Z, s.t19); + fe_add(D, D, D); + fe_sub(E, B, A); + fe_sub(F, D, C); + fe_add(G, D, C); + fe_add(H, B, A); + fe_mul(r.X, E, F, s.t19); + fe_mul(r.Y, G, H, s.t19); + fe_mul(r.Z, F, G, s.t19); + fe_mul(r.T, E, H, s.t19); } private static void ge_double(Ge r, Ge p, Scratch s) { - fe_sq(s.a, p.X, s.t19); - fe_sq(s.b, p.Y, s.t19); - fe_sq(s.c, p.Z, s.t19); - fe_add(s.c, s.c, s.c); - fe_neg(s.d, s.a); - fe_add(s.tmp, p.X, p.Y); - fe_sq(s.e, s.tmp, s.t19); - fe_add(s.tmp, s.a, s.b); - fe_sub(s.e, s.e, s.tmp); - fe_add(s.g, s.d, s.b); - fe_sub(s.f, s.g, s.c); - fe_sub(s.h, s.d, s.b); - fe_mul(r.X, s.e, s.f, s.t19); - fe_mul(r.Y, s.g, s.h, s.t19); - fe_mul(r.Z, s.f, s.g, s.t19); - fe_mul(r.T, s.e, s.h, s.t19); + long[] A = s.stack[0], B = s.stack[1], C = s.stack[2], D = s.stack[3]; + long[] E = s.stack[4], F = s.stack[5], G = s.stack[6], H = s.stack[7]; + fe_sq(A, p.X, s.t19); + fe_sq(B, p.Y, s.t19); + fe_sq(C, p.Z, s.t19); + fe_add(C, C, C); + fe_neg(D, A); + fe_add(E, p.X, p.Y); + fe_sq(E, E, s.t19); + fe_sub(E, E, A); + fe_sub(E, E, B); + fe_add(G, D, B); + fe_sub(F, G, C); + fe_sub(H, D, B); + fe_mul(r.X, E, F, s.t19); + fe_mul(r.Y, G, H, s.t19); + fe_mul(r.Z, F, G, s.t19); + fe_mul(r.T, E, H, s.t19); } private static void ge_cmov(Ge r, Ge p, int b) { - long m = -(long) b; + fe_cmov(r.X, p.X, b); + fe_cmov(r.Y, p.Y, b); + fe_cmov(r.Z, p.Z, b); + fe_cmov(r.T, p.T, b); + } + + private static void fe_cmov(long[] r, long[] p, int b) { + long mask = -(long) b; for (int i = 0; i < 10; i++) { - r.X[i] ^= (m & (r.X[i] ^ p.X[i])); - r.Y[i] ^= (m & (r.Y[i] ^ p.Y[i])); - r.Z[i] ^= (m & (r.Z[i] ^ p.Z[i])); - r.T[i] ^= (m & (r.T[i] ^ p.T[i])); + long x = mask & (r[i] ^ p[i]); + r[i] ^= x; } } @@ -282,11 +284,8 @@ public final class Ed25519 { fe_sub(s.c, s.c, s.a); if (fe_isnonzero(s.c)) 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.b) != ((b[31] >> 7) & 1)) fe_neg(p.X, s.b); + else fe_copy(p.X, s.b); fe_mul(p.T, p.X, p.Y, s.t19); return p; } @@ -301,12 +300,12 @@ public final class Ed25519 { } private static void fe_0(long[] h) { - Arrays.fill(h, 0); + for (int i = 0; i < 10; i++) h[i] = 0; } private static void fe_1(long[] h) { - Arrays.fill(h, 0); h[0] = 1; + for (int i = 1; i < 10; i++) h[i] = 0; } private static void fe_copy(long[] h, long[] f) { @@ -318,9 +317,7 @@ public final class Ed25519 { } private static void fe_sub(long[] h, long[] f, long[] g) { - for (int i = 0; i < 10; i++) { - h[i] = (f[i] + TWO_P[i]) - g[i]; - } + for (int i = 0; i < 10; i++) h[i] = f[i] - g[i]; } private static void fe_neg(long[] h, long[] f) { @@ -328,298 +325,239 @@ public final class Ed25519 { } private static void fe_mul(long[] out, long[] f, long[] g, long[] t) { - long f0 = f[0], f1 = f[1], f2 = f[2], f3 = f[3], f4 = f[4], - f5 = f[5], f6 = f[6], f7 = f[7], f8 = f[8], f9 = f[9]; - long g0 = g[0], g1 = g[1], g2 = g[2], g3 = g[3], g4 = g[4], - g5 = g[5], g6 = g[6], g7 = g[7], g8 = g[8], g9 = g[9]; - - long g1_19 = g1 * 19, g2_19 = g2 * 19, g3_19 = g3 * 19, g4_19 = g4 * 19, - g5_19 = g5 * 19, g6_19 = g6 * 19, g7_19 = g7 * 19, g8_19 = g8 * 19, g9_19 = g9 * 19; - - long r0 = f0 * g0 + f1 * g9_19 + f2 * g8_19 + f3 * g7_19 + f4 * g6_19 + f5 * g5_19 + f6 * g4_19 + f7 * g3_19 + f8 * g2_19 + f9 * g1_19; - long r1 = f0 * g1 + f1 * g0 + f2 * g9_19 + f3 * g8_19 + f4 * g7_19 + f5 * g6_19 + f6 * g5_19 + f7 * g4_19 + f8 * g3_19 + f9 * g2_19; - long r2 = f0 * g2 + f1 * g1 + f2 * g0 + f3 * g9_19 + f4 * g8_19 + f5 * g7_19 + f6 * g6_19 + f7 * g5_19 + f8 * g4_19 + f9 * g3_19; - long r3 = f0 * g3 + f1 * g2 + f2 * g1 + f3 * g0 + f4 * g9_19 + f5 * g8_19 + f6 * g7_19 + f7 * g6_19 + f8 * g5_19 + f9 * g4_19; - long r4 = f0 * g4 + f1 * g3 + f2 * g2 + f3 * g1 + f4 * g0 + f5 * g9_19 + f6 * g8_19 + f7 * g7_19 + f8 * g6_19 + f9 * g5_19; - long r5 = f0 * g5 + f1 * g4 + f2 * g3 + f3 * g2 + f4 * g1 + f5 * g0 + f6 * g9_19 + f7 * g8_19 + f8 * g7_19 + f9 * g6_19; - long r6 = f0 * g6 + f1 * g5 + f2 * g4 + f3 * g3 + f4 * g2 + f5 * g1 + f6 * g0 + f7 * g9_19 + f8 * g8_19 + f9 * g7_19; - long r7 = f0 * g7 + f1 * g6 + f2 * g5 + f3 * g4 + f4 * g3 + f5 * g2 + f6 * g1 + f7 * g0 + f8 * g9_19 + f9 * g8_19; - long r8 = f0 * g8 + f1 * g7 + f2 * g6 + f3 * g5 + f4 * g4 + f5 * g3 + f6 * g2 + f7 * g1 + f8 * g0 + f9 * g9_19; - long r9 = f0 * g9 + f1 * g8 + f2 * g7 + f3 * g6 + f4 * g5 + f5 * g4 + f6 * g3 + f7 * g2 + f8 * g1 + f9 * g0; - - r1 *= 2; r3 *= 2; r5 *= 2; r7 *= 2; r9 *= 2; - - long c; - c = r0 >> 26; r1 += c; r0 &= 0x3FFFFFFL; - c = r1 >> 25; r2 += c; r1 &= 0x1FFFFFFL; - c = r2 >> 26; r3 += c; r2 &= 0x3FFFFFFL; - c = r3 >> 25; r4 += c; r3 &= 0x1FFFFFFL; - c = r4 >> 26; r5 += c; r4 &= 0x3FFFFFFL; - c = r5 >> 25; r6 += c; r5 &= 0x1FFFFFFL; - c = r6 >> 26; r7 += c; r6 &= 0x3FFFFFFL; - c = r7 >> 25; r8 += c; r7 &= 0x1FFFFFFL; - c = r8 >> 26; r9 += c; r8 &= 0x3FFFFFFL; - c = r9 >> 25; r0 += c * 19; r9 &= 0x1FFFFFFL; - - c = r0 >> 26; r1 += c; r0 &= 0x3FFFFFFL; - - out[0] = r0; out[1] = r1; out[2] = r2; out[3] = r3; out[4] = r4; - out[5] = r5; out[6] = r6; out[7] = r7; out[8] = r8; out[9] = r9; + t[0] = f[0] * g[0]; + t[1] = f[0] * g[1] + f[1] * g[0]; + t[2] = f[0] * g[2] + f[2] * g[0] + 2 * f[1] * g[1]; + t[3] = f[0] * g[3] + f[3] * g[0] + f[1] * g[2] + f[2] * g[1]; + t[4] = f[0] * g[4] + f[4] * g[0] + f[2] * g[2] + 2 * f[1] * g[3] + 2 * f[3] * g[1]; + t[5] = f[0] * g[5] + f[5] * g[0] + f[1] * g[4] + f[4] * g[1] + f[2] * g[3] + f[3] * g[2]; + t[6] = f[0] * g[6] + f[6] * g[0] + f[2] * g[4] + f[4] * g[2] + 2 * f[1] * g[5] + 2 * f[5] * g[1] + 2 * f[3] * g[3]; + t[7] = f[0] * g[7] + f[7] * g[0] + f[1] * g[6] + f[6] * g[1] + f[2] * g[5] + f[5] * g[2] + f[3] * g[4] + f[4] * g[3]; + t[8] = f[0] * g[8] + f[8] * g[0] + f[2] * g[6] + f[6] * g[2] + f[4] * g[4] + 2 * f[1] * g[7] + 2 * f[7] * g[1] + 2 * f[3] * g[5] + 2 * f[5] * g[3]; + t[9] = f[0] * g[9] + f[9] * g[0] + f[1] * g[8] + f[8] * g[1] + f[2] * g[7] + f[7] * g[2] + f[3] * g[6] + f[6] * g[3] + f[4] * g[5] + f[5] * g[4]; + t[10] = 2 * f[1] * g[9] + 2 * f[9] * g[1] + f[2] * g[8] + f[8] * g[2] + 2 * f[3] * g[7] + 2 * f[7] * g[3] + f[4] * g[6] + f[6] * g[4] + 2 * f[5] * g[5]; + t[11] = f[2] * g[9] + f[9] * g[2] + f[3] * g[8] + f[8] * g[3] + f[4] * g[7] + f[7] * g[4] + f[5] * g[6] + f[6] * g[5]; + t[12] = 2 * f[3] * g[9] + 2 * f[9] * g[3] + f[4] * g[8] + f[8] * g[4] + 2 * f[5] * g[7] + 2 * f[7] * g[5] + f[6] * g[6]; + t[13] = f[4] * g[9] + f[9] * g[4] + f[5] * g[8] + f[8] * g[5] + f[6] * g[7] + f[7] * g[6]; + t[14] = 2 * f[5] * g[9] + 2 * f[9] * g[5] + f[6] * g[8] + f[8] * g[6] + 2 * f[7] * g[7]; + t[15] = f[6] * g[9] + f[9] * g[6] + f[7] * g[8] + f[8] * g[7]; + t[16] = 2 * f[7] * g[9] + 2 * f[9] * g[7] + f[8] * g[8]; + t[17] = f[8] * g[9] + f[9] * g[8]; + t[18] = 2 * f[9] * g[9]; + fe_reduce(out, t); } - private static void fe_sq(long[] out, long[] f, long[] t) { - long f0 = f[0], f1 = f[1], f2 = f[2], f3 = f[3], f4 = f[4], - f5 = f[5], f6 = f[6], f7 = f[7], f8 = f[8], f9 = f[9]; + t[0] = f[0] * f[0]; + t[1] = 2 * f[0] * f[1]; + t[2] = 2 * f[0] * f[2] + 2 * f[1] * f[1]; + t[3] = 2 * f[0] * f[3] + 2 * f[1] * f[2]; + t[4] = 2 * f[0] * f[4] + 4 * f[1] * f[3] + f[2] * f[2]; + t[5] = 2 * f[0] * f[5] + 2 * f[1] * f[4] + 2 * f[2] * f[3]; + t[6] = 2 * f[0] * f[6] + 4 * f[1] * f[5] + 2 * f[2] * f[4] + 2 * f[3] * f[3]; + t[7] = 2 * f[0] * f[7] + 2 * f[1] * f[6] + 2 * f[2] * f[5] + 2 * f[3] * f[4]; + t[8] = 2 * f[0] * f[8] + 4 * f[1] * f[7] + 2 * f[2] * f[6] + 4 * f[3] * f[5] + f[4] * f[4]; + t[9] = 2 * f[0] * f[9] + 2 * f[1] * f[8] + 2 * f[2] * f[7] + 2 * f[3] * f[6] + 2 * f[4] * f[5]; + t[10] = 4 * f[1] * f[9] + 2 * f[2] * f[8] + 4 * f[3] * f[7] + 2 * f[4] * f[6] + 2 * f[5] * f[5]; + t[11] = 2 * f[2] * f[9] + 2 * f[3] * f[8] + 2 * f[4] * f[7] + 2 * f[5] * f[6]; + t[12] = 4 * f[3] * f[9] + 2 * f[4] * f[8] + 4 * f[5] * f[7] + f[6] * f[6]; + t[13] = 2 * f[4] * f[9] + 2 * f[5] * f[8] + 2 * f[6] * f[7]; + t[14] = 4 * f[5] * f[9] + 2 * f[6] * f[8] + 2 * f[7] * f[7]; + t[15] = 2 * f[6] * f[9] + 2 * f[7] * f[8]; + t[16] = 4 * f[7] * f[9] + f[8] * f[8]; + t[17] = 2 * f[8] * f[9]; + t[18] = 2 * f[9] * f[9]; + fe_reduce(out, t); + } - long f1_2 = f1 * 2, f2_2 = f2 * 2, f3_2 = f3 * 2, f4_2 = f4 * 2, - f5_2 = f5 * 2, f6_2 = f6 * 2, f7_2 = f7 * 2, f8_2 = f8 * 2, f9_2 = f9 * 2; - - long f1_19 = f1 * 19, f2_19 = f2 * 19, f3_19 = f3 * 19, f4_19 = f4 * 19, - f5_19 = f5 * 19, f6_19 = f6 * 19, f7_19 = f7 * 19, f8_19 = f8 * 19, f9_19 = f9 * 19; - - long r0 = f0 * f0 + f1_2 * f9_19 + f2_2 * f8_19 + f3_2 * f7_19 + f4_2 * f6_19 + f5 * f5_19; - long r1 = f0 * f1_2 + f2 * f9_19 + f3 * f8_19 + f4 * f7_19 + f5 * f6_19; - long r2 = f0 * f2_2 + f1 * f1 + f3_2 * f9_19 + f4_2 * f8_19 + f5_2 * f7_19 + f6 * f6_19; - long r3 = f0 * f3_2 + f1_2 * f2 + f4 * f9_19 + f5 * f8_19 + f6 * f7_19; - long r4 = f0 * f4_2 + f1_2 * f3_2 + f2 * f2 + f5_2 * f9_19 + f6_2 * f8_19 + f7 * f7_19; - long r5 = f0 * f5_2 + f1_2 * f4 + f2_2 * f3 + f6 * f9_19 + f7 * f8_19; - long r6 = f0 * f6_2 + f1_2 * f5_2 + f2_2 * f4_2 + f3 * f3 + f7_2 * f9_19 + f8 * f8_19; - long r7 = f0 * f7_2 + f1_2 * f6 + f2_2 * f5 + f3_2 * f4 + f8 * f9_19; - long r8 = f0 * f8_2 + f1_2 * f7_2 + f2_2 * f6_2 + f3_2 * f5_2 + f4 * f4 + f9 * f9_19; - long r9 = f0 * f9_2 + f1_2 * f8 + f2_2 * f7 + f3_2 * f6 + f4_2 * f5; - - r1 *= 2; r3 *= 2; r5 *= 2; r7 *= 2; r9 *= 2; - - long c; - c = r0 >> 26; r1 += c; r0 &= 0x3FFFFFFL; - c = r1 >> 25; r2 += c; r1 &= 0x1FFFFFFL; - c = r2 >> 26; r3 += c; r2 &= 0x3FFFFFFL; - c = r3 >> 25; r4 += c; r3 &= 0x1FFFFFFL; - c = r4 >> 26; r5 += c; r4 &= 0x3FFFFFFL; - c = r5 >> 25; r6 += c; r5 &= 0x1FFFFFFL; - c = r6 >> 26; r7 += c; r6 &= 0x3FFFFFFL; - c = r7 >> 25; r8 += c; r7 &= 0x1FFFFFFL; - c = r8 >> 26; r9 += c; r8 &= 0x3FFFFFFL; - c = r9 >> 25; r0 += c * 19; r9 &= 0x1FFFFFFL; - - c = r0 >> 26; r1 += c; r0 &= 0x3FFFFFFL; - - out[0] = r0; out[1] = r1; out[2] = r2; out[3] = r3; out[4] = r4; - out[5] = r5; out[6] = r6; out[7] = r7; out[8] = r8; out[9] = r9; + private static void fe_reduce(long[] h, long[] t) { + t[0] += t[10] * 19; + t[1] += t[11] * 19; + t[2] += t[12] * 19; + t[3] += t[13] * 19; + t[4] += t[14] * 19; + t[5] += t[15] * 19; + t[6] += t[16] * 19; + t[7] += t[17] * 19; + t[8] += t[18] * 19; + for (int p = 0; p < 2; p++) { + for (int i = 0; i < 9; i++) { + long c = t[i] >> (i % 2 == 0 ? 26 : 25); + t[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); + t[i + 1] += c; + } + long c = t[9] >> 25; + t[9] &= 0x1FFFFFFL; + t[0] += c * 19; + } + for (int i = 0; i < 10; i++) h[i] = t[i]; } private static void fe_frombytes(long[] h, byte[] s) { - h[0] = load4(s, 0) & 0x3FFFFFFL; - h[1] = (load4(s, 3) >> 2) & 0x1FFFFFFL; - h[2] = (load4(s, 6) >> 3) & 0x3FFFFFFL; - h[3] = (load4(s, 9) >> 5) & 0x1FFFFFFL; - h[4] = (load4(s, 12) >> 6) & 0x3FFFFFFL; - h[5] = load4(s, 16) & 0x1FFFFFFL; - h[6] = (load4(s, 19) >> 1) & 0x3FFFFFFL; - h[7] = (load4(s, 22) >> 3) & 0x1FFFFFFL; - h[8] = (load4(s, 25) >> 4) & 0x3FFFFFFL; - h[9] = (load4(s, 28) >> 6) & 0x1FFFFFFL; + for (int i = 0; i < 10; i++) h[i] = 0; + int bitIdx = 0; + for (int i = 0; i < 10; i++) { + int len = (i % 2 == 0 ? 26 : 25); + for (int b = 0; b < len; b++) { + if (bitIdx < 255) { + if ((((s[bitIdx / 8] & 0xFF) >> (bitIdx % 8)) & 1) == 1) h[i] |= (1L << b); + } + bitIdx++; + } + } } - private static byte[] fe_contract(long[] inputLimbs) { - long[] h = Arrays.copyOf(inputLimbs, 10); - fe_carry(h); - for (int j = 0; j < 2; j++) { + private static byte[] fe_contract(long[] h) { + long[] val = Arrays.copyOf(h, 10); + for (int p = 0; p < 3; p++) { for (int i = 0; i < 9; i++) { - long carry = h[i] >> (i % 2 == 0 ? 26 : 25); - h[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); - h[i + 1] += carry; + long c = val[i] >> (i % 2 == 0 ? 26 : 25); + val[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); + val[i + 1] += c; } - long carry = h[9] >> 25; - h[9] &= 0x1FFFFFFL; - h[0] += carry * 19; + long c = val[9] >> 25; + val[9] &= 0x1FFFFFFL; + val[0] += c * 19; } - long[] hplus19 = Arrays.copyOf(h, 10); - hplus19[0] += 19; + long mask = 1; + for (int i = 9; i >= 0; i--) { + long target = (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); + if (i == 0) target -= 19; + if (val[i] < target) { + mask = 0; + break; + } + if (val[i] > target) break; + } + val[0] += mask * 19; for (int i = 0; i < 9; i++) { - long carry = hplus19[i] >> (i % 2 == 0 ? 26 : 25); - hplus19[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); - hplus19[i + 1] += carry; + long c = val[i] >> (i % 2 == 0 ? 26 : 25); + val[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); + val[i + 1] += c; } - long carry = hplus19[9] >> 25; - hplus19[9] &= 0x1FFFFFFL; - long mask = -(carry & 1); - for (int i = 0; i < 10; i++) { - h[i] ^= mask & (h[i] ^ hplus19[i]); + val[9] -= mask * (1L << 25); + for (int i = 0; i < 9; i++) { + long c = val[i] >> (i % 2 == 0 ? 26 : 25); + val[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); + val[i + 1] += c; } + val[0] += (val[9] >> 25) * 19; + val[9] &= 0x1FFFFFFL; byte[] out = new byte[32]; int bitIdx = 0; - for (int limbIdx = 0; limbIdx < 10; limbIdx++) { - int limbBitSize = (limbIdx % 2 == 0 ? 26 : 25); - for (int i = 0; i < limbBitSize; i++) { - int bit = (int) ((h[limbIdx] >> i) & 1); - out[bitIdx / 8] |= (byte) (bit << (bitIdx % 8)); - bitIdx++; + for (int i = 0; i < 10; i++) { + int len = (i % 2 == 0 ? 26 : 25); + for (int bit = 0; bit < len; bit++) { + if (bitIdx < 255) { + if (((val[i] >> bit) & 1) == 1) out[bitIdx / 8] |= (byte) (1 << (bitIdx % 8)); + bitIdx++; + } } } return out; } - private static void fe_carry(long[] h) { - for (int i = 0; i < 9; i++) { - long carry = h[i] >> (i % 2 == 0 ? 26 : 25); - h[i] &= (i % 2 == 0 ? 0x3FFFFFFL : 0x1FFFFFFL); - h[i + 1] += carry; - } - long carry = h[9] >> 25; - h[9] &= 0x1FFFFFFL; - h[0] += carry * 19; - } - private static void fe_invert(long[] out, long[] z, Scratch s) { - long[] z2 = new long[10], z9 = new long[10], z11 = new long[10], t0 = new long[10], t1 = new long[10], t2 = new long[10]; + long[] t0 = s.stack[0], t1 = s.stack[1], z2 = s.stack[2], z9 = s.stack[3], z11 = s.stack[4]; + long[] z2_5_0 = s.stack[5], z2_10_0 = s.stack[6], z2_20_0 = s.stack[7], z2_50_0 = s.stack[8], z2_100_0 = s.stack[9]; fe_sq(z2, z, s.t19); fe_sq(t1, z2, s.t19); fe_sq(t0, t1, s.t19); fe_mul(z9, t0, z, s.t19); fe_mul(z11, z9, z2, s.t19); fe_sq(t0, z11, s.t19); - fe_mul(t2, t0, z9, s.t19); - fe_sq(t0, t2, s.t19); - for (int i = 1; i < 5; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(t1, t0, t2, s.t19); + fe_mul(z2_5_0, t0, z9, s.t19); + fe_sq(t0, z2_5_0, s.t19); + for (int i = 1; i < 5; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_10_0, t0, z2_5_0, s.t19); + fe_sq(t0, z2_10_0, s.t19); + for (int i = 1; i < 10; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_20_0, t0, z2_10_0, s.t19); + fe_sq(t0, z2_20_0, s.t19); + for (int i = 1; i < 20; i++) fe_sq(t0, t0, s.t19); + fe_mul(t0, t0, z2_20_0, s.t19); + fe_sq(t0, t0, s.t19); + for (int i = 1; i < 10; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_50_0, t0, z2_10_0, s.t19); + fe_sq(t0, z2_50_0, s.t19); + for (int i = 1; i < 50; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_100_0, t0, z2_50_0, s.t19); + fe_sq(t1, z2_100_0, s.t19); + for (int i = 1; i < 100; i++) fe_sq(t1, t1, s.t19); + fe_mul(t1, t1, z2_100_0, s.t19); fe_sq(t0, t1, s.t19); - for (int i = 1; i < 10; i++) { - fe_sq(t2, t0, s.t19); - fe_copy(t0, t2); - } - fe_mul(t2, t0, t1, s.t19); - fe_sq(t0, t2, s.t19); - for (int i = 1; i < 20; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(t0, t0, t2, s.t19); + for (int i = 1; i < 50; i++) fe_sq(t0, t0, s.t19); + fe_mul(t0, t0, z2_50_0, s.t19); fe_sq(t1, t0, s.t19); - for (int i = 1; i < 10; i++) { - fe_sq(t0, t1, s.t19); - fe_copy(t1, t0); - } - fe_mul(t1, t1, t2, s.t19); - fe_sq(t0, t1, s.t19); - for (int i = 1; i < 50; i++) { - fe_sq(t2, t0, s.t19); - fe_copy(t0, t2); - } - fe_mul(t2, t0, t1, s.t19); - fe_sq(t0, t2, s.t19); - for (int i = 1; i < 100; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(t0, t0, t2, s.t19); - fe_sq(t1, t0, s.t19); - for (int i = 1; i < 50; i++) { - fe_sq(t0, t1, s.t19); - fe_copy(t1, t0); - } - fe_mul(t1, t1, t2, s.t19); - fe_sq(t0, t1, s.t19); - for (int i = 1; i < 5; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(out, t0, z, s.t19); + for (int i = 1; i < 5; i++) fe_sq(t1, t1, s.t19); + fe_mul(out, t1, z11, s.t19); } - private static void fe_pow22523(long[] out, long[] z, Scratch s) { - long[] t0 = new long[10], t1 = new long[10], t2 = new long[10]; - fe_sq(t0, z, s.t19); - fe_sq(t1, t0, s.t19); - fe_sq(t1, t1, s.t19); - fe_mul(t1, t1, z, s.t19); - fe_mul(t0, t0, t1, s.t19); + private static void fe_pow22523(long[] out, long[] in, Scratch s) { + long[] t0 = s.stack[0], t1 = s.stack[1], z2 = s.stack[2], z9 = s.stack[3], z11 = s.stack[4]; + long[] z2_5_0 = s.stack[5], z2_10_0 = s.stack[6], z2_20_0 = s.stack[7], z2_50_0 = s.stack[8], z2_100_0 = s.stack[9]; + fe_sq(z2, in, s.t19); + fe_sq(t1, z2, s.t19); + fe_sq(t0, t1, s.t19); + fe_mul(z9, t0, in, s.t19); + fe_mul(z11, z9, z2, s.t19); + fe_sq(t0, z11, s.t19); + fe_mul(z2_5_0, t0, z9, s.t19); + fe_sq(t0, z2_5_0, s.t19); + for (int i = 1; i < 5; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_10_0, t0, z2_5_0, s.t19); + fe_sq(t0, z2_10_0, s.t19); + for (int i = 1; i < 10; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_20_0, t0, z2_10_0, s.t19); + fe_sq(t0, z2_20_0, s.t19); + for (int i = 1; i < 20; i++) fe_sq(t0, t0, s.t19); + fe_mul(t0, t0, z2_20_0, s.t19); fe_sq(t0, t0, s.t19); - fe_mul(t0, t0, t1, s.t19); - fe_sq(t1, t0, s.t19); - for (int i = 1; i < 5; i++) { - fe_sq(t2, t1, s.t19); - fe_copy(t1, t2); - } - fe_mul(t1, t1, t0, s.t19); - fe_sq(t2, t1, s.t19); - for (int i = 1; i < 10; i++) { - fe_sq(t0, t2, s.t19); - fe_copy(t2, t0); - } - fe_mul(t2, t2, t1, s.t19); - fe_sq(t0, t2, s.t19); - for (int i = 1; i < 20; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(t0, t0, t2, s.t19); - fe_sq(t1, t0, s.t19); - for (int i = 1; i < 10; i++) { - fe_sq(t0, t1, s.t19); - fe_copy(t1, t0); - } - fe_mul(t1, t1, t2, s.t19); + for (int i = 1; i < 10; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_50_0, t0, z2_10_0, s.t19); + fe_sq(t0, z2_50_0, s.t19); + for (int i = 1; i < 50; i++) fe_sq(t0, t0, s.t19); + fe_mul(z2_100_0, t0, z2_50_0, s.t19); + fe_sq(t1, z2_100_0, s.t19); + for (int i = 1; i < 100; i++) fe_sq(t1, t1, s.t19); + fe_mul(t1, t1, z2_100_0, s.t19); fe_sq(t0, t1, s.t19); - for (int i = 1; i < 50; i++) { - fe_sq(t2, t0, s.t19); - fe_copy(t0, t2); - } - fe_mul(t2, t0, t1, s.t19); - fe_sq(t0, t2, s.t19); - for (int i = 1; i < 100; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(t0, t0, t2, s.t19); - fe_sq(t1, t0, s.t19); - for (int i = 1; i < 50; i++) { - fe_sq(t0, t1, s.t19); - fe_copy(t1, t0); - } - fe_mul(t1, t1, t2, s.t19); - fe_sq(t0, t1, s.t19); - for (int i = 1; i < 2; i++) { - fe_sq(t1, t0, s.t19); - fe_copy(t0, t1); - } - fe_mul(out, t0, z, s.t19); + for (int i = 1; i < 50; i++) fe_sq(t0, t0, s.t19); + fe_mul(t0, t0, z2_50_0, s.t19); + fe_sq(t0, t0, s.t19); + fe_sq(t0, t0, s.t19); + fe_mul(out, t0, in, s.t19); } private static boolean fe_isnonzero(long[] h) { - byte[] b = fe_contract(h); - int res = 0; - for (byte x : b) res |= x; - return res != 0; + byte[] s = fe_contract(h); + for (byte b : s) if (b != 0) return true; + return false; } private static int fe_isnegative(long[] h) { - byte[] b = fe_contract(h); - return b[0] & 1; + byte[] s = fe_contract(h); + return s[0] & 1; } - private static void reduce(byte[] s) { - long s0 = 2097151 & load3(s, 0); - long s1 = 2097151 & (load4(s, 2) >> 5); - long s2 = 2097151 & (load3(s, 5) >> 2); - long s3 = 2097151 & (load4(s, 7) >> 7); - long s4 = 2097151 & (load4(s, 10) >> 4); - long s5 = 2097151 & (load3(s, 13) >> 1); - long s6 = 2097151 & (load4(s, 15) >> 6); - long s7 = 2097151 & (load3(s, 18) >> 3); - long s8 = 2097151 & load3(s, 21); - long s9 = 2097151 & (load4(s, 23) >> 5); + private static void reduceScalar(byte[] s) { + long s0 = 2097151 & load3(s, 0); + long s1 = 2097151 & (load4(s, 2) >> 5); + long s2 = 2097151 & (load3(s, 5) >> 2); + long s3 = 2097151 & (load4(s, 7) >> 7); + long s4 = 2097151 & (load4(s, 10) >> 4); + long s5 = 2097151 & (load3(s, 13) >> 1); + long s6 = 2097151 & (load4(s, 15) >> 6); + long s7 = 2097151 & (load3(s, 18) >> 3); + long s8 = 2097151 & load3(s, 21); + long s9 = 2097151 & (load4(s, 23) >> 5); long s10 = 2097151 & (load3(s, 26) >> 2); long s11 = 2097151 & (load4(s, 28) >> 7); long s12 = 2097151 & (load4(s, 31) >> 4); long s13 = 2097151 & (load3(s, 34) >> 1); long s14 = 2097151 & (load4(s, 36) >> 6); long s15 = 2097151 & (load3(s, 39) >> 3); - long s16 = 2097151 & load3(s, 42); + long s16 = 2097151 & load3(s, 42); long s17 = 2097151 & (load4(s, 44) >> 5); long s18 = 2097151 & (load3(s, 47) >> 2); long s19 = 2097151 & (load4(s, 49) >> 7); @@ -639,28 +577,28 @@ public final class Ed25519 { s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; - s9 += s21 * 666643; + s9 += s21 * 666643; s10 += s21 * 470296; s11 += s21 * 654183; s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; - s8 += s20 * 666643; - s9 += s20 * 470296; + s8 += s20 * 666643; + s9 += s20 * 470296; s10 += s20 * 654183; s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; - s7 += s19 * 666643; - s8 += s19 * 470296; - s9 += s19 * 654183; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; - s6 += s18 * 666643; - s7 += s18 * 470296; - s8 += s18 * 654183; - s9 -= s18 * 997805; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; long c6 = (s6 + (1 << 20)) >> 21; @@ -834,19 +772,18 @@ public final class Ed25519 { long sccc5 = s5 >> 21; s6 += sccc5; s5 -= sccc5 << 21; - for (int i = 0; i < 32; i++) s[i] = 0; + Arrays.fill(s, (byte) 0); long[] resLimbs = {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11}; int bitIdx = 0; for (int limbIdx = 0; limbIdx < 12; limbIdx++) { for (int i = 0; i < 21; i++) { if (bitIdx < 256) { - int bit = (int) ((resLimbs[limbIdx] >> i) & 1); - s[bitIdx / 8] |= (byte) (bit << (bitIdx % 8)); + if (((resLimbs[limbIdx] >> i) & 1) == 1) + s[bitIdx / 8] |= (byte) (1 << (bitIdx % 8)); bitIdx++; } } } - Arrays.fill(s, 32, 64, (byte) 0); } private static long load3(byte[] in, int off) { @@ -860,13 +797,9 @@ public final class Ed25519 { private static void scalarMulAdd(byte[] S, byte[] k, byte[] s, byte[] r) { long[] res = new long[64]; for (int i = 0; i < 32; i++) { - for (int j = 0; j < 32; j++) { - res[i + j] += (k[i] & 0xFFL) * (s[j] & 0xFFL); - } - } - for (int i = 0; i < 32; i++) { - res[i] += (r[i] & 0xFFL); + for (int j = 0; j < 32; j++) res[i + j] += (k[i] & 0xFFL) * (s[j] & 0xFFL); } + for (int i = 0; i < 32; i++) res[i] += (r[i] & 0xFFL); byte[] buffer = new byte[64]; long carry = 0; for (int i = 0; i < 63; i++) { @@ -875,13 +808,10 @@ public final class Ed25519 { carry = val >>> 8; } buffer[63] = (byte) (res[63] + carry); - reduce(buffer); + reduceScalar(buffer); System.arraycopy(buffer, 0, S, 0, 32); } -// ========================================================================= -// PUBLIC TEST HOOKS FOR TESTINATOR / EXTERNAL VERIFICATION -// ========================================================================= public static void testFromBytes(long[] h, byte[] s) { fe_frombytes(h, s); } @@ -894,7 +824,7 @@ public final class Ed25519 { public static byte[] testReduce(byte[] in) { byte[] s = Arrays.copyOf(in, 64); - reduce(s); + reduceScalar(s); return Arrays.copyOf(s, 32); } diff --git a/gajumaru-core/test/.gitignore b/gajumaru-core/test/.gitignore index 20177b4..54e882c 100644 --- a/gajumaru-core/test/.gitignore +++ b/gajumaru-core/test/.gitignore @@ -13,3 +13,5 @@ doc/erlang.png rel/example_project .concrete/DEV_MODE .rebar +.idea +*.iml \ No newline at end of file diff --git a/gajumaru-core/test/DoubleCheck.java b/gajumaru-core/test/DoubleCheck.java deleted file mode 100644 index d8431a6..0000000 --- a/gajumaru-core/test/DoubleCheck.java +++ /dev/null @@ -1,12 +0,0 @@ -import swiss.qpq.gajumaru.core.crypto.*; -import swiss.qpq.gajumaru.core.tools.*; - -public class DoubleCheck { - public static void main(String[] args) { - // Base point limbs are already in Ed25519.java but private - // I'll just run one step of publicKey with a simple scalar. - byte[] seed = new byte[32]; // All zeros - byte[] pub = Ed25519.publicKey(seed); - System.out.println("Pub: " + CryptoUtils.binToHex(pub)); - } -} diff --git a/gajumaru-core/test/README.md b/gajumaru-core/test/README.md index 6bfd9e1..700334a 100644 --- a/gajumaru-core/test/README.md +++ b/gajumaru-core/test/README.md @@ -1,14 +1,18 @@ # GM Java <-> Erlang Tests The core Java libraries are all written as transform functions over data. -There is little point, therefore, in obsessing over "unit tests" and "regression testing" of Java code in Java when we have cannonical code in Erlang. +There is little point, therefore, in obsessing over "unit tests" and "regression testing" of +Java code in Java when we have cannonical code in Erlang. -The purpose of this utility is to instead test the Java libraries agains the Erlang libraries directly. + +The purpose of this utility is to instead test the Java libraries agains the Erlang libraries +directly. ## How to run -In the current directory simply run `zx runlocal` and the report map will be printed to the screen and random test data will be in the `temp/` directory. +In the current directory simply run `zx runlocal` and the report map will be printed to the screen +and random test data will be in the `temp/` directory. To run a specific test suite run `zx runlocal [module names]`. @@ -17,8 +21,33 @@ To list module names, run `zx runlocal list`. ## Where things are -The test cases are all generated in gmt.erl, generally based on randomized but valid data that are fed to reference Erlang implementations. +The test cases are all generated in gmt.erl, generally based on randomized but valid data that +are fed to reference Erlang implementations. Java code is built using plain old javac, and is +handled by the `gajumaru-core/bin/compile` and `gajumaru-core/bin/run` scripts. + The input to and output from each test is recorded to disk in the `temp/` dir. + The inputs are then read in by equivalent Java functions and the output is then compared. For a test case to pass the outputs must be identical. + + +## The Erlang execution context + +`zx` manages loading the execution context, so dependencies like gmserialization, ec_utils, +base58, etc. (all of which are targets for which is increasingly turning into a Java port) +are brought in dynamically and managed by `zx`. The easiest way to test a specific function +in Java against the Erlang implementation is to add a call to it in gmt.erl and a matching +call in `../src/Testinator.java`. + +The dependencies that are brought in can be listed with `zx list deps`. + +The package names follow the pattern `[realm]-[package_name]-[version]` + +The location of the sources varies on different systems. +- Linux: `$HOME/zomp/lib/[realm]/[package_name]/[version]/src` +- MacOS: `$HOME/.zx/zomp/lib/[realm]/[package_name]/[version]/src` +- Windows: `%%LOCALAPPDATA%%/zomp/lib/[realm]/[package_name]/[version]/src` + +So for example `otpr-ec_utils-1.0.0` has its sources at `~/zomp/lib/otpr/ec_utils/1.0.0/src/` +on a reasonable system. \ No newline at end of file diff --git a/gajumaru-core/test/src/gmt.erl b/gajumaru-core/test/src/gmt.erl index 86e4b1b..4bc05fe 100644 --- a/gajumaru-core/test/src/gmt.erl +++ b/gajumaru-core/test/src/gmt.erl @@ -376,17 +376,26 @@ fe_parity() -> Temp = temp_dir(), TestFile = filename:join(Temp, "fe.test"), ok = filelib:ensure_dir(TestFile), - Inputs = [<<(rand:bytes(31))/binary, 0>> || _ <- lists:seq(1, 100)], - ok = file:write_file(TestFile, unicode:characters_to_binary([[bin_to_hex(I), "\n"] || I <- Inputs])), + % 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)], + 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), - Expected = [bin_to_hex(I) || I <- Inputs], - length(Expected) =:= length(JResults) andalso - lists:all(fun({E, J}) -> E =:= J end, lists:zip(Expected, JResults)). + 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. reduce_parity() -> Temp = temp_dir(), @@ -438,7 +447,9 @@ femul_parity() -> Temp = temp_dir(), TestFile = filename:join(Temp, "femul.test"), ok = filelib:ensure_dir(TestFile), - Inputs = [{rand:bytes(32), rand:bytes(32)} || _ <- lists:seq(1, 10)], + % Mask to 255 bits + Gen = fun() -> <> = rand:bytes(32), << (B band ((1 bsl 255) - 1)):256/little >> end, + 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])),