diff --git a/common/.DS_Store b/common/.DS_Store new file mode 100644 index 0000000..ba91a7e Binary files /dev/null and b/common/.DS_Store differ diff --git a/common/src/.DS_Store b/common/src/.DS_Store new file mode 100644 index 0000000..746c869 Binary files /dev/null and b/common/src/.DS_Store differ diff --git a/common/src/main/.DS_Store b/common/src/main/.DS_Store new file mode 100644 index 0000000..5576e61 Binary files /dev/null and b/common/src/main/.DS_Store differ diff --git a/common/src/main/java/.DS_Store b/common/src/main/java/.DS_Store new file mode 100644 index 0000000..a409ac9 Binary files /dev/null and b/common/src/main/java/.DS_Store differ diff --git a/common/src/main/java/org/.DS_Store b/common/src/main/java/org/.DS_Store new file mode 100644 index 0000000..98b4384 Binary files /dev/null and b/common/src/main/java/org/.DS_Store differ diff --git a/common/src/main/java/org/whispersystems/.DS_Store b/common/src/main/java/org/whispersystems/.DS_Store new file mode 100644 index 0000000..e125c50 Binary files /dev/null and b/common/src/main/java/org/whispersystems/.DS_Store differ diff --git a/common/src/main/java/org/whispersystems/curve25519/.DS_Store b/common/src/main/java/org/whispersystems/curve25519/.DS_Store new file mode 100644 index 0000000..6fd098e Binary files /dev/null and b/common/src/main/java/org/whispersystems/curve25519/.DS_Store differ diff --git a/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java b/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java index 18e7941..115d5aa 100644 --- a/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java +++ b/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java @@ -9,6 +9,7 @@ import org.whispersystems.curve25519.java.Sha512; import org.whispersystems.curve25519.java.curve_sigs; import org.whispersystems.curve25519.java.scalarmult; +import org.whispersystems.curve25519.java.ed25519.veddsa_sigs; abstract class BaseJavaCurve25519Provider implements Curve25519Provider { @@ -69,18 +70,31 @@ public byte[] calculateSignature(byte[] random, byte[] privateKey, byte[] messag return result; } - public boolean verifySignature(byte[] publicKey, byte[] message, byte[] signature) { + public boolean verifySignature(byte[] publicKey, byte[] message, byte[] signature) + { return curve_sigs.curve25519_verify(sha512provider, signature, publicKey, message, message.length) == 0; } public byte[] calculateVrfSignature(byte[] random, byte[] privateKey, byte[] message) { - throw new AssertionError("NYI"); + byte[] result = new byte[96]; + + if (veddsa_sigs.VRFsign(sha512provider, result, privateKey, message, message.length, random) != 0) { + throw new IllegalArgumentException("Message exceeds max length!"); + } + + return result; } public byte[] verifyVrfSignature(byte[] publicKey, byte[] message, byte[] signature) throws VrfSignatureVerificationFailedException { - throw new AssertionError("NYI"); + byte[] result = new byte[32]; + + if (veddsa_sigs.VRFverify(sha512provider, result, signature, publicKey, message, message.length) != 0) { + throw new VrfSignatureVerificationFailedException(); + } + + return result; } public byte[] getRandom(int length) { diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/constants.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/constants.java new file mode 100644 index 0000000..f6927ac --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/constants.java @@ -0,0 +1,19 @@ +package org.whispersystems.curve25519.java.ed25519; + +public class constants { + + static final int LABELSETMAXLEN = 512; + static final int LABELMAXLEN = 128; + static final int BUFLEN = 1024; + static final int BLOCKLEN = 128; /* SHA512 */ + static final int HASHLEN = 64; /* SHA512 */ + static final int POINTLEN = 32; + static final int SCALARLEN = 32; + static final int RANDLEN = 32; + static final int SIGNATURELEN = 64; + static final int VRFSIGNATURELEN = 96; + static final int VRFOUTPUTLEN = 32; + static final int MSTART = 2048; + static final int MSGMAXLEN = 1048576; + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/elligator.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/elligator.java new file mode 100644 index 0000000..1556b47 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/elligator.java @@ -0,0 +1,102 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.Sha512; +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.fe_0.fe_0; +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_add.fe_add; +import static org.whispersystems.curve25519.java.fe_cmov.fe_cmov; +import static org.whispersystems.curve25519.java.fe_frombytes.fe_frombytes; +import static org.whispersystems.curve25519.java.fe_invert.fe_invert; +import static org.whispersystems.curve25519.java.fe_mul.fe_mul; +import static org.whispersystems.curve25519.java.fe_neg.fe_neg; +import static org.whispersystems.curve25519.java.fe_pow22523.fe_pow22523; +import static org.whispersystems.curve25519.java.fe_sq.fe_sq; +import static org.whispersystems.curve25519.java.fe_sq2.fe_sq2; +import static org.whispersystems.curve25519.java.fe_tobytes.fe_tobytes; + +public class elligator { + + /** + * @param in + * @return 1 -> square + * 0 -> 0 + * -1 -> nonsquare + */ + static int legendre_is_nonsquare(int[] in) + { + int[] temp = new int[10]; + byte[] bytes = new byte[32]; + fe_pow22523(temp, in); /* temp = in^((q-5)/8) */ + fe_sq(temp, temp); /* in^((q-5)/4) */ + fe_sq(temp, temp); /* in^((q-5)/2) */ + fe_mul(temp, temp, in); /* in^((q-3)/2) */ + fe_mul(temp, temp, in); /* in^((q-1)/2) */ + + fe_tobytes(bytes, temp); + return 1 & bytes[31]; + } + + /** + * Elligator2 uniform random bit string + * @param u + * @param r + */ + + static void elligator(int[] u, int[] r) + { + int[] A = new int[10], one = new int[10], twor2 = new int[10], twor2plus1 = new int[10], twor2plus1inv = new int[10]; + int[] x = new int[10], e = new int[10], Atemp = new int[10], uneg = new int[10]; + int nonsquare; + + fe_1(one); + fe_0(A); + A[0] = 486662; + + fe_sq2(twor2, r); + fe_add(twor2plus1, twor2, one); + fe_invert(twor2plus1inv, twor2plus1); + fe_mul(x, twor2plus1inv, A); + fe_neg(x, x); + + fe_mont_rhs.fe_mont_rhs(e, x); + nonsquare = legendre_is_nonsquare(e); + + fe_0(Atemp); + fe_cmov(Atemp, A, nonsquare); + fe_add(u, x, Atemp); + fe_neg(uneg, u); + fe_cmov(u, uneg, nonsquare); + } + + /** + * hash byte string to EC25519 Point + * @param p + * @param in + * @param in_len + * @param sha512provider + */ + static int hash_to_point(ge_p3 p, byte[] in, long in_len, Sha512 sha512provider) + { + byte[] hash = new byte[64]; + int[] h = new int[10], u = new int[10]; + int sign_bit; + ge_p3 p3 = new ge_p3(); + + sha512provider.calculateDigest(hash, in, in_len); + + /* take the high bit as Edwards sign bit */ + sign_bit = (hash[31] & 0x80) >> 7; + hash[31] &= 0x7F; + fe_frombytes(h, hash); + elligator(u, h); + + if (ge_montx_to_p3.ge_montx_to_p3(p3, u, sign_bit) !=0) + return -1; + ge_scalarmult_cofactor.ge_scalarmult_cofactor(p, p3); + + return 0; + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isequal.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isequal.java new file mode 100644 index 0000000..556f93f --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isequal.java @@ -0,0 +1,22 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.fe_isnonzero.fe_isnonzero; +import static org.whispersystems.curve25519.java.fe_sub.fe_sub; + +public class fe_isequal { + + /** + * + * @param f + * @param g + * @return 1 if f==g + * 0 if f!= g + */ + + static int fe_isequal(int[] f, int[] g) + { + int[] h = new int[10]; + fe_sub(h, f, g); + return (1 ^ (1 & (fe_isnonzero(h) >> 8))); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isreduced.java new file mode 100644 index 0000000..d57fa99 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_isreduced.java @@ -0,0 +1,25 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.crypto_verify_32.crypto_verify_32; +import static org.whispersystems.curve25519.java.fe_frombytes.fe_frombytes; +import static org.whispersystems.curve25519.java.fe_tobytes.fe_tobytes; + +public class fe_isreduced { + + /** + * + * @param s + * @return true if fe_isrecuded + * false otherwise + */ + static boolean fe_isreduced(byte[] s){ + int[] f = new int[10]; + byte[] strict = new byte[32]; + + fe_frombytes(f, s); + fe_tobytes(strict, f); + if (crypto_verify_32(strict, s) != 0) + return false; + return true; + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_mont_rhs.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_mont_rhs.java new file mode 100644 index 0000000..257a493 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_mont_rhs.java @@ -0,0 +1,26 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.fe_0.fe_0; +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_add.fe_add; +import static org.whispersystems.curve25519.java.fe_mul.fe_mul; +import static org.whispersystems.curve25519.java.fe_sq.fe_sq; + +public class fe_mont_rhs { + + static void fe_mont_rhs(int[] v2, int[] u) { + int[] A = new int[10], one = new int[10]; + int[] u2 = new int[10], Au = new int[10], inner = new int[10]; + + fe_1(one); + fe_0(A); + A[0] = 486662; + + fe_sq(u2, u); + fe_mul(Au, A, u); + fe_add(inner, u2, Au); + fe_add(inner, inner, one); + fe_mul(v2, u, inner); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_montx_to_edy.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_montx_to_edy.java new file mode 100644 index 0000000..3afa313 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_montx_to_edy.java @@ -0,0 +1,28 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_add.fe_add; +import static org.whispersystems.curve25519.java.fe_invert.fe_invert; +import static org.whispersystems.curve25519.java.fe_mul.fe_mul; +import static org.whispersystems.curve25519.java.fe_sub.fe_sub; + +public class fe_montx_to_edy { + + /** + * y = (u - 1) / (u + 1) + * @param y + * @param u + */ + static void fe_montx_to_edy(int[] y, int[] u) + { + + int[] one = new int[10], um1 = new int[10], up1 = new int[10]; + + fe_1(one); + fe_sub(um1, u, one); + fe_add(up1, u, one); + fe_invert(up1, up1); + fe_mul(y, um1, up1); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_sqrt.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_sqrt.java new file mode 100644 index 0000000..887833e --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/fe_sqrt.java @@ -0,0 +1,66 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.fe_0.fe_0; +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_cmov.fe_cmov; +import static org.whispersystems.curve25519.java.fe_copy.fe_copy; +import static org.whispersystems.curve25519.java.fe_frombytes.fe_frombytes; +import static org.whispersystems.curve25519.java.fe_mul.fe_mul; +import static org.whispersystems.curve25519.java.fe_pow22523.fe_pow22523; +import static org.whispersystems.curve25519.java.fe_sq.fe_sq; + +public class fe_sqrt { + + // sqrt(-1) + static final byte[] i_bytes = { + (byte) 0xb0, (byte) 0xa0, (byte) 0x0e, (byte) 0x4a, (byte) 0x27, (byte) 0x1b, (byte) 0xee, (byte) 0xc4, + (byte) 0x78, (byte) 0xe4, (byte) 0x2f, (byte) 0xad, (byte) 0x06, (byte) 0x18, (byte) 0x43, (byte) 0x2f, + (byte) 0xa7, (byte) 0xd7, (byte) 0xfb, (byte) 0x3d, (byte) 0x99, (byte) 0x00, (byte) 0x4d, (byte) 0x2b, + (byte) 0x0b, (byte) 0xdf, (byte) 0xc1, (byte) 0x4f, (byte) 0x80, (byte) 0x24, (byte) 0x83, (byte) 0x2b + }; + + + /** + * calc sqrt(a) + * @param out + * @param a + * @pre a is square or zero + * @post out^2 = a + * @return + */ + static int fe_sqrt(int[] out, int[] a) + { + int[] exp = new int[10], b = new int[10], b2 = new int[10], bi = new int[10], i = new int[10]; + int[] legendre = new int[10], zero = new int[10], one = new int[10]; + + fe_frombytes(i, i_bytes); + fe_pow22523(exp, a); + + fe_sq(legendre, exp); + fe_sq(legendre, legendre); + fe_mul(legendre, legendre, a); + fe_mul(legendre, legendre, a); + + fe_0(zero); + fe_1(one); + if (fe_isequal.fe_isequal(legendre, zero) == 0 && fe_isequal.fe_isequal(legendre, one) == 0) + return -1; + + fe_mul(b, a, exp); + fe_sq(b2, b); + + fe_mul(bi, b, i); + fe_cmov(b, bi, 1 ^ fe_isequal.fe_isequal(b2, a)); + fe_copy(out, b); + + + fe_sq(b2, out); + if (fe_isequal.fe_isequal(a, b2) == 0) + return -1; + + return 0; + + } + + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_isneutral.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_isneutral.java new file mode 100644 index 0000000..bd1eea7 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_isneutral.java @@ -0,0 +1,22 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.fe_0.fe_0; + +public class ge_isneutral { + + /** + * Check if p neutral point + * @param p + * @return 1 if p neutral point + * 0 otherwise + */ + public static int ge_isneutral(ge_p3 p) + { + int[] zero = new int[10]; + fe_0(zero); + + return (fe_isequal.fe_isequal(p.X, zero) & fe_isequal.fe_isequal(p.Y, p.Z)); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_montx_to_p3.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_montx_to_p3.java new file mode 100644 index 0000000..5f36a11 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_montx_to_p3.java @@ -0,0 +1,87 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_add.fe_add; +import static org.whispersystems.curve25519.java.fe_cmov.fe_cmov; +import static org.whispersystems.curve25519.java.fe_copy.fe_copy; +import static org.whispersystems.curve25519.java.fe_frombytes.fe_frombytes; +import static org.whispersystems.curve25519.java.fe_invert.fe_invert; +import static org.whispersystems.curve25519.java.fe_isnegative.fe_isnegative; +import static org.whispersystems.curve25519.java.fe_mul.fe_mul; +import static org.whispersystems.curve25519.java.fe_neg.fe_neg; +import static org.whispersystems.curve25519.java.fe_sq.fe_sq; + +public class ge_montx_to_p3 { + + static final byte[] A_bytes = { + (byte) 0x06, (byte) 0x7e, (byte) 0x45, (byte) 0xff, (byte) 0xaa, (byte) 0x04, (byte) 0x6e, (byte) 0xcc, + (byte) 0x82, (byte) 0x1a, (byte) 0x7d, (byte) 0x4b, (byte) 0xd1, (byte) 0xd3, (byte) 0xa1, (byte) 0xc5, + (byte) 0x7e, (byte) 0x4f, (byte) 0xfc, (byte) 0x03, (byte) 0xdc, (byte) 0x08, (byte) 0x7b, (byte) 0xd2, + (byte) 0xbb, (byte) 0x06, (byte) 0xa0, (byte) 0x60, (byte) 0xf4, (byte) 0xed, (byte) 0x26, (byte) 0x0f + }; + + /** + * + * @param p + * @param u + * @param ed_sign_bit + * @post check that p->X and p->Y satisfy the Ed curve equation + */ + + static int ge_montx_to_p3(ge_p3 p, int[] u, int ed_sign_bit) + { + int[] x = new int[10], y = new int[10], A = new int[10], v = new int[10], + v2 = new int[10], iv = new int[10], nx = new int[10]; + + fe_frombytes(A, A_bytes); + + fe_montx_to_edy.fe_montx_to_edy(y, u); /* y = (u - 1) / (u + 1) */ + + fe_mont_rhs.fe_mont_rhs(v2, u); /* v^2 = u(u^2 + Au + 1) */ + fe_sqrt.fe_sqrt(v, v2); /* v = sqrt(v^2) */ + + fe_mul(x, u, A); /* x = u * sqrt(-(A+2)) */ + fe_invert(iv, v); /* 1/v */ + fe_mul(x, x, iv); /* x = (u/v) * sqrt(-(A+2)) */ + + fe_neg(nx, x); /* negate x to match sign bit */ + fe_cmov(x, nx, fe_isnegative(x) ^ ed_sign_bit); + + fe_copy(p.X, x); + fe_copy(p.Y, y); + fe_1(p.Z); + fe_mul(p.T, p.X, p.Y); + + + int[] one = new int[10], d = new int[10], x2 = new int[10], y2 = new int[10], + x2y2 = new int[10], dx2y2 = new int[10]; + + byte[] dbytes = { + (byte) 0xa3, (byte) 0x78, (byte) 0x59, (byte) 0x13, (byte) 0xca, (byte) 0x4d, (byte) 0xeb, (byte) 0x75, + (byte) 0xab, (byte) 0xd8, (byte) 0x41, (byte) 0x41, (byte) 0x4d, (byte) 0x0a, (byte) 0x70, (byte) 0x00, + (byte) 0x98, (byte) 0xe8, (byte) 0x79, (byte) 0x77, (byte) 0x79, (byte) 0x40, (byte) 0xc7, (byte) 0x8c, + (byte) 0x73, (byte) 0xfe, (byte) 0x6f, (byte) 0x2b, (byte) 0xee, (byte) 0x6c, (byte) 0x03, (byte) 0x52 + }; + + fe_frombytes(d, dbytes); + fe_1(one); + fe_sq(x2, p.X); /* x^2 */ + fe_sq(y2, p.Y); /* y^2 */ + + fe_mul(dx2y2, x2, y2); /* x^2y^2 */ + fe_mul(dx2y2, dx2y2, d); /* dx^2y^2 */ + fe_add(dx2y2, dx2y2, one); /* dx^2y^2 + 1 */ + + fe_neg(x2y2, x2); /* -x^2 */ + fe_add(x2y2, x2y2, y2); /* -x^2 + y^2 */ + + if (fe_isequal.fe_isequal(x2y2, dx2y2) == 0) + return -1; + + return 0; + + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_neg.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_neg.java new file mode 100644 index 0000000..bc4bdd8 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_neg.java @@ -0,0 +1,22 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.fe_copy.fe_copy; +import static org.whispersystems.curve25519.java.fe_neg.fe_neg; + +public class ge_neg { + + /** + * return r = -p + * @param r + * @param p + */ + public static void ge_neg(ge_p3 r, ge_p3 p) + { + fe_neg(r.X, p.X); + fe_copy(r.Y, p.Y); + fe_copy(r.Z, p.Z); + fe_neg(r.T, p.T); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_add.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_add.java new file mode 100644 index 0000000..ff75e1a --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_add.java @@ -0,0 +1,28 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.ge_cached; +import org.whispersystems.curve25519.java.ge_p1p1; +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.ge_add.ge_add; +import static org.whispersystems.curve25519.java.ge_p1p1_to_p3.ge_p1p1_to_p3; +import static org.whispersystems.curve25519.java.ge_p3_to_cached.ge_p3_to_cached; + +public class ge_p3_add { + + /** + * r = p+q + * @param r + * @param p + * @param q + */ + static void ge_p3_add(ge_p3 r, ge_p3 p, ge_p3 q) + { + ge_cached p_cached = new ge_cached(); + ge_p1p1 r_p1p1 = new ge_p1p1(); + + ge_p3_to_cached(p_cached, p); + ge_add(r_p1p1, q, p_cached); + ge_p1p1_to_p3(r, r_p1p1); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_tobytes.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_tobytes.java new file mode 100644 index 0000000..f4157cd --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_p3_tobytes.java @@ -0,0 +1,20 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.*; + +public class ge_p3_tobytes { + + public static int ge_p3_tobytes(byte[] s, ge_p3 p){ + int [] recip = new int[10], x = new int[10], y = new int[10], fe = new int[10]; + + fe_invert.fe_invert(recip, p.Z); + fe_mul.fe_mul(x, p.X, recip); + fe_mul.fe_mul(y, p.Y, recip); + fe_tobytes.fe_tobytes(s, y); + + s[31] ^= fe_isnegative.fe_isnegative(x) << 7; + + return 0; + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult.java new file mode 100644 index 0000000..b2a924f --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult.java @@ -0,0 +1,224 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.*; + +import static org.whispersystems.curve25519.java.fe_0.fe_0; +import static org.whispersystems.curve25519.java.fe_1.fe_1; +import static org.whispersystems.curve25519.java.fe_cmov.fe_cmov; +import static org.whispersystems.curve25519.java.fe_copy.fe_copy; +import static org.whispersystems.curve25519.java.fe_neg.fe_neg; +import static org.whispersystems.curve25519.java.ge_add.ge_add; +import static org.whispersystems.curve25519.java.ge_p1p1_to_p2.ge_p1p1_to_p2; +import static org.whispersystems.curve25519.java.ge_p1p1_to_p3.ge_p1p1_to_p3; +import static org.whispersystems.curve25519.java.ge_p2_dbl.ge_p2_dbl; +import static org.whispersystems.curve25519.java.ge_p3_0.ge_p3_0; +import static org.whispersystems.curve25519.java.ge_p3_dbl.ge_p3_dbl; +import static org.whispersystems.curve25519.java.ge_p3_to_cached.ge_p3_to_cached; + +public class ge_scalarmult { + + /** + * check if 2 bits are equal + * @param b + * @param c + * @return 1 if yes + * 0 is no + */ + static int equal(int b, int c) + { + int ub = b; + int uc = c; + int x = ub ^ uc; + int y = x; + y -= 1; + y >>= 31; + return y; + } + + /** + * + * @param b + * @return -b + */ + static int negative(int b) + { + int x = b; + x >>= 63; + return x; + } + + /** + * + * @param t + * @param u + * @param b + */ + static void cmov(ge_cached t, ge_cached u, int b) + { + fe_cmov(t.YplusX,u.YplusX,b); + fe_cmov(t.YminusX,u.YminusX,b); + fe_cmov(t.Z,u.Z,b); + fe_cmov(t.T2d,u.T2d,b); + } + + static void cmov_p3(ge_p3 t, ge_p3 u, int b){ + fe_cmov(t.X, u.X, b); + fe_cmov(t.Y, u.Y, b); + fe_cmov(t.Z, u.Z, b); + fe_cmov(t.T, u.T, b); + } + + /** + * + * @param t + * @param pre + * @param b + */ + + static void select(ge_cached t, ge_cached[] pre, int b) + { + ge_cached minust = new ge_cached(); + int bnegative = negative(b); + int babs = b - (((-bnegative) & b) << 1); + + fe_1(t.YplusX); + fe_1(t.YminusX); + fe_1(t.Z); + fe_0(t.T2d); + + cmov(t,pre[0],equal(babs,1)); + cmov(t,pre[1],equal(babs,2)); + cmov(t,pre[2],equal(babs,3)); + cmov(t,pre[3],equal(babs,4)); + cmov(t,pre[4],equal(babs,5)); + cmov(t,pre[5],equal(babs,6)); + cmov(t,pre[6],equal(babs,7)); + cmov(t,pre[7],equal(babs,8)); + fe_copy(minust.YplusX,t.YminusX); + fe_copy(minust.YminusX,t.YplusX); + fe_copy(minust.Z,t.Z); + fe_neg(minust.T2d,t.T2d); + cmov(t,minust,bnegative); + } + + /** + * + * @param h + * @param a + * @param A + */ + + public static void ge_scalarmult_c(ge_p3 h, byte[] a, ge_p3 A) + { + byte[] e = new byte[64]; + int carry; + ge_p1p1 r = new ge_p1p1(); + ge_p2 s = new ge_p2(); + ge_p3 t0 = new ge_p3(), t1 = new ge_p3(), t2 = new ge_p3(); + ge_cached t = new ge_cached(); + ge_cached[] pre = new ge_cached[8]; + int i; + + for (i = 0; i < 8; i++){ + pre[i] = new ge_cached(); + } + + for (i = 0;i < 32;++i) { + e[2 * i + 0] = (byte) ((a[i] >> 0) & 15); + e[2 * i + 1] = (byte) ((a[i] >> 4) & 15); + } + + carry = 0; + for (i = 0;i < 63;++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + e[63] += carry; + + ge_p3_to_cached(pre[0], A); //A + + ge_p3_dbl(r, A); + ge_p1p1_to_p3(t0, r); + ge_p3_to_cached(pre[1], t0); // 2A + + ge_add(r, A, pre[1]); + ge_p1p1_to_p3(t1, r); + ge_p3_to_cached(pre[2], t1); // 3A + + ge_p3_dbl(r, t0); + ge_p1p1_to_p3(t0, r); + ge_p3_to_cached(pre[3], t0); // 4A + + ge_add(r, A, pre[3]); + ge_p1p1_to_p3(t2, r); + ge_p3_to_cached(pre[4], t2); // 5A + + ge_p3_dbl(r, t1); + ge_p1p1_to_p3(t1, r); + ge_p3_to_cached(pre[5], t1); // 6A + + ge_add(r, A, pre[5]); + ge_p1p1_to_p3(t1, r); + ge_p3_to_cached(pre[6], t1); // 7A + + ge_p3_dbl(r, t0); + ge_p1p1_to_p3(t0, r); + ge_p3_to_cached(pre[7], t0); // 8A + + ge_p3_0(h); + + for (i = 63;i > 0; i--) { + select(t,pre,e[i]); + ge_add(r, h, t); + ge_p1p1_to_p2(s,r); + + ge_p2_dbl(r,s); ge_p1p1_to_p2(s,r); + ge_p2_dbl(r,s); ge_p1p1_to_p2(s,r); + ge_p2_dbl(r,s); ge_p1p1_to_p2(s,r); + ge_p2_dbl(r,s); ge_p1p1_to_p3(h,r); + } + + select(t,pre,e[0]); + ge_add(r, h, t); + ge_p1p1_to_p3(h,r); + } + + public static void ge_scalarmult(ge_p3 h, byte[] a, ge_p3 A){ + ge_p3 p = new ge_p3(), q = new ge_p3(), t = new ge_p3(); + ge_cached c = new ge_cached(); + ge_p1p1 t0 = new ge_p1p1(); + + ge_p3_0.ge_p3_0(q); + + fe_copy(p.T, A.T); + fe_copy(p.X, A.X); + fe_copy(p.Y, A.Y); + fe_copy(p.Z, A.Z); + + int bit = 0; + + for (int i=0; i<256; i++){ + bit = ((a[i>>3]>>(i&7)) & 1); + + // ge add + ge_p3_to_cached(c, q); + ge_add(t0, p, c); + ge_p1p1_to_p3(t, t0); + + cmov_p3(q, t, bit); + + // ge_p3 double + ge_p2 p2 = new ge_p2(); + ge_p3_to_p2.ge_p3_to_p2(p2, p); + ge_p1p1 p1 = new ge_p1p1(); + ge_p3_dbl(p1, p); + ge_p1p1_to_p3(p, p1); + } + fe_copy(h.X, q.X); + fe_copy(h.Y, q.Y); + fe_copy(h.Z, q.Z); + fe_copy(h.T, q.T); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult_cofactor.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult_cofactor.java new file mode 100644 index 0000000..af590c6 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/ge_scalarmult_cofactor.java @@ -0,0 +1,33 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.ge_p1p1; +import org.whispersystems.curve25519.java.ge_p2; +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.ge_p1p1_to_p2.ge_p1p1_to_p2; +import static org.whispersystems.curve25519.java.ge_p1p1_to_p3.ge_p1p1_to_p3; +import static org.whispersystems.curve25519.java.ge_p2_dbl.ge_p2_dbl; +import static org.whispersystems.curve25519.java.ge_p3_dbl.ge_p3_dbl; + +public class ge_scalarmult_cofactor { + + /** + * q = 8*p + * @param q + * @param p + */ + static void ge_scalarmult_cofactor(ge_p3 q, ge_p3 p) + { + ge_p1p1 p1p1 = new ge_p1p1(); + ge_p2 p2 = new ge_p2(); + + ge_p3_dbl(p1p1, p); + ge_p1p1_to_p2(p2, p1p1); + + ge_p2_dbl(p1p1, p2); + ge_p1p1_to_p2(p2, p1p1); + + ge_p2_dbl(p1p1, p2); + ge_p1p1_to_p3(q, p1p1); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_eddsa.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_eddsa.java new file mode 100644 index 0000000..8eae6d1 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_eddsa.java @@ -0,0 +1,400 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.Sha512; +import org.whispersystems.curve25519.java.ge_p2; +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.ed25519.constants.*; +import static org.whispersystems.curve25519.java.ed25519.gen_labelset.*; +import static org.whispersystems.curve25519.java.ge_double_scalarmult.ge_double_scalarmult_vartime; +import static org.whispersystems.curve25519.java.ge_frombytes.ge_frombytes_negate_vartime; +import static org.whispersystems.curve25519.java.ge_p3_tobytes.ge_p3_tobytes; +import static org.whispersystems.curve25519.java.ge_scalarmult_base.ge_scalarmult_base; +import static org.whispersystems.curve25519.java.ge_tobytes.ge_tobytes; +import static org.whispersystems.curve25519.java.sc_muladd.sc_muladd; +import static org.whispersystems.curve25519.java.sc_reduce.sc_reduce; + +public class gen_eddsa { + + /* B: base point + * R: commitment (point), + r: private nonce (scalar) + K: encoded public key + k: private key (scalar) + Z: 32-bytes random + M: buffer containing message, message starts at M_start, continues for M_len + r = hash(B || labelset || Z || pad1 || k || pad2 || labelset || K || extra || M) (mod q) + */ + + /** + * VRF Commitment + * @param R_bytes + * @param r_scalar + * @param labelset + * @param labelset_len + * @param extra + * @param extra_len + * @param K_bytes + * @param k_scalar + * @param Z + * @param sha512provider + * @param M_buf + * @param M_start + * @param M_len + * @return 0 if success + * 1 otherwise + */ + static int generalized_commit(byte[] R_bytes, byte[] r_scalar, + byte[] labelset, long labelset_len, + byte[] extra, long extra_len, + byte[] K_bytes, byte[] k_scalar, + byte[] Z, Sha512 sha512provider, + byte[] M_buf, long M_start, long M_len) + { + ge_p3 R_point = new ge_p3(); + byte[] hash = new byte[(int) HASHLEN]; + long bufstart = 0; + long bufptr = 0; + long bufend = 0; + long prefix_len = 0; + + if (labelset_validate(labelset, labelset_len) != 0) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + if (R_bytes == null || r_scalar == null || + K_bytes == null || k_scalar == null || + Z == null || M_buf == null) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + if (extra == null && extra_len != 0) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + if (extra != null && extra_len == 0) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + if (extra != null && labelset_is_empty(labelset, labelset_len)) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + if (HASHLEN != 64) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + + prefix_len = 0; + prefix_len += POINTLEN + labelset_len + RANDLEN; + prefix_len += ((BLOCKLEN - (prefix_len % BLOCKLEN)) % BLOCKLEN); + prefix_len += SCALARLEN; + prefix_len += ((BLOCKLEN - (prefix_len % BLOCKLEN)) % BLOCKLEN); + prefix_len += labelset_len + POINTLEN + extra_len; + if (prefix_len > M_start) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + + bufstart = M_start - prefix_len; + bufptr = bufstart; + bufend = M_start; + bufptr = buffer_add(B_bytes, 0, POINTLEN, M_buf, bufptr, bufend); + bufptr = buffer_add(labelset, 0, labelset_len, M_buf, bufptr, bufend); + bufptr = buffer_add(Z, 0, RANDLEN, M_buf, bufptr, bufend); + bufptr = buffer_pad(M_buf, bufptr, bufend, bufstart); + bufptr = buffer_add(k_scalar, 0, SCALARLEN, M_buf, bufptr, bufend); + bufptr = buffer_pad(M_buf, bufptr, bufend, bufstart); + bufptr = buffer_add(labelset, 0, labelset_len, M_buf, bufptr, bufend); + bufptr = buffer_add(K_bytes, 0, POINTLEN, M_buf, bufptr, bufend); + bufptr = buffer_add(extra, 0, extra_len, M_buf, bufptr, bufend); + if (bufptr != bufend || bufptr != M_start || bufptr - bufstart != prefix_len || bufptr < 0) { + zeroize_commit(hash, M_buf, M_start, prefix_len); + return -1; + } + + byte[] M_buf_start_prefix = new byte[(int) (prefix_len + M_len)]; + System.arraycopy(M_buf, (int) (M_start - prefix_len), M_buf_start_prefix, 0, (int) (prefix_len + M_len)); + sha512provider.calculateDigest(hash, M_buf_start_prefix, prefix_len + M_len); + sc_reduce(hash); + ge_scalarmult_base(R_point, hash); + ge_p3_tobytes(R_bytes, R_point); + System.arraycopy(hash, 0, r_scalar, 0, (int) SCALARLEN); + + // ZEROIZE: + zeroize_commit(hash, M_buf, M_start, prefix_len); + return 0; + } + + /** + * Zeroize all arrays used in generalized_commit + * @param hash + * @param M_buf + * @param M_start + * @param prefix_len + */ + private static void zeroize_commit(byte[] hash, byte[] M_buf, long M_start, long prefix_len){ + byte[] zero = new byte[(int) HASHLEN]; + System.arraycopy(zero, 0, hash, 0, (int) HASHLEN); + zero = new byte[(int) prefix_len]; + System.arraycopy(zero, 0, M_buf, (int) (M_start-prefix_len), (int) (prefix_len)); + } + + /* if is_labelset_empty(labelset): + return hash(R || K || M) (mod q) + else: + return hash(B || labelset || R || labelset || K || extra || M) (mod q) + */ + + /** + * Challenge commitment + * @param h_scalar + * @param labelset + * @param labelset_len + * @param extra + * @param extra_len + * @param R_bytes + * @param K_bytes + * @param M_buf + * @param M_start + * @param M_len + * @param sha512provider + * @return + */ + static int generalized_challenge(byte[] h_scalar, + byte[] labelset, long labelset_len, + byte[] extra, long extra_len, + byte[] R_bytes, + byte[] K_bytes, + byte[] M_buf, long M_start, long M_len, + Sha512 sha512provider) + { + byte[] hash = new byte[(int) HASHLEN]; + long bufstart = 0; + long bufptr = 0; + long bufend = 0; + long prefix_len = 0; + + if (h_scalar == null) + return -1; + + if (h_scalar.length != SCALARLEN) + return -1; + + if (labelset_validate(labelset, labelset_len) != 0) + return -1; + if (R_bytes == null || K_bytes == null || M_buf == null) + return -1; + if (extra == null && extra_len != 0) + return -1; + if (extra != null && extra_len == 0) + return -1; + if (extra != null && labelset_is_empty(labelset, labelset_len)) + return -1; + if (HASHLEN != 64) + return -1; + + if (labelset_is_empty(labelset, labelset_len)) { + if (2*POINTLEN > M_start) + return -1; + if (extra != null || extra_len != 0) + return -1; + System.arraycopy(R_bytes, 0, M_buf, (int) (M_start - (2*POINTLEN)), (int) POINTLEN); + System.arraycopy(K_bytes, 0, M_buf, (int) (M_start - (1*POINTLEN)), (int) POINTLEN); + prefix_len = 2*POINTLEN; + } else { + prefix_len = 3*POINTLEN + 2*labelset_len + extra_len; + if (prefix_len > M_start) + return -1; + + bufstart = M_start - prefix_len; + bufptr = bufstart; + bufend = M_start; + bufptr = buffer_add(B_bytes, 0, POINTLEN, M_buf, bufptr, bufend); + bufptr = buffer_add(labelset, 0, labelset_len, M_buf, bufptr, bufend); + bufptr = buffer_add(R_bytes, 0, POINTLEN, M_buf, bufptr, bufend); + bufptr = buffer_add(labelset, 0, labelset_len, M_buf, bufptr, bufend); + bufptr = buffer_add(K_bytes, 0, POINTLEN, M_buf, bufptr, bufend); + bufptr = buffer_add(extra, 0, extra_len, M_buf, bufptr, bufend); + + if (bufptr < 0) + return -1; + if (bufptr != bufend || bufptr != M_start || bufptr - bufstart != prefix_len) + return -1; + } + + byte[] M_buf_start_prefix = new byte[(int) (prefix_len + M_len)]; + System.arraycopy(M_buf, (int) (M_start - prefix_len), M_buf_start_prefix, 0, (int) (prefix_len + M_len)); + sha512provider.calculateDigest(hash, M_buf_start_prefix, (int) (prefix_len + M_len)); + sc_reduce(hash); + System.arraycopy(hash, 0, h_scalar, 0, (int) SCALARLEN); + return 0; + } + + /* return r + kh (mod q) */ + + /** + * prove VRF hash output + * @param out_scalar + * @param r_scalar + * @param k_scalar + * @param h_scalar + * @return + */ + static int generalized_prove(byte[]out_scalar, byte[] r_scalar, byte[] k_scalar, byte[] h_scalar) + { + sc_muladd(out_scalar, h_scalar, k_scalar, r_scalar); + + // Zeroize Stack: + byte[] m = new byte[1024]; + + return 0; + } + + + static int generalized_solve_commitment(byte[] R_bytes_out, ge_p3 K_point_out, + ge_p3 B_point, byte[] s_scalar, + byte[] K_bytes, byte[] h_scalar) + { + + ge_p3 Kneg_point = new ge_p3(); + ge_p2 R_calc_point_p2 = new ge_p2(); + + ge_p3 sB = new ge_p3(); + ge_p3 hK = new ge_p3(); + ge_p3 R_calc_point_p3 = new ge_p3(); + + if (ge_frombytes_negate_vartime(Kneg_point, K_bytes) != 0){ + return -1; + } + + + if (B_point == null) { + ge_double_scalarmult_vartime(R_calc_point_p2, h_scalar, Kneg_point, s_scalar); + ge_tobytes(R_bytes_out, R_calc_point_p2); + } + else { + // s * Bv + ge_scalarmult.ge_scalarmult(sB, s_scalar, B_point); + + // h * -K + ge_scalarmult.ge_scalarmult(hK, h_scalar, Kneg_point); + + // R = sB - hK + ge_p3_add.ge_p3_add(R_calc_point_p3, sB, hK); + ge_p3_tobytes(R_bytes_out, R_calc_point_p3); + } + + if (K_point_out != null) { + ge_neg.ge_neg(K_point_out, Kneg_point); + } + + return 0; + } + + /** + * + * @param signature_out + * @param eddsa_25519_pubkey_bytes + * @param eddsa_25519_privkey_scalar + * @param msg + * @param msg_len + * @param random + * @param sha512provider + * @param customization_label + * @param customization_label_len + * @return 0 if success + * -1 otherwise + */ + static int generalized_eddsa_25519_sign( + byte[] signature_out, + byte[] eddsa_25519_pubkey_bytes, + byte[] eddsa_25519_privkey_scalar, + byte[] msg, long msg_len, + byte[] random, Sha512 sha512provider, + byte[] customization_label, long customization_label_len) + { + byte[] labelset = new byte[LABELSETMAXLEN]; + long labelset_len = 0; + byte[] R_bytes = new byte[POINTLEN]; + byte[] r_scalar = new byte[SCALARLEN]; + byte[] h_scalar = new byte[SCALARLEN]; + byte[] s_scalar = new byte[SCALARLEN]; + byte[] M_buf = new byte[(int) (msg_len + MSTART)]; + + if (signature_out == null){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (signature_out.length != SIGNATURELEN){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (eddsa_25519_pubkey_bytes == null){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (eddsa_25519_privkey_scalar == null){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (msg == null){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (customization_label == null && customization_label_len != 0){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (customization_label_len > LABELMAXLEN){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (msg_len > MSGMAXLEN){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + + System.arraycopy(msg, 0, M_buf, MSTART, (int) msg_len); + + if (labelset_new(labelset, labelset_len, LABELSETMAXLEN, null, 0, + customization_label, customization_label_len) != 0){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (generalized_commit(R_bytes, r_scalar, labelset, labelset_len, null, 0, + eddsa_25519_pubkey_bytes, eddsa_25519_privkey_scalar, + random, sha512provider, M_buf, MSTART, msg_len) != 0) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (generalized_challenge(h_scalar, labelset, labelset_len, null, 0, + R_bytes, eddsa_25519_pubkey_bytes, M_buf, MSTART, msg_len, sha512provider) != 0){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (generalized_prove(s_scalar, r_scalar, eddsa_25519_privkey_scalar, h_scalar) != 0){ + zeroize_sign(r_scalar, M_buf); + return -1; + } + + System.arraycopy(R_bytes, 0, signature_out, 0, POINTLEN); + System.arraycopy(s_scalar, 0, signature_out, POINTLEN, SCALARLEN); + + zeroize_sign(r_scalar, M_buf); + return 0; + + } + + private static void zeroize_sign(byte[] r_scalar, byte[] M_buf){ + r_scalar = new byte[SCALARLEN]; + + // Zeroize Stack: + byte[] m = new byte[1024]; + + M_buf = new byte[1]; + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_labelset.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_labelset.java new file mode 100644 index 0000000..a4195d1 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_labelset.java @@ -0,0 +1,256 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.ed25519.constants.*; + +public class gen_labelset { + + static final byte[] B_bytes = { + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }; + + // signature: + // buffer_add(byte[] in, long in_pos, long in_len, byte[] out, long out_pos, long out_len) + + /** + * Add byte[] to buffer at pos + * @param in + * @param in_pos + * @param in_len + * @param out + * @param out_pos + * @param out_len + * @pre out != null + * @pre out_pos < out_len + * @pre out_pos + in_len < out_len + * @pre in != null + * @pre in_pos < in_len + * @return buffer_len if success + * -1 otherwise + */ + static long buffer_add(byte[] in, long in_pos, long in_len, + byte[] out, long out_pos, long out_len) + { + long count = 0; + + if (in_pos < 0) + return -1; + if (out == null || out_pos > out_len) + return -1; + if (in_pos > in_len) + return -1; + if (in == null && in_len != 0) + return -1; + if (out_len - out_pos < in_len) + return -1; + + for (count=0; count < in_len; count++) { + if (out_pos + count >= out_len) + return -1; + out[(int) (out_pos + count)] = in[(int) (in_pos+count)]; + } + + return out_pos+in_len; + } + + //signature: + // buffer_pad(byte[] buf, long pos, long end) + + /** + * Pad buffer with 0 at position + * @param buf + * @param pos + * @param len + * @pre buf != null && pos < len + * @pre pos + pad_len < len + * @return new buffer_len if success + * 0 otherwise + */ + static long buffer_pad(byte[] buf, long pos, long len, long correction) + { + long count = 0; + long pad_len = 0; + + if (pos < 0) + return -1; + if (buf == null || pos >= len) + return -1; + + pad_len = (BLOCKLEN - ((pos-correction) % BLOCKLEN)) % BLOCKLEN; + if (len - pos < pad_len) + return -1; + + for (count=0; count < pad_len; count++) { + if (pos+count >= len) + return -1; + buf[(int) (pos+count)] = 0; + } + return pos + pad_len; + } + + + //signature: + // labelset_new(byte[] labelset, long labelset_len, long labelset_maxlen, + // byte[] protocol_name, long protocol_name_len, + // byte[] customization_label, long customization_label_len) + + /** + * Create new labelset from empty byte[LABELSETMAXLEN] + * @param labelset + * @param labelset_len + * @param labelset_maxlen + * @param protocol_name + * @param protocol_name_len + * @param customization_label + * @param customization_label_len + * @pre labelset != null && labelset_maxlen <= LABELSETMAXLEN + * @pre labelset_maxlen >= 3 + protocol_name_len + customization_label_len + * @pre (protocol_name == null && protocol_name_len <= 0) || protocol_name != null + * @pre protocol_name_len <= LABELMAXLEN + * @pre (customization_label == null && customization_label_len <= 0) || protocol_name != null + * @pre customization_label_len <= LABELMAXLEN + * @post labelset_len == 3 + protocol_name_len + customization_label_len + * @return labelset_len + */ + static long labelset_new(byte[] labelset, long labelset_len, long labelset_maxlen, + byte[] protocol_name, long protocol_name_len, + byte[] customization_label, long customization_label_len) + { + labelset_len = 0; + + if (labelset == null) + return -1; + if (labelset_maxlen > LABELSETMAXLEN) + return -1; + if (labelset_maxlen < 3 + protocol_name_len + customization_label_len) + return -1; + if (protocol_name == null && protocol_name_len > 0) + return -1; + if (customization_label == null && customization_label_len > 0) + return -1; + if (protocol_name_len > LABELMAXLEN) + return -1; + if (customization_label_len > LABELMAXLEN) + return -1; + + long labelset_ptr = 0; + labelset[(int) labelset_ptr++] = 2; + labelset[(int) labelset_ptr++] = (byte) protocol_name_len; + labelset_ptr = buffer_add(protocol_name, 0, protocol_name_len, labelset, labelset_ptr, labelset_maxlen); + if (labelset_ptr < 0) + return -1; + if (labelset != null && labelset_ptr < labelset_maxlen) + labelset[(int) labelset_ptr++] = (byte) customization_label_len; + + labelset_ptr = buffer_add(customization_label, 0, customization_label_len, labelset, labelset_ptr, labelset_maxlen); + if (labelset_ptr < 0) + return -1; + if (labelset != null && labelset_ptr == 3 + protocol_name_len + customization_label_len) { + + return labelset_ptr; + + } + return -1; + } + + + //signature: + //labelset_add(byte[] labelset, long labelset_len, long labelset_maxlen, + // byte[] label, long label_len) + + /** + * Appends new label to labelset + * @param labelset + * @param labelset_len + * @param labelset_maxlen + * @param label + * @param label_len + * @return labelset_len + */ + static long labelset_add(byte[] labelset, long labelset_len, long labelset_maxlen, + byte[] label, long label_len) + { + if (labelset_len < 0) + return -1; + if (labelset_len > LABELSETMAXLEN || labelset_maxlen > LABELSETMAXLEN) + return -1; + if (labelset_len >= labelset_maxlen || labelset_len + label_len + 1 > labelset_maxlen) + return -1; + if (labelset_len < 3 || labelset_maxlen < 4) + return -1; + if (label_len > LABELMAXLEN) + return -1; + + long labelset_ptr = labelset_len; + labelset[0]++; + labelset[(int) labelset_ptr++] = (byte) label_len; + labelset_ptr = buffer_add(label, 0, label_len, labelset, labelset_ptr, labelset_maxlen); + if (labelset_ptr < 0) + return -1; + if (labelset == null) + return -1; + if (labelset_ptr >= labelset_maxlen) + return -1; + if (labelset_ptr != labelset_len + 1 + label_len) + return -1; + + return labelset_ptr; + } + + // signature: + // labelset_validate(byte[] labelset, long labelset_len) + + /** + * + * @param labelset + * @param labelset_len + * @return -1 => INVALID + * 0 => VALID + */ + static int labelset_validate(byte[] labelset, long labelset_len) + { + int num_labels = 0; + int count = 0; + int offset = 0; + int label_len = 0; + + if (labelset == null) + return -1; + if (labelset_len < 3 || labelset_len > LABELSETMAXLEN) + return -1; + + num_labels = labelset[0]; + offset = 1; + for (count = 0; count < num_labels; count++) { + label_len = labelset[offset]; + if (label_len > LABELMAXLEN) + return -1; + offset += 1 + label_len; + if (offset > labelset_len) + return -1; + } + if (offset != labelset_len) + return -1; + return 0; + } + + + //signature: + //labelset_is_empty(byte[] labelset, long labelset_len) + + /** + * Check if labelset is empty + * @param labelset + * @param labelset_len + * @return bool is_empty + */ + static boolean labelset_is_empty(byte[] labelset, long labelset_len) + { + if (labelset_len != 3) + return false; + return true; + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_veddsa.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_veddsa.java new file mode 100644 index 0000000..a91a4a8 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_veddsa.java @@ -0,0 +1,435 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.*; + +import static org.whispersystems.curve25519.java.ed25519.constants.*; +import static org.whispersystems.curve25519.java.ed25519.gen_eddsa.*; +import static org.whispersystems.curve25519.java.ed25519.gen_labelset.*; + + +public class gen_veddsa { + + /** + * + * @param Bv_point + * @param labelset + * @param labelset_len + * @param K_bytes + * @param M_buf + * @param M_start + * @param M_len + * @param sha512provider + * @return 0 if success + * -1 otherwise + */ + public static int generalized_calculate_Bv(ge_p3 Bv_point, + byte[] labelset, long labelset_len, byte[] K_bytes, + byte[] M_buf, long M_start, long M_len, + Sha512 sha512provider) + { + long prefix_len = 0; + + if (labelset_validate(labelset, labelset_len) != 0) + return -1; + if (Bv_point == null || K_bytes == null || M_buf == null) + return -1; + + prefix_len = 2*POINTLEN + labelset_len; + if (prefix_len > M_start) + return -1; + + long M_buf_ptr = M_start - prefix_len; + // buffer_add signature: + // buffer_add(byte[] src, srcpos, src_len, byte[] dest, dest_pos, dest_len) + M_buf_ptr = buffer_add(B_bytes, 0, POINTLEN, M_buf, M_buf_ptr, M_start); + M_buf_ptr = buffer_add(labelset, 0, labelset_len, M_buf, M_buf_ptr, M_start); + M_buf_ptr = buffer_add(K_bytes, 0, POINTLEN, M_buf, M_buf_ptr, M_start); + if (M_buf_ptr < 0 || M_buf_ptr != M_start) + return -1; + + //elligator.hash_to_point signature: + // hash_to_point(ge_p3 out, byte[] in, in_pos, in_len) + byte[] M_buf_start_prefix_len = new byte[(int) (prefix_len + M_len)]; + System.arraycopy(M_buf, (int)(M_start - prefix_len), M_buf_start_prefix_len, 0, (int)(M_len + prefix_len)); + + if (elligator.hash_to_point(Bv_point, M_buf_start_prefix_len, prefix_len + M_len, sha512provider) != 0) + return -1; + if (ge_isneutral.ge_isneutral(Bv_point) == 1) + return -1; + + return 0; + } + + /** + * + * @param vrf_output + * @param labelset + * @param labelset_len + * @param cKv_point + * @param sha512provider + * @pre vrf_output.len == VRFOUTPUTLEN + * @return 0 if success + * -1 otherwise + */ + static int generalized_calculate_vrf_output(byte[] vrf_output, + byte[] labelset, long labelset_len, + ge_p3 cKv_point, + Sha512 sha512provider) + { + if (vrf_output.length != VRFOUTPUTLEN) + return -1; + + byte[] buf = new byte[(int) BUFLEN]; + long buflen = BUFLEN; + byte[] cKv_bytes = new byte[(int) POINTLEN]; + byte[] hash = new byte[(int) HASHLEN]; + + if (vrf_output == null) + return -1; + + if (labelset_len + 2*POINTLEN > BUFLEN) + return -1; + if (labelset_validate(labelset, labelset_len) != 0) + return -1; + if (cKv_point == null) + return -1; + if (VRFOUTPUTLEN > HASHLEN) + return -1; + + ge_p3_tobytes.ge_p3_tobytes(cKv_bytes, cKv_point); + long bufptr = 0; + bufptr = buffer_add(B_bytes, 0, POINTLEN, buf, bufptr, buflen); + bufptr = buffer_add(labelset, 0, labelset_len, buf, bufptr, buflen); + bufptr = buffer_add(cKv_bytes, 0, POINTLEN, buf, bufptr, buflen); + if (bufptr < 0) + return -1; + if (bufptr > BUFLEN) + return -1; + sha512provider.calculateDigest(hash, buf, bufptr); + System.arraycopy(hash, 0, vrf_output, 0, (int) VRFOUTPUTLEN); + + return 0; + } + + + + static int generalized_veddsa_25519_sign(byte[] signature_out, + byte[] eddsa_25519_pubkey_bytes, byte[] eddsa_25519_privkey_scalar, + byte[] msg, long msg_len, + byte[] random, Sha512 sha512provider, + byte[] customization_label, long customization_label_len) + { + byte[] labelset = new byte[(int) LABELSETMAXLEN]; + long labelset_len = 0; + ge_p3 Bv_point = new ge_p3(); + ge_p3 Kv_point = new ge_p3(); + ge_p3 Rv_point = new ge_p3(); + byte[] Bv_bytes = new byte[(int) POINTLEN]; + byte[] Kv_bytes = new byte[(int) POINTLEN]; + byte[] Rv_bytes = new byte[(int) POINTLEN]; + byte[] R_bytes = new byte[(int) POINTLEN]; + byte[] r_scalar = new byte[(int) SCALARLEN]; + byte[] h_scalar = new byte[(int) SCALARLEN]; + byte[] s_scalar = new byte[(int) SCALARLEN]; + byte[] extra = new byte[(int) (3*POINTLEN)]; + byte[] M_buf = new byte[(int) (msg_len + MSTART)]; + byte[] protocol_name = "VEdDSA_25519_SHA512_Elligator2".getBytes(); + + if (signature_out == null || signature_out.length != VRFSIGNATURELEN) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (eddsa_25519_pubkey_bytes == null) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (eddsa_25519_privkey_scalar == null) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (msg == null) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (customization_label == null && customization_label_len != 0) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (customization_label_len > LABELMAXLEN) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + if (msg_len > MSGMAXLEN) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + System.arraycopy(msg, 0, M_buf, (int) MSTART, (int) msg_len); + + labelset_len = labelset_new(labelset, labelset_len, LABELMAXLEN, + protocol_name, protocol_name.length, + customization_label, customization_label_len); + if (labelset_len < 0) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + labelset_len = labelset_add(labelset, labelset_len, LABELSETMAXLEN, "1".getBytes(), 1); + if (labelset_len < 0) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (generalized_calculate_Bv(Bv_point, labelset, labelset_len, + eddsa_25519_pubkey_bytes, M_buf, MSTART, msg_len, sha512provider) != 0) + { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + ge_scalarmult.ge_scalarmult(Kv_point, eddsa_25519_privkey_scalar, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Bv_bytes, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Kv_bytes, Kv_point); + + labelset[(int) (labelset_len-1)] = "2".getBytes()[0]; + System.arraycopy(Bv_bytes, 0, extra, 0, (int) POINTLEN); + System.arraycopy(Kv_bytes, 0, extra, (int) POINTLEN, (int) POINTLEN); + + if (generalized_commit(R_bytes, r_scalar, + labelset, labelset_len, + extra, 2*POINTLEN, + eddsa_25519_pubkey_bytes, eddsa_25519_privkey_scalar, + random, sha512provider, + M_buf, MSTART, msg_len) != 0) + { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + ge_scalarmult.ge_scalarmult(Rv_point, r_scalar, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Rv_bytes, Rv_point); + + labelset[(int) (labelset_len-1)] = 3; + System.arraycopy(Rv_bytes, 0, extra, (int)(2*POINTLEN), (int) (POINTLEN)); + if (generalized_challenge(h_scalar, + labelset, labelset_len, + extra, 3*POINTLEN, + R_bytes, eddsa_25519_pubkey_bytes, + M_buf, MSTART, msg_len, + sha512provider) != 0) + { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + if (generalized_prove(s_scalar, r_scalar, eddsa_25519_privkey_scalar, h_scalar) != 0) { + zeroize_sign(r_scalar, M_buf); + return -1; + } + + // return (Kv || h || s) + System.arraycopy(Kv_bytes, 0, signature_out, 0, (int) POINTLEN); + System.arraycopy(h_scalar, 0, signature_out, (int) POINTLEN, (int) SCALARLEN); + System.arraycopy(s_scalar, 0, signature_out, POINTLEN+SCALARLEN, SCALARLEN); + + zeroize_sign(r_scalar, M_buf); + return 0; + + } + + /** + * Zeroize all arrays used in generalized_veddsa_25519_sign + * @param r_scalar + * @param M_buf + */ + private static void zeroize_sign(byte[] r_scalar, byte[] M_buf){ + byte[] zero = new byte[SCALARLEN]; //ZEROIZE r_scalar + System.arraycopy(zero, 0, r_scalar, 0, SCALARLEN); + byte[] m = new byte[1024]; //ZEROIZE STACK; + M_buf = new byte[1]; //Free M_buf + } + + + /** + * + * @param vrf_out + * @param signature + * @param eddsa_25519_pubkey_bytes + * @param msg + * @param msg_len + * @param customization_label + * @param customization_label_len + * @param sha512provider + * @return -1 if not successful + * 0 if success + */ + static int generalized_veddsa_25519_verify(byte[] vrf_out, + byte[] signature, + byte[] eddsa_25519_pubkey_bytes, + byte[] msg, long msg_len, + byte[] customization_label, long customization_label_len, + Sha512 sha512provider) + { + byte[] labelset = new byte[LABELSETMAXLEN]; + long labelset_len = 0; + byte[] Kv_bytes = new byte[POINTLEN]; + byte[] h_scalar = new byte[SCALARLEN]; + byte[] s_scalar = new byte[SCALARLEN]; + ge_p3 Bv_point = new ge_p3(), K_point = new ge_p3(), Kv_point = new ge_p3(), cK_point = new ge_p3(), cKv_point = new ge_p3(); + byte[] Bv_bytes = new byte[POINTLEN]; + byte[] R_calc_bytes = new byte[POINTLEN]; + byte[] Rv_calc_bytes = new byte[POINTLEN]; + byte[] h_calc_scalar = new byte[SCALARLEN]; + byte[] extra = new byte[3*POINTLEN]; + byte[] M_buf = new byte[(int) (msg_len + MSTART)]; + byte[] protocol_name = "VEdDSA_25519_SHA512_Elligator2".getBytes(); + + if (vrf_out == null){ + zeroize_verify(M_buf); + return -1; + } + if (vrf_out.length != VRFOUTPUTLEN){ + zeroize_verify(M_buf); + return -1; + } + if (signature == null){ + zeroize_verify(M_buf); + return -1; + } + if (eddsa_25519_pubkey_bytes == null){ + zeroize_verify(M_buf); + return -1; + } + if (msg == null){ + zeroize_verify(M_buf); + return -1; + } + if (customization_label == null && customization_label_len != 0){ + zeroize_verify(M_buf); + return -1; + } + if (customization_label_len > LABELMAXLEN){ + zeroize_verify(M_buf); + return -1; + } + if (msg_len > MSGMAXLEN){ + zeroize_verify(M_buf); + return -1; + } + + System.arraycopy(msg, 0, M_buf, MSTART, (int) msg_len); + + System.arraycopy(signature, 0, Kv_bytes, 0, POINTLEN); + System.arraycopy(signature, POINTLEN, h_scalar, 0, SCALARLEN); + System.arraycopy(signature, POINTLEN+SCALARLEN, s_scalar, 0, SCALARLEN); + + if (!point_isreduced.point_isreduced(eddsa_25519_pubkey_bytes)){ + zeroize_verify(M_buf); + return -1; + } + if (!point_isreduced.point_isreduced(Kv_bytes)){ + zeroize_verify(M_buf); + return -1; + } + if (!sc_isreduced.sc_isreduced(h_scalar)){ + zeroize_verify(M_buf); + return -1; + } + if (!sc_isreduced.sc_isreduced(s_scalar)){ + zeroize_verify(M_buf); + return -1; + } + + labelset_len = labelset_new(labelset, labelset_len, LABELSETMAXLEN, + protocol_name, protocol_name.length, + customization_label, customization_label_len); + if (labelset_len < 0) { + zeroize_verify(M_buf); + return -1; + } + + labelset_len = labelset_add(labelset, labelset_len, LABELSETMAXLEN, "1".getBytes(), 1); + if (labelset_len < 0){ + zeroize_verify(M_buf); + return -1; + } + + if (generalized_calculate_Bv(Bv_point, labelset, labelset_len, + eddsa_25519_pubkey_bytes, M_buf, MSTART, msg_len, sha512provider) != 0) + { + zeroize_verify(M_buf); + return -1; + } + + ge_p3_tobytes.ge_p3_tobytes(Bv_bytes, Bv_point); + + // R = solve_commitment(B, s, K, h) + if (generalized_solve_commitment(R_calc_bytes, K_point, null, + s_scalar, eddsa_25519_pubkey_bytes, h_scalar) != 0) + { + zeroize_verify(M_buf); + return -1; + } + + + // Rv = solve_commitment(Bv, s, Kv, h) + if (generalized_solve_commitment(Rv_calc_bytes, Kv_point, Bv_point, + s_scalar, Kv_bytes, h_scalar) != 0) + { + zeroize_verify(M_buf); + return -1; + } + + + ge_scalarmult_cofactor.ge_scalarmult_cofactor(cK_point, K_point); + ge_scalarmult_cofactor.ge_scalarmult_cofactor(cKv_point, Kv_point); + if (ge_isneutral.ge_isneutral(cK_point) == 1 || ge_isneutral.ge_isneutral(cKv_point) == 1 || + ge_isneutral.ge_isneutral(Bv_point) == 1) + { + zeroize_verify(M_buf); + return -1; + } + + // labelset3 = add_label(labels, "3") + // h = challenge(labelset3, (Bv || Kv || Rv), R, K, M) + labelset[(int) (labelset_len-1)] = 3; + + System.arraycopy(Bv_bytes, 0, extra, 0, POINTLEN); + System.arraycopy(Kv_bytes, 0, extra, POINTLEN, POINTLEN); + System.arraycopy(Rv_calc_bytes, 0, extra, 2*POINTLEN, POINTLEN); + + if (generalized_challenge(h_calc_scalar, + labelset, labelset_len, + extra, 3*POINTLEN, + R_calc_bytes, eddsa_25519_pubkey_bytes, + M_buf, MSTART, msg_len, + sha512provider) != 0) + { + zeroize_verify(M_buf); + return -1; + } + + + // if bytes_equal(h, h') + if (crypto_verify_32.crypto_verify_32(h_scalar, h_calc_scalar) != 0){ + zeroize_verify(M_buf); + return -1; + } + + // labelset4 = add_label(labels, "4") + // v = hash(labelset4 || c*Kv) + labelset[(int) (labelset_len-1)] = 4; + if (generalized_calculate_vrf_output(vrf_out, labelset, labelset_len, cKv_point, sha512provider) != 0){ + zeroize_verify(M_buf); + return -1; + } + + zeroize_verify(M_buf); + return 0; + } + + private static void zeroize_verify(byte[] M_buf){ + M_buf = new byte[1]; + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_x.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_x.java new file mode 100644 index 0000000..0a6e9a3 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/gen_x.java @@ -0,0 +1,165 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.Sha512; +import org.whispersystems.curve25519.java.fe_tobytes; +import org.whispersystems.curve25519.java.ge_p3; + +import static org.whispersystems.curve25519.java.ed25519.constants.POINTLEN; +import static org.whispersystems.curve25519.java.ed25519.constants.SCALARLEN; +import static org.whispersystems.curve25519.java.fe_frombytes.fe_frombytes; +import static org.whispersystems.curve25519.java.ge_p3_tobytes.ge_p3_tobytes; +import static org.whispersystems.curve25519.java.ge_scalarmult_base.ge_scalarmult_base; + +public class gen_x { + + /** + * convert z25519 pubkey to edwards pubkey + * @param ed_pubkey_bytes + * @param x25519_pubkey_bytes + * @return 0 if success + */ + static int convert_25519_pubkey(byte[] ed_pubkey_bytes, byte[] x25519_pubkey_bytes) { + int[] u = new int[10]; + int[] y = new int[10]; + + if (!fe_isreduced.fe_isreduced(x25519_pubkey_bytes)) + return -1; + fe_frombytes(u, x25519_pubkey_bytes); + fe_montx_to_edy.fe_montx_to_edy(y, u); + fe_tobytes.fe_tobytes(ed_pubkey_bytes, y); + return 0; + } + + + /** + * calculate ed_keypair from x25519 privkey + * @param K_bytes + * @param k_scalar + * @param x25519_privkey_scalar + * @return -1 if not successful + * 0 is successful + */ + static int calculate_25519_keypair(byte[] K_bytes, byte[] k_scalar, byte[] x25519_privkey_scalar) + { + byte[] kneg = new byte[SCALARLEN]; + ge_p3 ed_pubkey_point = new ge_p3(); + int sign_bit = 0; + + if (SCALARLEN != 32) + return -1; + + ge_scalarmult_base(ed_pubkey_point, x25519_privkey_scalar); + ge_p3_tobytes(K_bytes, ed_pubkey_point); + + sign_bit = (K_bytes[31] & 0x80) >> 7; + System.arraycopy(x25519_privkey_scalar, 0, k_scalar, 0, 32); + sc_neg.sc_neg(kneg, k_scalar); + sc_cmov.sc_cmov(k_scalar, kneg, sign_bit); + K_bytes[31] &= 0x7F; + + kneg = new byte[SCALARLEN]; + + return 0; + } + + /** + * + * @param signature_out + * @param x25519_privkey_scalar + * @param msg + * @param msg_len + * @param random + * @param sha512provider + * @param customization_label + * @param customization_label_len + * @return -1 if not successful + * 0 if successful + */ + public static int generalized_xeddsa_25519_sign(byte[] signature_out, + byte[] x25519_privkey_scalar, + byte[] msg, long msg_len, + byte[] random, Sha512 sha512provider, + byte[] customization_label, long customization_label_len) + { + byte[] K_bytes = new byte[POINTLEN]; + byte[] k_scalar = new byte[SCALARLEN]; + int retval = -1; + + if (calculate_25519_keypair(K_bytes, k_scalar, x25519_privkey_scalar) != 0) + return -1; + + retval = gen_eddsa.generalized_eddsa_25519_sign(signature_out, + K_bytes, k_scalar, + msg, msg_len, random, sha512provider, + customization_label, customization_label_len); + k_scalar = new byte[SCALARLEN]; + + return retval; + } + + + /** + * + * @param signature_out + * @param x25519_privkey_scalar + * @param msg + * @param msg_len + * @param random + * @param sha512provider + * @param customization_label + * @param customization_label_len + * @return -1 if not successful + * 0 if successful + */ + public static int generalized_xveddsa_25519_sign(byte[] signature_out, + byte[] x25519_privkey_scalar, + byte[] msg, long msg_len, + byte[] random, Sha512 sha512provider, + byte[] customization_label, long customization_label_len) + { + byte[] K_bytes = new byte[POINTLEN]; + byte[] k_scalar = new byte[SCALARLEN]; + int retval = -1; + + if (calculate_25519_keypair(K_bytes, k_scalar, x25519_privkey_scalar) != 0) + return -1; + + retval = gen_veddsa.generalized_veddsa_25519_sign(signature_out, K_bytes, k_scalar, + msg, msg_len, random, sha512provider, + customization_label, customization_label_len); + + k_scalar = new byte[SCALARLEN]; + + return retval; + } + + /** + * + * @param vrf_out + * @param signature + * @param x25519_pubkey_bytes + * @param msg + * @param msg_len + * @param customization_label + * @param customization_label_len + * @param sha512provider + * @return -1 if not successful + * 0 if successful + */ + static int generalized_xveddsa_25519_verify( + byte[] vrf_out, + byte[] signature, + byte[] x25519_pubkey_bytes, + byte[] msg, long msg_len, + byte[] customization_label, long customization_label_len, + Sha512 sha512provider) + { + byte[] K_bytes = new byte[POINTLEN]; + + if (convert_25519_pubkey(K_bytes, x25519_pubkey_bytes) != 0) + return -1; + + return gen_veddsa.generalized_veddsa_25519_verify(vrf_out, signature, K_bytes, msg, msg_len, + customization_label, customization_label_len, sha512provider); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/point_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/point_isreduced.java new file mode 100644 index 0000000..fc69e04 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/point_isreduced.java @@ -0,0 +1,15 @@ +package org.whispersystems.curve25519.java.ed25519; + +public class point_isreduced { + + static boolean point_isreduced(byte[] p) + { + byte[] strict = new byte[32]; + + System.arraycopy(p, 0, strict, 0, 32); + strict[31] &= 0x7F; /* mask off sign bit */ + return fe_isreduced.fe_isreduced(strict); + } + + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_cmov.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_cmov.java new file mode 100644 index 0000000..7ff7f32 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_cmov.java @@ -0,0 +1,17 @@ +package org.whispersystems.curve25519.java.ed25519; + +public class sc_cmov { + + static void sc_cmov(byte[] f, byte[] g, int b) + { + int count=32; + byte[] x = new byte[32]; + for (count=0; count < 32; count++) + x[count] = (byte) (f[count] ^ g[count]); + b = -b; + for (count=0; count < 32; count++) + x[count] &= b; + for (count=0; count < 32; count++) + f[count] = (byte) (f[count] ^ x[count]); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_isreduced.java new file mode 100644 index 0000000..3fd6f30 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_isreduced.java @@ -0,0 +1,19 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.crypto_verify_32.crypto_verify_32; +import static org.whispersystems.curve25519.java.sc_reduce.sc_reduce; + +public class sc_isreduced { + + static boolean sc_isreduced(byte[] s) + { + byte[] strict = new byte[64]; + + System.arraycopy(s, 0, strict, 0, 32); + + sc_reduce(strict); + if (crypto_verify_32(strict, s) != 0) + return false; + return true; + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_neg.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_neg.java new file mode 100644 index 0000000..12e05f3 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/sc_neg.java @@ -0,0 +1,24 @@ +package org.whispersystems.curve25519.java.ed25519; + +import static org.whispersystems.curve25519.java.sc_muladd.sc_muladd; + +public class sc_neg { + + + static final byte[] lminus1 = { + (byte) 0xec, (byte) 0xd3, (byte) 0xf5, (byte) 0x5c, (byte) 0x1a, (byte) 0x63, (byte) 0x12, (byte) 0x58, + (byte) 0xd6, (byte) 0x9c, (byte) 0xf7, (byte) 0xa2, (byte) 0xde, (byte) 0xf9, (byte) 0xde, (byte) 0x14, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}; + + /** + * b = -a (mod 1) + * @param b + * @param a + */ + static void sc_neg(byte[] b, byte[] a) + { + byte[] zero = new byte[32]; + sc_muladd(b, lminus1, a, zero); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ed25519/veddsa_sigs.java b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/veddsa_sigs.java new file mode 100644 index 0000000..f568210 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ed25519/veddsa_sigs.java @@ -0,0 +1,35 @@ +package org.whispersystems.curve25519.java.ed25519; + +import org.whispersystems.curve25519.java.Sha512; + +public class veddsa_sigs { + + public static int VRFsign(Sha512 sha512provider, byte[] result, byte[] privateKey, byte[] message, int message_len, byte[] random){ + + byte[] customization_label = "100".getBytes(); + + int ret_val = -1; + + ret_val = gen_x.generalized_xveddsa_25519_sign(result, privateKey, message, message_len, random, + sha512provider, null, 0); + + return ret_val; + + } + + public static int VRFverify(Sha512 sha512provider, byte[] vrf_out, byte[] signature, + byte[] publicKey, byte[] message, long messagelen){ + + byte[] customization_label = new byte[64]; + + int ret_val = -1; + + ret_val = gen_x.generalized_xveddsa_25519_verify(vrf_out, signature, publicKey, + message, messagelen, null, 0, sha512provider); + + + return ret_val; + + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_frombytes.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_frombytes.java index a67acff..c42d8e7 100644 --- a/common/src/main/java/org/whispersystems/curve25519/java/ge_frombytes.java +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_frombytes.java @@ -14,7 +14,7 @@ public class ge_frombytes { -32595792,-7943725,9377950,3500415,12389472,-272473,-25146209,-2005654,326686,11406482 } ; -static int ge_frombytes_negate_vartime(ge_p3 h,byte[] s) +public static int ge_frombytes_negate_vartime(ge_p3 h, byte[] s) { int[] u = new int[10]; int[] v = new int[10]; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a1a7687..2d80b69 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Oct 17 15:10:52 PDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000..ba91a7e Binary files /dev/null and b/tests/.DS_Store differ diff --git a/tests/src/.DS_Store b/tests/src/.DS_Store new file mode 100644 index 0000000..746c869 Binary files /dev/null and b/tests/src/.DS_Store differ diff --git a/tests/src/main/.DS_Store b/tests/src/main/.DS_Store new file mode 100644 index 0000000..5576e61 Binary files /dev/null and b/tests/src/main/.DS_Store differ diff --git a/tests/src/main/java/.DS_Store b/tests/src/main/java/.DS_Store new file mode 100644 index 0000000..a409ac9 Binary files /dev/null and b/tests/src/main/java/.DS_Store differ diff --git a/tests/src/main/java/org/.DS_Store b/tests/src/main/java/org/.DS_Store new file mode 100644 index 0000000..98b4384 Binary files /dev/null and b/tests/src/main/java/org/.DS_Store differ diff --git a/tests/src/main/java/org/whispersystems/.DS_Store b/tests/src/main/java/org/whispersystems/.DS_Store new file mode 100644 index 0000000..e125c50 Binary files /dev/null and b/tests/src/main/java/org/whispersystems/.DS_Store differ diff --git a/tests/src/main/java/org/whispersystems/curve25519/Curve25519Test.java b/tests/src/main/java/org/whispersystems/curve25519/Curve25519Test.java index e2286ba..db41669 100644 --- a/tests/src/main/java/org/whispersystems/curve25519/Curve25519Test.java +++ b/tests/src/main/java/org/whispersystems/curve25519/Curve25519Test.java @@ -1,12 +1,18 @@ package org.whispersystems.curve25519; import junit.framework.TestCase; +import org.whispersystems.curve25519.java.ed25519.ge_p3_tobytes; +import org.whispersystems.curve25519.java.ed25519.ge_scalarmult; +import org.whispersystems.curve25519.java.ge_frombytes; +import org.whispersystems.curve25519.java.ge_p3; +import org.whispersystems.curve25519.java.ed25519.ge_neg; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import static org.fest.assertions.Assertions.assertThat; +import static org.whispersystems.curve25519.java.ed25519.ge_isneutral.ge_isneutral; +import static org.whispersystems.curve25519.java.ge_scalarmult_base.ge_scalarmult_base; public abstract class Curve25519Test extends TestCase { @@ -142,6 +148,88 @@ public void testLargeSignatures() throws NoSuchAlgorithmException, InvalidKeyExc assertFalse(getInstance().verifySignature(keys.getPublicKey(), message, signature)); } + public void testVRFSignatures() throws NoSuchProviderException, IllegalArgumentException{ + Curve25519KeyPair keys = getInstance().generateKeyPair(); + byte[] message1 = new byte[1024]; + byte[] message2 = new byte[ 512]; + byte[] signature1 = getInstance().calculateVrfSignature(keys.getPrivateKey(), message1); + byte[] signature2 = getInstance().calculateVrfSignature(keys.getPrivateKey(), message2); + + try { + byte[] vrf_out = getInstance().verifyVrfSignature(keys.getPublicKey(), message1, signature1); + } catch (VrfSignatureVerificationFailedException e) { + throw new AssertionError("Sig verification failed!"); + } + + try { + byte[] vrf_out = getInstance().verifyVrfSignature(keys.getPublicKey(), message1, signature2); + throw new AssertionError("Sig verification succeeded!"); + } catch (VrfSignatureVerificationFailedException e) { + } + } + + public void testGeScalarMult() { + byte[] B_bytes = { + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }; + + byte[] misc_bytes = { + (byte) 0x57, (byte) 0x17, (byte) 0xfa, (byte) 0xce, (byte) 0xca, (byte) 0xb9, (byte) 0xdf, (byte) 0x0e, + (byte) 0x90, (byte) 0x67, (byte) 0xaa, (byte) 0x46, (byte) 0xba, (byte) 0x83, (byte) 0x2f, (byte) 0xeb, + (byte) 0x1c, (byte) 0x49, (byte) 0xd0, (byte) 0x21, (byte) 0xb1, (byte) 0x33, (byte) 0xff, (byte) 0x11, + (byte) 0xc9, (byte) 0x7a, (byte) 0xb8, (byte) 0xcf, (byte) 0xe3, (byte) 0x29, (byte) 0x46, (byte) 0x17, + }; + + byte[] q_scalar = { + (byte) 0xed, (byte) 0xd3, (byte) 0xf5, (byte) 0x5c, (byte) 0x1a, (byte) 0x63, (byte) 0x12, (byte) 0x58, + (byte) 0xd6, (byte) 0x9c, (byte) 0xf7, (byte) 0xa2, (byte) 0xde, (byte) 0xf9, (byte) 0xde, (byte) 0x14, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + }; + + byte[] c_scalar = { + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + byte[] neutral_bytes = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + + ge_p3 point1 = new ge_p3(), point2 = new ge_p3(), B_point = new ge_p3(), misc_point = new ge_p3(), miscneg_point = new ge_p3(); + + + byte[] output1 = new byte[32], output2 = new byte[32]; + if (ge_frombytes.ge_frombytes_negate_vartime(B_point, B_bytes) != 0) + throw new AssertionError("Ge From Bytes Negate Var Time Failed!"); + if (ge_frombytes.ge_frombytes_negate_vartime(miscneg_point, misc_bytes) != 0) + throw new AssertionError("Ge From Bytes Negate Var Time Failed!"); + ge_neg.ge_neg(B_point, B_point); + ge_neg.ge_neg(misc_point, miscneg_point); + + + ge_scalarmult_base(point1, q_scalar); + ge_scalarmult.ge_scalarmult(point2, q_scalar, B_point); + ge_p3_tobytes.ge_p3_tobytes(output1, point1); + ge_p3_tobytes.ge_p3_tobytes(output2, point2); + if (!java.util.Arrays.equals(output1, neutral_bytes)) + throw new AssertionError("Ge Scalar Multiplaction Failed!"); + if (!java.util.Arrays.equals(output1, output2)) + throw new AssertionError("Ge Scalar Multiplaction Failed!"); + if (ge_isneutral(point1 ) != 1 && ge_isneutral(point2)==1 && ge_isneutral(B_point) != 0) + throw new AssertionError("Ge Scalar Multiplaction Failed!"); + + } + protected Curve25519 getInstance() throws NoSuchProviderException { return Curve25519.getInstance(getProviderName()); }