|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|