Serialization and Ed25519 #2

Merged
zxq9 merged 11 commits from no-bigint into master 2026-08-20 21:19:17 +09:00
7 changed files with 375 additions and 390 deletions
Showing only changes of commit 4c8ed9bc1f - Show all commits
+26
View File
@@ -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 <info@qpq.swiss>. All rights reserved.
+1 -2
View File
@@ -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
@@ -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);
}
+2
View File
@@ -13,3 +13,5 @@ doc/erlang.png
rel/example_project
.concrete/DEV_MODE
.rebar
.idea
*.iml
-12
View File
@@ -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));
}
}
+33 -4
View File
@@ -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.
+17 -6
View File
@@ -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 <<B:256/little>> = 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 <<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.
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() -> <<B:256/little>> = 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])),