package net.i2p.crypto.eddsa.math; import net.i2p.crypto.eddsa.*; import net.i2p.crypto.eddsa.spec.*; import org.hamcrest.core.*; import org.junit.*; import org.junit.rules.ExpectedException; import java.math.BigInteger; import java.util.Arrays; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; /** * @author str4d * Additional tests by NEM project team. * */ public class GroupElementTest { static final byte[] BYTES_ZEROZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000"); static final byte[] BYTES_ONEONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000080"); static final byte[] BYTES_TENZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000"); static final byte[] BYTES_ONETEN = Utils.hexToBytes("0a00000000000000000000000000000000000000000000000000000000000080"); static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512"); static final Curve curve = ed25519.getCurve(); static final FieldElement ZERO = curve.getField().ZERO; static final FieldElement ONE = curve.getField().ONE; static final FieldElement TWO = curve.getField().TWO; static final FieldElement TEN = curve.getField().fromByteArray(Utils.hexToBytes("0a00000000000000000000000000000000000000000000000000000000000000")); static final GroupElement P2_ZERO = GroupElement.p2(curve, ZERO, ONE, ONE); static final FieldElement[] PKR = new FieldElement[] { curve.getField().fromByteArray(Utils.hexToBytes("5849722e338aced7b50c7f0e9328f9a10c847b08e40af5c5b0577b0fd8984f15")), curve.getField().fromByteArray(Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29")) }; static final byte[] BYTES_PKR = Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29"); @Rule public ExpectedException exception = ExpectedException.none(); /** * Test method for {@link GroupElement#p2(Curve, FieldElement, FieldElement, FieldElement)}. */ @Test public void testP2() { final GroupElement t = GroupElement.p2(curve, ZERO, ONE, ONE); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.P2)); assertThat(t.X, is(ZERO)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ONE)); assertThat(t.T, is((FieldElement) null)); } /** * Test method for {@link GroupElement#p3(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}. */ @Test public void testP3() { final GroupElement t = GroupElement.p3(curve, ZERO, ONE, ONE, ZERO); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.P3)); assertThat(t.X, is(ZERO)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ONE)); assertThat(t.T, is(ZERO)); } /** * Test method for {@link GroupElement#p1p1(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}. */ @Test public void testP1p1() { final GroupElement t = GroupElement.p1p1(curve, ZERO, ONE, ONE, ONE); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.P1P1)); assertThat(t.X, is(ZERO)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ONE)); assertThat(t.T, is(ONE)); } /** * Test method for {@link GroupElement#precomp(Curve, FieldElement, FieldElement, FieldElement)}. */ @Test public void testPrecomp() { final GroupElement t = GroupElement.precomp(curve, ONE, ONE, ZERO); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.PRECOMP)); assertThat(t.X, is(ONE)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ZERO)); assertThat(t.T, is((FieldElement) null)); } /** * Test method for {@link GroupElement#cached(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}. */ @Test public void testCached() { final GroupElement t = GroupElement.cached(curve, ONE, ONE, ONE, ZERO); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.CACHED)); assertThat(t.X, is(ONE)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ONE)); assertThat(t.T, is(ZERO)); } /** * Test method for {@link GroupElement#GroupElement(Curve, GroupElement.Representation, FieldElement, FieldElement, FieldElement, FieldElement)}. */ @Test public void testGroupElementCurveRepresentationFieldElementFieldElementFieldElementFieldElement() { final GroupElement t = new GroupElement(curve, GroupElement.Representation.P3, ZERO, ONE, ONE, ZERO); assertThat(t.curve, is(equalTo(curve))); assertThat(t.repr, is(GroupElement.Representation.P3)); assertThat(t.X, is(ZERO)); assertThat(t.Y, is(ONE)); assertThat(t.Z, is(ONE)); assertThat(t.T, is(ZERO)); } /** * Tests {@link GroupElement#GroupElement(Curve, byte[])} and * {@link GroupElement#toByteArray()} against valid public keys. */ @Test public void testToAndFromByteArray() { GroupElement t; for (Ed25519TestVectors.TestTuple testCase : Ed25519TestVectors.testCases) { t = new GroupElement(curve, testCase.pk); assertThat("Test case " + testCase.caseNum + " failed", t.toByteArray(), is(equalTo(testCase.pk))); } } /** * Test method for {@link GroupElement#GroupElement(Curve, byte[])}. */ @Test public void testGroupElementByteArray() { final GroupElement t = new GroupElement(curve, BYTES_PKR); final GroupElement s = GroupElement.p3(curve, PKR[0], PKR[1], ONE, PKR[0].multiply(PKR[1])); assertThat(t, is(equalTo(s))); } @Test public void constructorUsingByteArrayReturnsExpectedResult() { for (int i=0; i<100; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); final byte[] bytes = g.toByteArray(); // Act: final GroupElement h1 = new GroupElement(curve, bytes); final GroupElement h2 = MathUtils.toGroupElement(bytes); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); } } /** * Test method for {@link GroupElement#toByteArray()}. * <p> * TODO 20141001 BR: why test with points which are not on the curve? */ @Test public void testToByteArray() { byte[] zerozero = GroupElement.p2(curve, ZERO, ZERO, ONE).toByteArray(); assertThat(zerozero.length, is(equalTo(BYTES_ZEROZERO.length))); assertThat(zerozero, is(equalTo(BYTES_ZEROZERO))); byte[] oneone = GroupElement.p2(curve, ONE, ONE, ONE).toByteArray(); assertThat(oneone.length, is(equalTo(BYTES_ONEONE.length))); assertThat(oneone, is(equalTo(BYTES_ONEONE))); byte[] tenzero = GroupElement.p2(curve, TEN, ZERO, ONE).toByteArray(); assertThat(tenzero.length, is(equalTo(BYTES_TENZERO.length))); assertThat(tenzero, is(equalTo(BYTES_TENZERO))); byte[] oneten = GroupElement.p2(curve, ONE, TEN, ONE).toByteArray(); assertThat(oneten.length, is(equalTo(BYTES_ONETEN.length))); assertThat(oneten, is(equalTo(BYTES_ONETEN))); byte[] pkr = GroupElement.p2(curve, PKR[0], PKR[1], ONE).toByteArray(); assertThat(pkr.length, is(equalTo(BYTES_PKR.length))); assertThat(pkr, is(equalTo(BYTES_PKR))); } @Test public void toByteArrayReturnsExpectedResult() { for (int i=0; i<100; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final byte[] gBytes = g.toByteArray(); final byte[] bytes = MathUtils.toByteArray(MathUtils.toBigInteger(g.getY())); if (MathUtils.toBigInteger(g.getX()).mod(new BigInteger("2")).equals(BigInteger.ONE)) { bytes[31] |= 0x80; } // Assert: Assert.assertThat(Arrays.equals(gBytes, bytes), IsEqual.equalTo(true)); } } // region toX where X is the representation /** * Test method for {@link GroupElement#toP2()}. */ @Test public void testToP2() { GroupElement p3zero = curve.getZero(GroupElement.Representation.P3); GroupElement t = p3zero.toP2(); assertThat(t.repr, is(GroupElement.Representation.P2)); assertThat(t.X, is(p3zero.X)); assertThat(t.Y, is(p3zero.Y)); assertThat(t.Z, is(p3zero.Z)); assertThat(t.T, is((FieldElement) null)); GroupElement B = ed25519.getB(); t = B.toP2(); assertThat(t.repr, is(GroupElement.Representation.P2)); assertThat(t.X, is(B.X)); assertThat(t.Y, is(B.Y)); assertThat(t.Z, is(B.Z)); assertThat(t.T, is((FieldElement) null)); } @Test (expected = IllegalArgumentException.class) public void toP2ThrowsIfGroupElementHasPrecompRepresentation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP); // Assert: g.toP2(); } @Test (expected = IllegalArgumentException.class) public void toP2ThrowsIfGroupElementHasCachedRepresentation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED); // Assert: g.toP2(); } @Test public void toP2ReturnsExpectedResultIfGroupElementHasP2Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2); // Act: final GroupElement h = g.toP2(); // Assert: Assert.assertThat(h, IsEqual.equalTo(g)); Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2)); Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX())); Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY())); Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ())); Assert.assertThat(h.getT(), IsEqual.equalTo(null)); } } @Test public void toP2ReturnsExpectedResultIfGroupElementHasP3Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g.toP2(); final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P2); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2)); Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX())); Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY())); Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ())); Assert.assertThat(h1.getT(), IsEqual.equalTo(null)); } } @Test public void toP2ReturnsExpectedResultIfGroupElementHasP1P1Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1); // Act: final GroupElement h1 = g.toP2(); final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P2); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2)); Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX().multiply(g.getT()))); Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().multiply(g.getZ()))); Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ().multiply(g.getT()))); Assert.assertThat(h1.getT(), IsEqual.equalTo(null)); } } @Test (expected = IllegalArgumentException.class) public void toP3ThrowsIfGroupElementHasP2Representation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2); // Assert: g.toP3(); } @Test (expected = IllegalArgumentException.class) public void toP3ThrowsIfGroupElementHasPrecompRepresentation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP); // Assert: g.toP3(); } @Test (expected = IllegalArgumentException.class) public void toP3ThrowsIfGroupElementHasCachedRepresentation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED); // Assert: g.toP3(); } @Test public void toP3ReturnsExpectedResultIfGroupElementHasP1P1Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1); // Act: final GroupElement h1 = g.toP3(); final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P3); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P3)); Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX().multiply(g.getT()))); Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().multiply(g.getZ()))); Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ().multiply(g.getT()))); Assert.assertThat(h1.getT(), IsEqual.equalTo(g.getX().multiply(g.getY()))); } } @Test public void toP3ReturnsExpectedResultIfGroupElementHasP3Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h = g.toP3(); // Assert: Assert.assertThat(h, IsEqual.equalTo(g)); Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P3)); Assert.assertThat(h, IsEqual.equalTo(g)); Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX())); Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY())); Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ())); Assert.assertThat(h.getT(), IsEqual.equalTo(g.getT())); } } @Test (expected = IllegalArgumentException.class) public void toCachedThrowsIfGroupElementHasP2Representation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2); // Assert: g.toCached(); } @Test (expected = IllegalArgumentException.class) public void toCachedThrowsIfGroupElementHasPrecompRepresentation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP); // Assert: g.toCached(); } @Test (expected = IllegalArgumentException.class) public void toCachedThrowsIfGroupElementHasP1P1Representation() { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1); // Assert: g.toCached(); } @Test public void toCachedReturnsExpectedResultIfGroupElementHasCachedRepresentation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED); // Act: final GroupElement h = g.toCached(); // Assert: Assert.assertThat(h, IsEqual.equalTo(g)); Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED)); Assert.assertThat(h, IsEqual.equalTo(g)); Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX())); Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY())); Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ())); Assert.assertThat(h.getT(), IsEqual.equalTo(g.getT())); } } @Test public void toCachedReturnsExpectedResultIfGroupElementHasP3Representation() { for (int i=0; i<10; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g.toCached(); final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.CACHED); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED)); Assert.assertThat(h1, IsEqual.equalTo(g)); Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getY().add(g.getX()))); Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().subtract(g.getX()))); Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ())); Assert.assertThat(h1.getT(), IsEqual.equalTo(g.getT().multiply(curve.get2D()))); } } // endregion /** * Test method for {@link GroupElement#precompute(boolean)}. */ @Test public void testPrecompute() { GroupElement B = ed25519.getB(); assertThat(B.precmp, is(equalTo(PrecomputationTestVectors.testPrecmp))); assertThat(B.dblPrecmp, is(equalTo(PrecomputationTestVectors.testDblPrecmp))); } @Test public void precomputedTableContainsExpectedGroupElements() { // Arrange: GroupElement g = ed25519.getB(); // Act + Assert: for (int i = 0; i < 32; i++) { GroupElement h = g; for (int j = 0; j < 8; j++) { Assert.assertThat(MathUtils.toRepresentation(h, GroupElement.Representation.PRECOMP), IsEqual.equalTo(ed25519.getB().precmp[i][j])); h = MathUtils.addGroupElements(h, g); } for (int k = 0; k < 8; k++) { g = MathUtils.addGroupElements(g, g); } } } @Test public void dblPrecomputedTableContainsExpectedGroupElements() { // Arrange: GroupElement g = ed25519.getB(); GroupElement h = MathUtils.addGroupElements(g, g); // Act + Assert: for (int i=0; i<8; i++) { Assert.assertThat(MathUtils.toRepresentation(g, GroupElement.Representation.PRECOMP), IsEqual.equalTo(ed25519.getB().dblPrecmp[i])); g = MathUtils.addGroupElements(g, h); } } /** * Test method for {@link GroupElement#dbl()}. */ @Test public void testDbl() { GroupElement B = ed25519.getB(); // 2 * B = B + B assertThat(B.dbl(), is(equalTo(B.add(B.toCached())))); } @Test public void dblReturnsExpectedResult() { for (int i=0; i<1000; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g.dbl(); final GroupElement h2 = MathUtils.doubleGroupElement(g); // Assert: Assert.assertThat(h2, IsEqual.equalTo(h1)); } } @Test public void addingNeutralGroupElementDoesNotChangeGroupElement() { final GroupElement neutral = GroupElement.p3(curve, curve.getField().ZERO, curve.getField().ONE, curve.getField().ONE, curve.getField().ZERO); for (int i=0; i<1000; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g.add(neutral.toCached()); final GroupElement h2 = neutral.add(g.toCached()); // Assert: Assert.assertThat(g, IsEqual.equalTo(h1)); Assert.assertThat(g, IsEqual.equalTo(h2)); } } @Test public void addReturnsExpectedResult() { for (int i=0; i<1000; i++) { // Arrange: final GroupElement g1 = MathUtils.getRandomGroupElement(); final GroupElement g2 = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g1.add(g2.toCached()); final GroupElement h2 = MathUtils.addGroupElements(g1, g2); // Assert: Assert.assertThat(h2, IsEqual.equalTo(h1)); } } @Test public void subReturnsExpectedResult() { for (int i=0; i<1000; i++) { // Arrange: final GroupElement g1 = MathUtils.getRandomGroupElement(); final GroupElement g2 = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g1.sub(g2.toCached()); final GroupElement h2 = MathUtils.addGroupElements(g1, MathUtils.negateGroupElement(g2)); // Assert: Assert.assertThat(h2, IsEqual.equalTo(h1)); } } // region hashCode / equals /** * Test method for {@link GroupElement#equals(java.lang.Object)}. */ @Test public void testEqualsObject() { assertThat(GroupElement.p2(curve, ZERO, ONE, ONE), is(equalTo(P2_ZERO))); } @Test public void equalsOnlyReturnsTrueForEquivalentObjects() { // Arrange: final GroupElement g1 = MathUtils.getRandomGroupElement(); final GroupElement g2 = MathUtils.toRepresentation(g1, GroupElement.Representation.P2); final GroupElement g3 = MathUtils.toRepresentation(g1, GroupElement.Representation.CACHED); final GroupElement g4 = MathUtils.toRepresentation(g1, GroupElement.Representation.P1P1); final GroupElement g5 = MathUtils.getRandomGroupElement(); // Assert Assert.assertThat(g2, IsEqual.equalTo(g1)); Assert.assertThat(g3, IsEqual.equalTo(g1)); Assert.assertThat(g1, IsEqual.equalTo(g4)); Assert.assertThat(g1, IsNot.not(IsEqual.equalTo(g5))); Assert.assertThat(g2, IsNot.not(IsEqual.equalTo(g5))); Assert.assertThat(g3, IsNot.not(IsEqual.equalTo(g5))); Assert.assertThat(g5, IsNot.not(IsEqual.equalTo(g4))); } @Test public void hashCodesAreEqualForEquivalentObjects() { // Arrange: final GroupElement g1 = MathUtils.getRandomGroupElement(); final GroupElement g2 = MathUtils.toRepresentation(g1, GroupElement.Representation.P2); final GroupElement g3 = MathUtils.toRepresentation(g1, GroupElement.Representation.P1P1); final GroupElement g4 = MathUtils.getRandomGroupElement(); // Assert Assert.assertThat(g2.hashCode(), IsEqual.equalTo(g1.hashCode())); Assert.assertThat(g3.hashCode(), IsEqual.equalTo(g1.hashCode())); Assert.assertThat(g1.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode()))); Assert.assertThat(g2.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode()))); Assert.assertThat(g3.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode()))); } // endregion static final byte[] BYTES_ZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000"); static final byte[] BYTES_ONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000"); static final byte[] BYTES_42 = Utils.hexToBytes("2A00000000000000000000000000000000000000000000000000000000000000"); static final byte[] BYTES_1234567890 = Utils.hexToBytes("D202964900000000000000000000000000000000000000000000000000000000"); static final byte[] RADIX16_ZERO = Utils.hexToBytes("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); static final byte[] RADIX16_ONE = Utils.hexToBytes("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); static final byte[] RADIX16_42 = Utils.hexToBytes("FA030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); /** * Test method for {@link GroupElement#toRadix16(byte[])}. */ @Test public void testToRadix16() { assertThat(GroupElement.toRadix16(BYTES_ZERO), is(RADIX16_ZERO)); assertThat(GroupElement.toRadix16(BYTES_ONE), is(RADIX16_ONE)); assertThat(GroupElement.toRadix16(BYTES_42), is(RADIX16_42)); byte[] from1234567890 = GroupElement.toRadix16(BYTES_1234567890); int total = 0; for (int i = 0; i < from1234567890.length; i++) { assertThat(from1234567890[i], is(greaterThanOrEqualTo((byte)-8))); assertThat(from1234567890[i], is(lessThanOrEqualTo((byte)8))); total += from1234567890[i] * Math.pow(16, i); } assertThat(total, is(1234567890)); byte[] pkrR16 = GroupElement.toRadix16(BYTES_PKR); for (int i = 0; i < pkrR16.length; i++) { assertThat(pkrR16[i], is(greaterThanOrEqualTo((byte)-8))); assertThat(pkrR16[i], is(lessThanOrEqualTo((byte)8))); } } /** * Test method for {@link GroupElement#cmov(GroupElement, int)}. */ @Test public void testCmov() { GroupElement a = curve.getZero(GroupElement.Representation.PRECOMP); GroupElement b = GroupElement.precomp(curve, TWO, ZERO, TEN); assertThat(a.cmov(b, 0), is(equalTo(a))); assertThat(a.cmov(b, 1), is(equalTo(b))); } /** * Test method for {@link GroupElement#select(int, int)}. */ @Test public void testSelect() { GroupElement B = ed25519.getB(); for (int i = 0; i < 32; i++) { // 16^i 0 B assertThat(i + ",0", B.select(i, 0), is(equalTo(GroupElement.precomp(curve, ONE, ONE, ZERO)))); for (int j = 1; j < 8; j++) { // 16^i r_i B GroupElement t = B.select(i, j); assertThat(i + "," + j, t, is(equalTo(B.precmp[i][j-1]))); // -16^i r_i B t = B.select(i, -j); GroupElement neg = GroupElement.precomp(curve, B.precmp[i][j-1].Y, B.precmp[i][j-1].X, B.precmp[i][j-1].Z.negate()); assertThat(i + "," + -j, t, is(equalTo(neg))); } } } // region scalar multiplication /** * Test method for {@link GroupElement#scalarMultiply(byte[])}. * Test values generated with Python Ed25519 implementation. */ @Test public void testScalarMultiplyByteArray() { // Little-endian byte[] zero = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000"); byte[] one = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000"); byte[] two = Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000"); byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c"); GroupElement A = new GroupElement(curve, Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66")); assertThat("scalarMultiply(0) failed", ed25519.getB().scalarMultiply(zero), is(equalTo(curve.getZero(GroupElement.Representation.P3)))); assertThat("scalarMultiply(1) failed", ed25519.getB().scalarMultiply(one), is(equalTo(ed25519.getB()))); assertThat("scalarMultiply(2) failed", ed25519.getB().scalarMultiply(two), is(equalTo(ed25519.getB().dbl()))); assertThat("scalarMultiply(a) failed", ed25519.getB().scalarMultiply(a), is(equalTo(A))); } @Test public void scalarMultiplyBasePointWithZeroReturnsNeutralElement() { // Arrange: final GroupElement basePoint = ed25519.getB(); // Act: final GroupElement g = basePoint.scalarMultiply(curve.getField().ZERO.toByteArray()); // Assert: Assert.assertThat(curve.getZero(GroupElement.Representation.P3), IsEqual.equalTo(g)); } @Test public void scalarMultiplyBasePointWithOneReturnsBasePoint() { // Arrange: final GroupElement basePoint = ed25519.getB(); // Act: final GroupElement g = basePoint.scalarMultiply(curve.getField().ONE.toByteArray()); // Assert: Assert.assertThat(basePoint, IsEqual.equalTo(g)); } // This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result. @Test public void scalarMultiplyBasePointReturnsExpectedResult() { for (int i=0; i<100; i++) { // Arrange: final GroupElement basePoint = ed25519.getB(); final FieldElement f = MathUtils.getRandomFieldElement(); // Act: final GroupElement g = basePoint.scalarMultiply(f.toByteArray()); final GroupElement h = MathUtils.scalarMultiplyGroupElement(basePoint, f); // Assert: Assert.assertThat(g, IsEqual.equalTo(h)); } } @Test public void testDoubleScalarMultiplyVariableTime() { // Little-endian byte[] zero = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000"); byte[] one = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000"); byte[] two = Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000"); byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c"); GroupElement A = new GroupElement(curve, Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66")); GroupElement B = ed25519.getB(); GroupElement geZero = curve.getZero(GroupElement.Representation.P3); geZero.precompute(false); // 0 * GE(0) + 0 * GE(0) = GE(0) assertThat(geZero.doubleScalarMultiplyVariableTime(geZero, zero, zero), is(equalTo(geZero))); // 0 * GE(0) + 0 * B = GE(0) assertThat(B.doubleScalarMultiplyVariableTime(geZero, zero, zero), is(equalTo(geZero))); // 1 * GE(0) + 0 * B = GE(0) assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, zero), is(equalTo(geZero))); // 1 * GE(0) + 1 * B = B assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, one), is(equalTo(B))); // 1 * B + 1 * B = 2 * B assertThat(B.doubleScalarMultiplyVariableTime(B, one, one), is(equalTo(B.dbl()))); // 1 * B + 2 * B = 3 * B assertThat(B.doubleScalarMultiplyVariableTime(B, one, two), is(equalTo(B.dbl().toP3().add(B.toCached())))); // 2 * B + 2 * B = 4 * B assertThat(B.doubleScalarMultiplyVariableTime(B, two, two), is(equalTo(B.dbl().toP3().dbl()))); // 0 * B + a * B = A assertThat(B.doubleScalarMultiplyVariableTime(B, zero, a), is(equalTo(A))); // a * B + 0 * B = A assertThat(B.doubleScalarMultiplyVariableTime(B, a, zero), is(equalTo(A))); // a * B + a * B = 2 * A assertThat(B.doubleScalarMultiplyVariableTime(B, a, a), is(equalTo(A.dbl()))); } // This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result. @Test public void doubleScalarMultiplyVariableTimeReturnsExpectedResult() { for (int i=0; i<50; i++) { // Arrange: final GroupElement basePoint = ed25519.getB(); final GroupElement g = MathUtils.getRandomGroupElement(); g.precompute(false); final FieldElement f1 = MathUtils.getRandomFieldElement(); final FieldElement f2 = MathUtils.getRandomFieldElement(); // Act: final GroupElement h1 = basePoint.doubleScalarMultiplyVariableTime(g, f2.toByteArray(), f1.toByteArray()); final GroupElement h2 = MathUtils.doubleScalarMultiplyGroupElements(basePoint, f1, g, f2); // Assert: Assert.assertThat(h1, IsEqual.equalTo(h2)); } } // endregion /** * Test method for {@link GroupElement#isOnCurve(Curve)}. */ @Test public void testIsOnCurve() { assertThat(P2_ZERO.isOnCurve(curve), is(true)); assertThat(GroupElement.p2(curve, ZERO, ZERO, ONE).isOnCurve(curve), is(false)); assertThat(GroupElement.p2(curve, ONE, ONE, ONE).isOnCurve(curve), is(false)); assertThat(GroupElement.p2(curve, TEN, ZERO, ONE).isOnCurve(curve), is(false)); assertThat(GroupElement.p2(curve, ONE, TEN, ONE).isOnCurve(curve), is(false)); assertThat(GroupElement.p2(curve, PKR[0], PKR[1], ONE).isOnCurve(curve), is(true)); } @Test public void isOnCurveReturnsTrueForPointsOnTheCurve() { for (int i=0; i<100; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Assert: Assert.assertThat(g.isOnCurve(), IsEqual.equalTo(true)); } } @Test public void isOnCurveReturnsFalseForPointsNotOnTheCurve() { for (int i=0; i<100; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); final GroupElement h = GroupElement.p2(curve, g.getX(), g.getY(), g.getZ().multiply(curve.getField().TWO)); // Assert (can only fail for 5*Z^2=1): Assert.assertThat(h.isOnCurve(), IsEqual.equalTo(false)); } } }