package de.persosim.simulator.crypto; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.math.BigInteger; import java.security.KeyPair; import java.security.spec.ECPoint; import org.junit.Test; import de.persosim.simulator.test.PersoSimTestCase; import de.persosim.simulator.tlv.Asn1; import de.persosim.simulator.utils.HexString; import de.persosim.simulator.utils.Utils; /** * @author slutters * */ public class CryptoUtilTest extends PersoSimTestCase { /** * Positive test case: multiply EC point with scalar (with performance optimization). */ @Test public void testScalarPointMultiplicationEllipticCurveBigIntegerECPointBigInteger() { DomainParameterSetEcdh domParamsEcdh = (DomainParameterSetEcdh) StandardizedDomainParameters.getDomainParameterSetById(13); // values originate from successful PACE test run and are checked against the implementation of scalar point multiplication by Bouncy Castle BigInteger px = new BigInteger(1, HexString.toByteArray("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262")); BigInteger py = new BigInteger(1, HexString.toByteArray("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")); BigInteger mult = new BigInteger(1, HexString.toByteArray("FA587945E9FE2AEB417DF0ADF951B7CBD9D5E476F8F6EF1B701C59C56B180204")); BigInteger expectedX = new BigInteger(1, HexString.toByteArray("874F6277A3C0621DFE106B47DA1242C0F7155905B3A1847D59A64494CF5CE0B9")); BigInteger expectedY = new BigInteger(1, HexString.toByteArray("3E4EBE21E2E131D5A740037635F823DFB859AC58305CEEC9992CEE437F60A730")); ECPoint ecPointP = new ECPoint(px, py); ECPoint expectedEcPoint = new ECPoint(expectedX, expectedY); ECPoint receivedEcPoint = CryptoUtil.scalarPointMultiplication(domParamsEcdh.getCurve(), domParamsEcdh.getOrder(), ecPointP, mult); assertEquals("mult x", expectedEcPoint.getAffineX(), receivedEcPoint.getAffineX()); assertEquals("mult y", expectedEcPoint.getAffineY(), receivedEcPoint.getAffineY()); } /** * Positive test case: multiply EC point with scalar (without performance optimization). */ @Test public void testScalarPointMultiplicationEllipticCurveECPointBigInteger() { DomainParameterSetEcdh domParamsEcdh = (DomainParameterSetEcdh) StandardizedDomainParameters.getDomainParameterSetById(13); // values originate from successful PACE test run and are checked against the implementation of scalar point multiplication by Bouncy Castle BigInteger px = new BigInteger(1, HexString.toByteArray("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262")); BigInteger py = new BigInteger(1, HexString.toByteArray("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")); BigInteger mult = new BigInteger(1, HexString.toByteArray("FA587945E9FE2AEB417DF0ADF951B7CBD9D5E476F8F6EF1B701C59C56B180204")); BigInteger expectedX = new BigInteger(1, HexString.toByteArray("874F6277A3C0621DFE106B47DA1242C0F7155905B3A1847D59A64494CF5CE0B9")); BigInteger expectedY = new BigInteger(1, HexString.toByteArray("3E4EBE21E2E131D5A740037635F823DFB859AC58305CEEC9992CEE437F60A730")); ECPoint ecPointP = new ECPoint(px, py); ECPoint expectedEcPoint = new ECPoint(expectedX, expectedY); ECPoint receivedEcPoint = CryptoUtil.scalarPointMultiplication(domParamsEcdh.getCurve(), ecPointP, mult); assertEquals("mult x", expectedEcPoint.getAffineX(), receivedEcPoint.getAffineX()); assertEquals("mult y", expectedEcPoint.getAffineY(), receivedEcPoint.getAffineY()); } /** * Positive test case: add two EC points. */ @Test public void testAddPoint() { DomainParameterSetEcdh domParamsEcdh = (DomainParameterSetEcdh) StandardizedDomainParameters.getDomainParameterSetById(13); // values originate from successful PACE test run and are checked against the implementation of point addition by Bouncy Castle BigInteger qx = new BigInteger(1, HexString.toByteArray("3EB50DD69CA2E6B0BE8D4C3089DD55F1657273CFC5728012CA346BAE0AF9A7D8")); BigInteger qy = new BigInteger(1, HexString.toByteArray("829F38EB7E87D468BD9A63CEE4CB15DA25D6EAFE1008FD889D3D6B0F5FB04C02")); BigInteger px = new BigInteger(1, HexString.toByteArray("2100DFDFFE149B14E2D9C0BCD71F50B1A96BC6778531FAE793C3AB1BCCF3FD68")); BigInteger py = new BigInteger(1, HexString.toByteArray("4DBEF9BE48DEB0183AA6AB8BD2B51D7870E050993BEBE823A6AA976AC3088611")); BigInteger expectedX = new BigInteger(1, HexString.toByteArray("5EED472701BF5F15C19A7BA97323DC6BD8DF35C331D78B9EAA57A8864BA00D9E")); BigInteger expectedY = new BigInteger(1, HexString.toByteArray("2DD711DA7FF82CA05D0C67D01AFD94210512A908EBF85ADE326F487E05D4C390")); ECPoint ecPointQ = new ECPoint(qx, qy); ECPoint ecPointP = new ECPoint(px, py); ECPoint expectedEcPoint = new ECPoint(expectedX, expectedY); ECPoint receivedEcPoint = CryptoUtil.addPoint(domParamsEcdh.getCurve(), ecPointQ, ecPointP); assertEquals("add x", expectedEcPoint.getAffineX(), receivedEcPoint.getAffineX()); assertEquals("add y", expectedEcPoint.getAffineY(), receivedEcPoint.getAffineY()); } /** * Positive test case: add two EC points and check commutativity of parameters. */ @Test public void testAddPoint_CommutativityOfParameters() { DomainParameterSetEcdh domParamsEcdh = (DomainParameterSetEcdh) StandardizedDomainParameters.getDomainParameterSetById(13); // values originate from successful PACE test run and are checked against the implementation of point addition by Bouncy Castle BigInteger qx = new BigInteger(1, HexString.toByteArray("3EB50DD69CA2E6B0BE8D4C3089DD55F1657273CFC5728012CA346BAE0AF9A7D8")); BigInteger qy = new BigInteger(1, HexString.toByteArray("829F38EB7E87D468BD9A63CEE4CB15DA25D6EAFE1008FD889D3D6B0F5FB04C02")); BigInteger px = new BigInteger(1, HexString.toByteArray("2100DFDFFE149B14E2D9C0BCD71F50B1A96BC6778531FAE793C3AB1BCCF3FD68")); BigInteger py = new BigInteger(1, HexString.toByteArray("4DBEF9BE48DEB0183AA6AB8BD2B51D7870E050993BEBE823A6AA976AC3088611")); ECPoint ecPointQ = new ECPoint(qx, qy); ECPoint ecPointP = new ECPoint(px, py); ECPoint receivedEcPointQP = CryptoUtil.addPoint(domParamsEcdh.getCurve(), ecPointQ, ecPointP); ECPoint receivedEcPointPQ = CryptoUtil.addPoint(domParamsEcdh.getCurve(), ecPointP, ecPointQ); assertEquals("add x", receivedEcPointQP.getAffineX(), receivedEcPointPQ.getAffineX()); assertEquals("add y", receivedEcPointQP.getAffineY(), receivedEcPointPQ.getAffineY()); } /** * Positive test case: double a single EC point. */ @Test public void testDoublePoint() { DomainParameterSetEcdh domParamsEcdh = (DomainParameterSetEcdh) StandardizedDomainParameters.getDomainParameterSetById(13); // values originate from successful PACE test run and are checked against the implementation of point doubling by Bouncy Castle BigInteger px = new BigInteger(1, HexString.toByteArray("3EB50DD69CA2E6B0BE8D4C3089DD55F1657273CFC5728012CA346BAE0AF9A7D8")); BigInteger py = new BigInteger(1, HexString.toByteArray("829F38EB7E87D468BD9A63CEE4CB15DA25D6EAFE1008FD889D3D6B0F5FB04C02")); BigInteger expectedX = new BigInteger(1, HexString.toByteArray("1E2F557EC531A3E47859A238F8CCDB3F646FC6533A02F05359B2200C41BC7F79")); BigInteger expectedY = new BigInteger(1, HexString.toByteArray("754D80BA26DD2B844F2C02D26CD693A9F28BBB0B318493C3EB84A20086BFD2B0")); ECPoint ecPointP = new ECPoint(px, py); ECPoint expectedEcPoint = new ECPoint(expectedX, expectedY); ECPoint receivedEcPoint = CryptoUtil.doublePoint(domParamsEcdh.getCurve(), ecPointP); assertEquals("double x", expectedEcPoint.getAffineX(), receivedEcPoint.getAffineX()); assertEquals("double y", expectedEcPoint.getAffineY(), receivedEcPoint.getAffineY()); } /** * Positive test case: encode point with same length coordinates. */ @Test public void testEncode() { String xStr = "CA2E6B0BE8D4C39A"; String yStr = "DC6BD8DF35C331D7"; int refLength = 8; byte[] exp = HexString.toByteArray("04" + xStr + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED)); } /** * Positive test case: encode point with shorter x coordinate. */ @Test public void testEncode_shortX() { String xStr = "2E6B0BE8D4C39A"; String yStr = "DC6BD8DF35C331D7"; int refLength = 8; byte[] exp = HexString.toByteArray("0400" + xStr + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED)); } /** * Positive test case: encode point with shorter y coordinate. */ @Test public void testEncode_shortY() { String xStr = "CA2E6B0BE8D4C39A"; String yStr = "D8DF35C331D7"; int refLength = 8; byte[] exp = HexString.toByteArray("04" + xStr + "0000" + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED)); } /** * Positive test case: encode point with shorter x and y coordinates. */ @Test public void testEncode_shortXY() { String xStr = "2E6B0BE8D4C39A"; String yStr = "6BD8DF35C331D7"; int refLength = 8; byte[] exp = HexString.toByteArray("0400" + xStr + "00" + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED)); } /** * Negative test case: x coordinate larger than reference length. */ @Test(expected = IllegalArgumentException.class) public void testEncode_largeX() { String xStr = "ADCA2E6B0BE8D4C39A"; String yStr = "DC6BD8DF35C331D7"; int refLength = 8; BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED); } /** * Negative test case: y coordinate larger than reference length. */ @Test(expected = IllegalArgumentException.class) public void testEncode_largeY() { String xStr = "CA2E6B0BE8D4C39A"; String yStr = "ADDC6BD8DF35C331D7"; int refLength = 8; BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_UNCOMPRESSED); } /** * Positive test case: perform compressed point encoding for y-coordinate's LSB=1. */ @Test public void testEncode_Compressed1() { String xStr = "42"; String yStr = "43"; int refLength = 1; byte[] exp = HexString.toByteArray("03" + xStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_COMPRESSED)); } /** * Positive test case: perform compressed point encoding for y-coordinate's LSB=0. */ @Test public void testEncode_Compressed0() { String xStr = "42"; String yStr = "42"; int refLength = 1; byte[] exp = HexString.toByteArray("02" + xStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_COMPRESSED)); } /** * Positive test case: perform hybrid point encoding for y-coordinate's LSB=1. */ @Test public void testEncode_Hybrid1() { String xStr = "42"; String yStr = "43"; int refLength = 1; byte[] exp = HexString.toByteArray("07" + xStr + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_HYBRID)); } /** * Positive test case: perform hybrid point encoding for y-coordinate's LSB=0. */ @Test public void testEncode_Hybrid0() { String xStr = "42"; String yStr = "42"; int refLength = 1; byte[] exp = HexString.toByteArray("06" + xStr + yStr); BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); assertArrayEquals(exp, CryptoUtil.encode(ecPoint, refLength, CryptoUtil.ENCODING_HYBRID)); } /** * Negative test case: unknown/illegal encoding type. */ @Test(expected = IllegalArgumentException.class) public void testEncode_IllegalEncoding() { String xStr = "42"; String yStr = "42"; int refLength = 1; BigInteger x = new BigInteger(1, HexString.toByteArray(xStr)); BigInteger y = new BigInteger(1, HexString.toByteArray(yStr)); ECPoint ecPoint = new ECPoint(x, y); CryptoUtil.encode(ecPoint, refLength, Byte.MAX_VALUE); } /** * Positive test case: encode a raw representation of a valid signature into * its ASN.1 representation */ @Test public void testRestoreAsn1SignatureStructure() { byte[] signature = HexString .toByteArray("24B41D25993A96A9CEB75CFB6ACAB615DE0A6124CBE41779B8ECDD804B8A7DB70A09DBFFC745865253BCAEFBBC228AB70C253B1B7E9CD92AB25EA934A8257E1F"); byte[] expectedResult = HexString .toByteArray("3044022024B41D25993A96A9CEB75CFB6ACAB615DE0A6124CBE41779B8ECDD804B8A7DB702200A09DBFFC745865253BCAEFBBC228AB70C253B1B7E9CD92AB25EA934A8257E1F"); assertArrayEquals(expectedResult, CryptoUtil .restoreAsn1SignatureStructure(signature).toByteArray()); } /** * Positive test case: encode a raw representation of a signature consisting * of negative numbers to its ASN.1 representation */ @Test public void testRestoreAsn1SignatureStructureRSAreNegative() { byte[] testData = new BigInteger("-20000000000000", 10).toByteArray(); byte[] signature = Utils.concatByteArrays(testData, testData); byte[] expectedResult = Utils.concatByteArrays( new byte []{Asn1.SEQUENCE}, Utils.toUnsignedByteArray((byte) ((testData.length + 3) * 2)), new byte []{Asn1.INTEGER}, Utils.toUnsignedByteArray((byte) (testData.length + 1)), HexString.toByteArray("00"), testData, new byte []{Asn1.INTEGER}, Utils.toUnsignedByteArray((byte) (testData.length + 1)), HexString.toByteArray("00"), testData); assertArrayEquals(expectedResult, CryptoUtil .restoreAsn1SignatureStructure(signature).toByteArray()); } /** * Positive test case: recreate key pair from minimum representation of * public and private parts. */ @Test public void testKeyConversion() { byte[] pubKeyBytes = HexString.toByteArray("047D1EA24146C3ADAC11143E7267B4E3EC572534828DB54904877B8D6EFDC5C13123A9E955890447643735C4F0AB9093FAA0C96DEFA1CE9079DA0B3C43BE6A0255"); byte[] privKeyBytes = HexString.toByteArray("1183F16814B3947D01DAED7F8D236769F5ABD8020FFF53C5E5FE86A8ABAB02D2"); KeyPair keyPair = CryptoUtil.reconstructKeyPair(13, pubKeyBytes, privKeyBytes); assertTrue(keyPair != null); } /** * Positive test case: get cipher name as string from string containing delimiters */ @Test public void testGetCipherNameAsString_withDelimiters() { String input = "DESede/CBC/NoPadding"; String expected = "DESede"; String received = CryptoUtil.getCipherNameAsString(input); assertEquals(expected, received); } /** * Positive test case: get cipher name as string from string not containing any delimiters */ @Test public void testGetCipherNameAsString_withoutDelimiters() { String input = "DESede"; String expected = "DESede"; String received = CryptoUtil.getCipherNameAsString(input); assertEquals(expected, received); } /** * Positive test case: get cipher name as string from empty string */ @Test public void testGetCipherNameAsString_empty() { String input = ""; String expected = ""; String received = CryptoUtil.getCipherNameAsString(input); assertEquals(expected, received); } /** * Positive test case: get cipher name as string from string starting with delimiter */ @Test public void testGetCipherNameAsString_delimiterFirst() { String input = "/"; String expected = ""; String received = CryptoUtil.getCipherNameAsString(input); assertEquals(expected, received); } }