blob: 6ae95d8cf20664cc608404cdaaa5e1ae2c6aee7d [file] [log] [blame]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.security.cryptauth.lib.securegcm;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
import com.google.security.cryptauth.lib.securegcm.Ed25519.Ed25519Exception;
import java.math.BigInteger;
import junit.framework.TestCase;
/**
* Android compatible tests for the {@link Ed25519} class.
*/
public class Ed25519Test extends TestCase {
// Points on the curve
private static final int HEX_RADIX = 16;
private static final BigInteger[] KM = new BigInteger[] {
new BigInteger("1981FB43F103290ECF9772022DB8B19BFAF389057ED91E8486EB368763435925", HEX_RADIX),
new BigInteger("A714C34F3B588AAC92FD2587884A20964FD351A1F147D5C4BBF5C2F37A77C36", HEX_RADIX)};
private static final BigInteger[] KN = new BigInteger[] {
new BigInteger("201A184F47D9A7973891D148E3D1C864D8084547131C2C1CEFB7EEBD26C63567", HEX_RADIX),
new BigInteger("6DA2D3B18EC4F9AA3B08E39C997CD8BF6E9948FFD4FEFFECAF8DD0B3D648B7E8", HEX_RADIX)};
// Curve prime P
private static final BigInteger P =
new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", HEX_RADIX);
// Test vectors obtain by multiplying KM by k by manually using the official implementation
// see: http://ed25519.cr.yp.to/python/ed25519.py
// k = 2
private static final BigInteger[] KM_2 = new BigInteger[] {
new BigInteger("718079972e63c2d62caf0ee93ec6f00337ceaff4e283181c04c4082b1d5e1ecf", HEX_RADIX),
new BigInteger("143d18d393a8058c8614335bf36bf59364cc7c451db74726b322ce9d0b826d51", HEX_RADIX)
};
// k = 3
private static final BigInteger[] KM_3 = new BigInteger[] {
new BigInteger("39DA3C92EFC0577586B4D58F4A5C0BF65A6CC8F6BF358F38D70B2E6C28A31E8E", HEX_RADIX),
new BigInteger("6D194F054B3FC2BE217F6A360BBEC747D2937FCEBD74B67FC3B20ED638ADD670", HEX_RADIX)
};
// k = 317698
private static final BigInteger[] KM_317698 = new BigInteger[] {
new BigInteger("7945D0ADEB568B16495476E81ADF281F4515439AE835914FBF6CEEAFEB9CD7E8", HEX_RADIX),
new BigInteger("3631503DCDEBC0BF9BB1FFC3984A8CB52A34FFC2E77E9C19FD896DC6EE64A530", HEX_RADIX)
};
// k = P
private static final BigInteger[] KM_HUGE = new BigInteger[] {
new BigInteger("530162B05F440E00E219DFD3188524821C860C41FD87B9AC6AF2A283FDD585A1", HEX_RADIX),
new BigInteger("48385A7D2BB858F3DB7F72E7CDFE218B9CA84DDA8BD64C3775AA43551D974F60", HEX_RADIX)
};
// k = P + 10000
private static final BigInteger[] KM_XRAHUGE = new BigInteger[] {
new BigInteger("16377E9F5EE2C0F4C70E17AC298EF670700A7CB186EEB0DA10CDD59635000AF8", HEX_RADIX),
new BigInteger("5BD7921EEE662ACBAC3A96D8B6039D2356F154859FAF41FD2F0D99DF06CD2EAE", HEX_RADIX)
};
// Helpful constants
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger ZERO = BigInteger.ZERO;
// Identity element of the group (the zero) in affine and extended representations
private static final BigInteger[] ID = new BigInteger[] {ZERO, ONE};
private static final BigInteger[] ID_EX = new BigInteger[] {ZERO, ONE, ONE, ZERO};
public void testValidPoints() throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
// We've got a couple of valid points
Ed25519.validateAffinePoint(KM);
Ed25519.validateAffinePoint(KN);
// And a bunch of invalid ones
try {
Ed25519.validateAffinePoint(new BigInteger[] {ZERO, ONE});
fail("Validate point not catching zero x coordinates");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("positive"));
}
try {
Ed25519.validateAffinePoint(new BigInteger[] {ONE, ZERO});
fail("Validate point not catching zero y coordinates");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("positive"));
}
try {
Ed25519.validateAffinePoint(new BigInteger[] {new BigInteger("-1"), ONE});
fail("Validate point not catching negative x coordinates");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("positive"));
}
try {
Ed25519.validateAffinePoint(new BigInteger[] {ONE, new BigInteger("-1")});
fail("Validate point not catching negative y coordinates");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("positive"));
}
try {
Ed25519.validateAffinePoint(new BigInteger[] {ONE, ONE});
fail("Validate point not catching points that are not on curve");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("expected curve"));
}
}
public void testAffineExtendedConversion() throws Exception {
BigInteger[] km1 = Ed25519.toAffine(Ed25519.toExtended(KM));
BigInteger[] kn1 = Ed25519.toAffine(Ed25519.toExtended(KN));
assertArrayEquals(KM, km1);
assertArrayEquals(KN, kn1);
assertArrayEquals(ID, Ed25519.toAffine(ID_EX));
assertArrayEquals(ID_EX, Ed25519.toExtended(ID));
}
public void testRepresentationCheck() throws Exception {
Ed25519.checkPointIsInAffineRepresentation(KM);
Ed25519.checkPointIsInExtendedRepresentation(ID_EX);
try {
Ed25519.checkPointIsInExtendedRepresentation(KM);
fail("Point is not really in extended representation, expected failure");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("not in extended"));
}
try {
Ed25519.checkPointIsInAffineRepresentation(Ed25519.toExtended(KM));
fail("Point is not really in affine representation, expected failure");
} catch (Ed25519Exception e) {
assertThat(e.getMessage(), containsString("not in affine"));
}
}
public void testAddSubtractExtendedPoints() throws Exception {
// Adding/subtracting identity to/from itself should yield the identity point
assertArrayEquals(ID, Ed25519.addAffinePoints(ID, ID));
assertArrayEquals(ID, Ed25519.subtractAffinePoints(ID, ID));
// In fact adding/subtracting the identity point to/from any point should yield that point
assertArrayEquals(KM, Ed25519.addAffinePoints(KM, ID));
assertArrayEquals(KM, Ed25519.subtractAffinePoints(KM, ID));
// Subtracting a point from itself should yield the identity element
assertArrayEquals(ID, Ed25519.subtractAffinePoints(KM, KM));
assertArrayEquals(ID, Ed25519.subtractAffinePoints(KN, KN));
// Adding and subtracting should yield the same point
assertArrayEquals(KM, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KM, KN), KN));
assertArrayEquals(KN, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KN, KM), KM));
}
public void testScalarMultiplyExtendedPoints() throws Exception {
// A point times one is the point itself
assertArrayEquals(KM, Ed25519.scalarMultiplyAffinePoint(KM, ONE));
assertArrayEquals(KN, Ed25519.scalarMultiplyAffinePoint(KN, ONE));
// A point times zero is the identity point
assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KM, ZERO));
assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KN, ZERO));
// The identity times a scalar is the identity
assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(ID, BigInteger.valueOf(317698)));
// Use test vectors
assertArrayEquals(KM_2, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(2)));
assertArrayEquals(KM_3, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(3)));
assertArrayEquals(KM_317698, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(317698)));
assertArrayEquals(KM_HUGE, Ed25519.scalarMultiplyAffinePoint(KM, P));
assertArrayEquals(KM_XRAHUGE,
Ed25519.scalarMultiplyAffinePoint(KM, P.add(BigInteger.valueOf(10000))));
}
}