diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-10 16:14:17 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-05-10 16:14:17 +0000 |
commit | 80bd5c441a57372e451f4e661a42b0c8c4716767 (patch) | |
tree | 8c736dde0f6155d7625056b1a639dd12ea72e60d | |
parent | 95c2700dc12b3b7c8128de3a0472e51297dca74b (diff) | |
parent | 7669d1ff48ad30ebabf9555ddf40663180b9cdd9 (diff) | |
download | bouncycastle-busytown-mac-infra-release.tar.gz |
Merge "Snap for 11819167 from 716439e2b6aae37d9a5285e80764580916a9737f to busytown-mac-infra-release" into busytown-mac-infra-releasebusytown-mac-infra-release
39 files changed, 4996 insertions, 139 deletions
@@ -51,38 +51,23 @@ java_defaults { errorprone: { javacflags: [ "-Xep:MissingOverride:OFF", // Ignore missing @Override. + "-Xep:BoxedPrimitiveEquality:WARN", + "-Xep:DoubleBraceInitialization:WARN", + "-Xep:HashtableContains:WARN", ], }, } -// These cannot build in the PDK, because the PDK requires all libraries -// compile against SDK versions. java_defaults { name: "bouncycastle-defaults", defaults: [ "bouncycastle-errorprone-defaults", ], hostdex: true, - target: { - android: { - product_variables: { - pdk: { - enabled: false, - }, - }, - }, - }, lint: { warning_checks: ["SuspiciousIndentation"], }, - errorprone: { - javacflags: [ - "-Xep:BoxedPrimitiveEquality:WARN", - "-Xep:DoubleBraceInitialization:WARN", - "-Xep:HashtableContains:WARN", - ], - }, } // The src files for bouncycastle, used to generate core platform / intra-core @@ -104,7 +89,6 @@ java_library { visibility: [ "//art/build/apex", "//art/build/sdk", - "//external/wycheproof", "//libcore:__subpackages__", "//packages/modules/ArtPrebuilt", ], @@ -120,6 +104,34 @@ java_library { libs: ["unsupportedappusage"], + optimize: { + enabled: true, + shrink: true, + optimize: true, + obfuscate: false, + proguard_compatibility: false, + ignore_warnings: false, + proguard_flags_files: ["proguard.flags"], + }, + + sdk_version: "none", + system_modules: "art-module-intra-core-api-stubs-system-modules", +} + +java_library_static { + name: "bouncycastle-test-lib", + visibility: [ + "//packages/modules/ArtPrebuilt", + "//external/bouncycastle/mts", + ], + srcs: [ + "repackaged/bcprov/src/test/java/**/*.java", + ], + libs: ["bouncycastle"], + static_libs: [ + "junit", + "platform-test-annotations", + ], sdk_version: "none", system_modules: "art-module-intra-core-api-stubs-system-modules", } @@ -174,11 +186,13 @@ unbundled_visibility = [ "//packages/apps/RemoteProvisioner/tests/unittests", "//packages/modules/Connectivity/tests/cts/net", "//packages/modules/RemoteKeyProvisioning/app/tests/unit", + "//packages/modules/Virtualization/service_vm/test_apk", "//packages/modules/Wifi/service", "//packages/modules/Wifi/service/tests/wifitests", "//libcore", "//system/extras/verity", "//system/security/identity/util", + "//tools/apksig", "//tools/security/remote_provisioning/attestation_testing", "//vendor:__subpackages__", ] @@ -199,6 +213,7 @@ java_library { sdk_version: "core_current", min_sdk_version: "30", java_version: "1.7", + apex_available: ["com.android.wifi"], } // Bouncycastle PKIX classes in the original org.bouncycastle package for use @@ -247,6 +262,22 @@ java_library_host { static_libs: ["bouncycastle-unbundled"], } +java_test_host { + name: "bouncycastle-host-tests", + srcs: [ + "bcprov/src/test/java/**/*.java", + ], + static_libs: [ + "bouncycastle-unbundled", + "junit", + "platform-test-annotations", + ], + test_options: { + unit_test: true, + }, + test_suites: ["general-tests"], +} + // Bouncycastle subset for use by frameworks/opt/net/ike project. // // Avoids including the whole of bouncycastle_unbundled in ike. @@ -307,6 +338,7 @@ java_library { //Excludes directories not needed. java_library { name: "bouncycastle-uwb", + defaults: ["bouncycastle-errorprone-defaults"], visibility: [ "//packages/modules/Uwb/service", ], diff --git a/README.android b/README.android index 2b6c07f2..da805c03 100644 --- a/README.android +++ b/README.android @@ -64,11 +64,16 @@ The following steps are recommended for porting new Bouncy Castle versions. * If upstream added a file to a directory we deleted, we probably don't need it - d) Confirm all changes + d) Update the list of exported APIs in proguard.flags, if necessary. + + Check this in particular if new algorithms are getting registered with + ConfigurableProvider.addAlgorithm or ConfigurableProvider.addPrivateAlgorithm. + + e) Confirm all changes git diff aosp/master - e) Run the tests, commonly at least + f) Run the tests, commonly at least cts -m CtsLibcoreTestCases cts -m CtsLibcoreFileIOTestCases @@ -77,6 +82,6 @@ The following steps are recommended for porting new Bouncy Castle versions. cts -m CtsLibcoreOkHttpTestCases cts -m CtsLibcoreWycheproofBCTestCases - e) Get the change reviewed + g) Get the change reviewed repo upload . diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 00000000..5a9d38a3 --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "MtsLibcoreBouncyCastleTestCases", + "options": [ + { + "instrumentation-arg": "core-test-mode:=presubmit" + } + ] + } + ] +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 8c678059..7dadfa7f 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -957,56 +957,26 @@ public class BaseBlockCipher { byte[] iv = new byte[ivLength]; - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // ivRandom.nextBytes(iv); + // rejected outright in BC 1.54 (even if an IV was passed in params). + // See b/27995180 for historical details. if (!isBCPBEKeyWithoutIV(key)) { ivRandom.nextBytes(iv); } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // END Android-changed: Reject PBE keys with no IV param = new ParametersWithIV(param, iv); ivParam = (ParametersWithIV)param; } else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) { - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // throw new InvalidAlgorithmParameterException("no IV set when one expected"); - if (!isBCPBEKeyWithoutIV(key)) { - throw new InvalidAlgorithmParameterException("no IV set when one expected"); - } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); - // Mimic behaviour in 1.52 by using an IV of 0's - param = new ParametersWithIV(param, new byte[ivLength]); - ivParam = (ParametersWithIV)param; - } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // rejected outright in BC 1.54 (even if an IV was passed in params). + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); + // END Android-changed: Reject PBE keys with no IV } } diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java new file mode 100644 index 00000000..b59f1924 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java @@ -0,0 +1,175 @@ +package org.bouncycastle.math.ec.custom.sec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat256; + +import junit.framework.TestCase; + +public class SecP256R1FieldTest extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final X9ECParameters DP = CustomNamedCurves + .getByOID(SECObjectIdentifiers.secp256r1); + private static final BigInteger Q = DP.getCurve().getField().getCharacteristic(); + + public void testMultiply1() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + ECFieldElement y = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testMultiply2() + { + int COUNT = 100; + ECFieldElement[] inputs = new ECFieldElement[COUNT]; + BigInteger[] INPUTS = new BigInteger[COUNT]; + + for (int i = 0; i < inputs.length; ++i) + { + inputs[i] = generateMultiplyInput_Random(); + INPUTS[i] = inputs[i].toBigInteger(); + } + + for (int j = 0; j < inputs.length; ++j) + { + for (int k = 0; k < inputs.length; ++k) + { + BigInteger R = INPUTS[j].multiply(INPUTS[k]).mod(Q); + + ECFieldElement z = inputs[j].multiply(inputs[k]); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + } + + public void testSquare() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /** + * Test multiplication with specifically selected values that triggered a bug in the modular + * reduction in OpenSSL (last affected version 0.9.8g). + * + * See "Practical realisation and elimination of an ECC-related software bug attack", B. B. + * Brumley, M. Barbarosa, D. Page, F. Vercauteren. + */ + public void testMultiply_OpenSSLBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInputA_OpenSSLBug(); + ECFieldElement y = generateMultiplyInputB_OpenSSLBug(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /** + * Test squaring with specifically selected values that triggered a bug in the modular reduction + * in OpenSSL (last affected version 0.9.8g). + * + * See "Practical realisation and elimination of an ECC-related software bug attack", B. B. + * Brumley, M. Barbarosa, D. Page, F. Vercauteren. + */ + public void testSquare_OpenSSLBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateSquareInput_OpenSSLBug(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + private ECFieldElement fe(BigInteger x) + { + return DP.getCurve().fromBigInteger(x); + } + + private ECFieldElement generateMultiplyInput_Random() + { + return fe(new BigInteger(DP.getCurve().getFieldSize() + 32, RANDOM).mod(Q)); + } + + private ECFieldElement generateMultiplyInputA_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[4] = 3; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } + + private ECFieldElement generateMultiplyInputB_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[3] = 1; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } + + private ECFieldElement generateSquareInput_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[4] = 2; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java new file mode 100644 index 00000000..be6f5d19 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java @@ -0,0 +1,140 @@ +package org.bouncycastle.math.ec.custom.sec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat; + +import junit.framework.TestCase; + +public class SecP384R1FieldTest extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final X9ECParameters DP = CustomNamedCurves + .getByOID(SECObjectIdentifiers.secp384r1); + private static final BigInteger Q = DP.getCurve().getField().getCharacteristic(); + + public void testMultiply1() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + ECFieldElement y = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testMultiply2() + { + int COUNT = 100; + ECFieldElement[] inputs = new ECFieldElement[COUNT]; + BigInteger[] INPUTS = new BigInteger[COUNT]; + + for (int i = 0; i < inputs.length; ++i) + { + inputs[i] = generateMultiplyInput_Random(); + INPUTS[i] = inputs[i].toBigInteger(); + } + + for (int j = 0; j < inputs.length; ++j) + { + for (int k = 0; k < inputs.length; ++k) + { + BigInteger R = INPUTS[j].multiply(INPUTS[k]).mod(Q); + + ECFieldElement z = inputs[j].multiply(inputs[k]); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + } + + public void testSquare() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testSquare_CarryBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateSquareInput_CarryBug(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /* + * Based on another example input demonstrating the carry propagation bug in Nat192.square, as + * reported by Joseph Friel on dev-crypto. + */ + public void testSquare_CarryBug_Reported() + { + ECFieldElement x = fe(new BigInteger("2fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", 16)); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + + private ECFieldElement fe(BigInteger x) + { + return DP.getCurve().fromBigInteger(x); + } + + private ECFieldElement generateMultiplyInput_Random() + { + return fe(new BigInteger(DP.getCurve().getFieldSize() + 32, RANDOM).mod(Q)); + } + + private ECFieldElement generateSquareInput_CarryBug() + { + int[] x = Nat.create(12); + x[0] = RANDOM.nextInt() >>> 1; + x[6] = 2; + x[10] = -1 << 16; + x[11] = -1; + + return fe(Nat.toBigInteger(12, x)); + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/AllTests.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/AllTests.java new file mode 100644 index 00000000..7d5751c7 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/AllTests.java @@ -0,0 +1,65 @@ +package org.bouncycastle.math.ec.test; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.test.PrintTestResult; + +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("EC Math tests"); + + // Android-changed: parameterized the test. + // suite.addTestSuite(ECAlgorithmsTest.class); + suite.addTestSuite(ECPointTest.class); + suite.addTestSuite(FixedPointTest.class); + + return new BCTestSetup(suite); + } + + static List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java new file mode 100644 index 00000000..0c43b5a1 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java @@ -0,0 +1,239 @@ +package org.bouncycastle.math.ec.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECPoint; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +// Android-changed: parameterized the test. +@RunWith(Parameterized.class) +public class ECAlgorithmsTest +{ + private static final int SCALE = 4; + private static final SecureRandom RND = new SecureRandom(); + + + // BEGIN Android-added: parameterized the test. + @Parameterized.Parameters(name = "{0}") + public static String[] getAllX9ECParameters() { + Set<String> names = new HashSet<>(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + return names.toArray(new String[0]); + } + + @Parameterized.Parameter(0) + public String name; + + private ArrayList getX9s(String name) { + ArrayList<X9ECParameters> x9s = new ArrayList<>(); + + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + + x9 = CustomNamedCurves.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + return x9s; + } + // END Android-added: parameterized the test. + + @Ignore("secp256r1 is covered by testSumOfMultipliesComplete") + public void testSumOfMultiplies() + { + X9ECParameters x9 = CustomNamedCurves.getByName("secp256r1"); + assertNotNull(x9); + doTestSumOfMultiplies(x9); + } + + // TODO Ideally, mark this test not to run by default + @Test + public void testSumOfMultipliesComplete() + { + // Android-changed: parameterized the test. + // ArrayList x9s = getTestCurves(); + ArrayList<X9ECParameters> x9s = getX9s(name); + Iterator it = x9s.iterator(); + while (it.hasNext()) + { + X9ECParameters x9 = (X9ECParameters)it.next(); + doTestSumOfMultiplies(x9); + } + } + + @Ignore("secp256r1 is covered by testSumOfTwoMultipliesComplete") + public void testSumOfTwoMultiplies() + { + X9ECParameters x9 = CustomNamedCurves.getByName("secp256r1"); + assertNotNull(x9); + doTestSumOfTwoMultiplies(x9); + } + + // TODO Ideally, mark this test not to run by default + @Test + public void testSumOfTwoMultipliesComplete() + { + // Android-changed: parameterized the test. + // ArrayList x9s = getTestCurves(); + ArrayList<X9ECParameters> x9s = getX9s(name); + Iterator it = x9s.iterator(); + while (it.hasNext()) + { + X9ECParameters x9 = (X9ECParameters)it.next(); + doTestSumOfTwoMultiplies(x9); + } + } + + private void doTestSumOfMultiplies(X9ECParameters x9) + { + ECPoint[] points = new ECPoint[SCALE]; + BigInteger[] scalars = new BigInteger[SCALE]; + for (int i = 0; i < SCALE; ++i) + { + points[i] = getRandomPoint(x9); + scalars[i] = getRandomScalar(x9); + } + + ECPoint u = x9.getCurve().getInfinity(); + for (int i = 0; i < SCALE; ++i) + { + u = u.add(points[i].multiply(scalars[i])); + + ECPoint v = ECAlgorithms.sumOfMultiplies(copyPoints(points, i + 1), copyScalars(scalars, i + 1)); + + ECPoint[] results = new ECPoint[]{ u, v }; + x9.getCurve().normalizeAll(results); + + assertPointsEqual("ECAlgorithms.sumOfMultiplies is incorrect", results[0], results[1]); + } + } + + private void doTestSumOfTwoMultiplies(X9ECParameters x9) + { + ECPoint p = getRandomPoint(x9); + BigInteger a = getRandomScalar(x9); + + for (int i = 0; i < SCALE; ++i) + { + ECPoint q = getRandomPoint(x9); + BigInteger b = getRandomScalar(x9); + + ECPoint u = p.multiply(a).add(q.multiply(b)); + ECPoint v = ECAlgorithms.shamirsTrick(p, a, q, b); + ECPoint w = ECAlgorithms.sumOfTwoMultiplies(p, a, q, b); + + ECPoint[] results = new ECPoint[]{ u, v, w }; + x9.getCurve().normalizeAll(results); + + assertPointsEqual("ECAlgorithms.shamirsTrick is incorrect", results[0], results[1]); + assertPointsEqual("ECAlgorithms.sumOfTwoMultiplies is incorrect", results[0], results[2]); + + p = q; + a = b; + } + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + assertEquals(message, a, b); + } + + private ECPoint[] copyPoints(ECPoint[] ps, int len) + { + ECPoint[] result = new ECPoint[len]; + System.arraycopy(ps, 0, result, 0, len); + return result; + } + + private BigInteger[] copyScalars(BigInteger[] ks, int len) + { + BigInteger[] result = new BigInteger[len]; + System.arraycopy(ks, 0, result, 0, len); + return result; + } + + private ECPoint getRandomPoint(X9ECParameters x9) + { + return x9.getG().multiply(getRandomScalar(x9)); + } + + private BigInteger getRandomScalar(X9ECParameters x9) + { + return new BigInteger(x9.getN().bitLength(), RND); + } + + private ArrayList getTestCurves() + { + ArrayList x9s = new ArrayList(); + Set names = new HashSet(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + + x9 = CustomNamedCurves.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + } + return x9s; + } + + private void addTestCurves(ArrayList x9s, X9ECParameters x9) + { + ECCurve curve = x9.getCurve(); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (curve.getCoordinateSystem() == coord) + { + x9s.add(x9); + } + else if (curve.supportsCoordinateSystem(coord)) + { + ECCurve c = curve.configure().setCoordinateSystem(coord).create(); + x9s.add(new X9ECParameters(c, new X9ECPoint(c.importPoint(x9.getG()), false), x9.getN(), x9.getH())); + } + } + } + + // Android-changed: Use JUnit4. + /* + public static Test suite() + { + return new TestSuite(ECAlgorithmsTest.class); + } + */ +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java new file mode 100644 index 00000000..65cf8faa --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java @@ -0,0 +1,202 @@ +package org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import junit.framework.TestCase; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Times; + +/** + * Compares the performance of the the window NAF point multiplication against conventional point + * multiplication. + */ +public class ECPointPerformanceTest extends TestCase +{ + static final int MILLIS_PER_ROUND = 200; + static final int MILLIS_WARMUP = 1000; + + static final int MULTS_PER_CHECK = 16; + static final int NUM_ROUNDS = 10; + + private static String[] COORD_NAMES = new String[]{ "AFFINE", "HOMOGENEOUS", "JACOBIAN", "JACOBIAN-CHUDNOVSKY", + "JACOBIAN-MODIFIED", "LAMBDA-AFFINE", "LAMBDA-PROJECTIVE", "SKEWED" }; + + private void randMult(String curveName) throws Exception + { + X9ECParameters spec = ECNamedCurveTable.getByName(curveName); + if (spec != null) + { + randMult(curveName, spec); + } + + spec = CustomNamedCurves.getByName(curveName); + if (spec != null) + { + randMult(curveName + " (custom)", spec); + } + } + + private void randMult(String label, X9ECParameters spec) throws Exception + { + ECCurve C = spec.getCurve(); + ECPoint G = (ECPoint)spec.getG(); + BigInteger n = spec.getN(); + + SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); + random.setSeed(System.currentTimeMillis()); + + System.out.println(label); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (C.supportsCoordinateSystem(coord)) + { + ECCurve c = C; + ECPoint g = G; + + boolean defaultCoord = (c.getCoordinateSystem() == coord); + if (!defaultCoord) + { + c = C.configure().setCoordinateSystem(coord).create(); + g = c.importPoint(G); + } + + double avgRate = randMult(random, g, n); + String coordName = COORD_NAMES[coord]; + StringBuffer sb = new StringBuffer(); + sb.append(" "); + sb.append(defaultCoord ? '*' : ' '); + sb.append(coordName); + for (int j = sb.length(); j < 30; ++j) + { + sb.append(' '); + } + sb.append(": "); + sb.append(avgRate); + sb.append(" mults/sec"); + for (int j = sb.length(); j < 64; ++j) + { + sb.append(' '); + } + sb.append('('); + sb.append(1000.0 / avgRate); + sb.append(" millis/mult)"); + System.out.println(sb.toString()); + } + } + } + + private double randMult(SecureRandom random, ECPoint g, BigInteger n) throws Exception + { + BigInteger[] ks = new BigInteger[128]; + for (int i = 0; i < ks.length; ++i) + { + ks[i] = new BigInteger(n.bitLength() - 1, random); + } + + int ki = 0; + ECPoint p = g; + + { + long startTime = Times.nanoTime(); + long goalTime = startTime + 1000000L * MILLIS_WARMUP; + + do + { + BigInteger k = ks[ki]; + p = g.multiply(k); + if ((ki & 1) != 0) + { + g = p; + } + if (++ki == ks.length) + { + ki = 0; + } + } + while (Times.nanoTime() < goalTime); + } + + double minRate = Double.MAX_VALUE, maxRate = Double.MIN_VALUE, totalRate = 0.0; + + for (int i = 1; i <= NUM_ROUNDS; i++) + { + long startTime = Times.nanoTime(); + long goalTime = startTime + 1000000L * MILLIS_PER_ROUND; + long count = 0, endTime; + + do + { + ++count; + + for (int j = 0; j < MULTS_PER_CHECK; ++j) + { + BigInteger k = ks[ki]; + p = g.multiply(k); + if ((ki & 1) != 0) + { + g = p; + } + if (++ki == ks.length) + { + ki = 0; + } + } + + endTime = Times.nanoTime(); + } + while (endTime < goalTime); + + double roundElapsed = (double)(endTime - startTime); + double roundRate = count * MULTS_PER_CHECK * 1000000000L / roundElapsed; + + minRate = Math.min(minRate, roundRate); + maxRate = Math.max(maxRate, roundRate); + totalRate += roundRate; + } + + return (totalRate - minRate - maxRate) / (NUM_ROUNDS - 2); + } + + public void testMultiply() throws Exception + { + // Android-changed: Skip this test as this perf test is timed out. + if (true) { + return; + } + SortedSet names = new TreeSet(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + + Set oids = new HashSet(); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name); + if (oid == null) + { + oid = CustomNamedCurves.getOID(name); + } + if (oid != null && !oids.add(oid)) + { + continue; + } + + randMult(name); + } + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java new file mode 100644 index 00000000..aaf2f5b6 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/ECPointTest.java @@ -0,0 +1,740 @@ +package org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECPoint; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WNafUtil; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.encoders.Hex; + +import android.platform.test.annotations.LargeTest; + +/** + * Test class for {@link org.bouncycastle.math.ec.ECPoint ECPoint}. All + * literature values are taken from "Guide to elliptic curve cryptography", + * Darrel Hankerson, Alfred J. Menezes, Scott Vanstone, 2004, Springer-Verlag + * New York, Inc. + */ +public class ECPointTest extends TestCase +{ + /** + * Random source used to generate random points + */ + private SecureRandom secRand = new SecureRandom(); + + private ECPointTest.Fp fp = null; + + private ECPointTest.F2m f2m = null; + + /** + * Nested class containing sample literature values for <code>Fp</code>. + */ + public static class Fp + { + private final BigInteger q = new BigInteger("1063"); + + private final BigInteger a = new BigInteger("4"); + + private final BigInteger b = new BigInteger("20"); + + private final BigInteger n = new BigInteger("38"); + + private final BigInteger h = new BigInteger("1"); + + private final ECCurve curve = new ECCurve.Fp(q, a, b, n, h); + + private final ECPoint infinity = curve.getInfinity(); + + private final int[] pointSource = { 1, 5, 4, 10, 234, 1024, 817, 912 }; + + private ECPoint[] p = new ECPoint[pointSource.length / 2]; + + /** + * Creates the points on the curve with literature values. + */ + private void createPoints() + { + for (int i = 0; i < pointSource.length / 2; i++) + { + p[i] = curve.createPoint( + new BigInteger(Integer.toString(pointSource[2 * i])), + new BigInteger(Integer.toString(pointSource[2 * i + 1]))); + } + } + } + + /** + * Nested class containing sample literature values for <code>F2m</code>. + */ + public static class F2m + { + // Irreducible polynomial for TPB z^4 + z + 1 + private final int m = 4; + + private final int k1 = 1; + + // a = z^3 + private final BigInteger aTpb = new BigInteger("1000", 2); + + // b = z^3 + 1 + private final BigInteger bTpb = new BigInteger("1001", 2); + + private final BigInteger n = new BigInteger("23"); + + private final BigInteger h = new BigInteger("1"); + + private final ECCurve.F2m curve = new ECCurve.F2m(m, k1, aTpb, bTpb, n, h); + + private final ECPoint.F2m infinity = (ECPoint.F2m) curve.getInfinity(); + + private final String[] pointSource = { "0010", "1111", "1100", "1100", + "0001", "0001", "1011", "0010" }; + + private ECPoint[] p = new ECPoint[pointSource.length / 2]; + + /** + * Creates the points on the curve with literature values. + */ + private void createPoints() + { + for (int i = 0; i < pointSource.length / 2; i++) + { + p[i] = curve.createPoint( + new BigInteger(pointSource[2 * i], 2), + new BigInteger(pointSource[2 * i + 1], 2)); + } + } + } + + public void setUp() + { + fp = new ECPointTest.Fp(); + fp.createPoints(); + + f2m = new ECPointTest.F2m(); + f2m.createPoints(); + } + + /** + * Tests, if inconsistent points can be created, i.e. points with exactly + * one null coordinate (not permitted). + */ + public void testPointCreationConsistency() + { + try + { + ECPoint bad = fp.curve.createPoint(new BigInteger("12"), null); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = fp.curve.createPoint(null, new BigInteger("12")); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = f2m.curve.createPoint(new BigInteger("1011"), null); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = f2m.curve.createPoint(null, new BigInteger("1011")); + fail(); + } + catch (IllegalArgumentException expected) + { + } + } + + /** + * Tests <code>ECPoint.add()</code> against literature values. + * + * @param p + * The array of literature values. + * @param infinity + * The point at infinity on the respective curve. + */ + private void implTestAdd(ECPoint[] p, ECPoint infinity) + { + assertPointsEqual("p0 plus p1 does not equal p2", p[2], p[0].add(p[1])); + assertPointsEqual("p1 plus p0 does not equal p2", p[2], p[1].add(p[0])); + for (int i = 0; i < p.length; i++) + { + assertPointsEqual("Adding infinity failed", p[i], p[i].add(infinity)); + assertPointsEqual("Adding to infinity failed", p[i], infinity.add(p[i])); + } + } + + /** + * Calls <code>implTestAdd()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testAdd() + { + implTestAdd(fp.p, fp.infinity); + implTestAdd(f2m.p, f2m.infinity); + } + + /** + * Tests <code>ECPoint.twice()</code> against literature values. + * + * @param p + * The array of literature values. + */ + private void implTestTwice(ECPoint[] p) + { + assertPointsEqual("Twice incorrect", p[3], p[0].twice()); + assertPointsEqual("Add same point incorrect", p[3], p[0].add(p[0])); + } + + /** + * Calls <code>implTestTwice()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testTwice() + { + implTestTwice(fp.p); + implTestTwice(f2m.p); + } + + private void implTestThreeTimes(ECPoint[] p) + { + ECPoint P = p[0]; + ECPoint _3P = P.add(P).add(P); + assertPointsEqual("ThreeTimes incorrect", _3P, P.threeTimes()); + assertPointsEqual("TwicePlus incorrect", _3P, P.twicePlus(P)); + } + + /** + * Calls <code>implTestThreeTimes()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testThreeTimes() + { + implTestThreeTimes(fp.p); + implTestThreeTimes(f2m.p); + } + + /** + * Goes through all points on an elliptic curve and checks, if adding a + * point <code>k</code>-times is the same as multiplying the point by + * <code>k</code>, for all <code>k</code>. Should be called for points + * on very small elliptic curves only. + * + * @param p + * The base point on the elliptic curve. + * @param infinity + * The point at infinity on the elliptic curve. + */ + private void implTestAllPoints(ECPoint p, ECPoint infinity) + { + ECPoint adder = infinity; + ECPoint multiplier = infinity; + + BigInteger i = BigInteger.valueOf(1); + do + { + adder = adder.add(p); + multiplier = p.multiply(i); + assertPointsEqual("Results of add() and multiply() are inconsistent " + + i, adder, multiplier); + i = i.add(BigInteger.ONE); + } + while (!(adder.equals(infinity))); + } + + /** + * Calls <code>implTestAllPoints()</code> for the small literature curves, + * both for <code>Fp</code> and <code>F2m</code>. + */ + public void testAllPoints() + { + for (int i = 0; i < fp.p.length; i++) + { + implTestAllPoints(fp.p[i], fp.infinity); + } + + for (int i = 0; i < f2m.p.length; i++) + { + implTestAllPoints(f2m.p[i], f2m.infinity); + } + } + + /** + * Checks, if the point multiplication algorithm of the given point yields + * the same result as point multiplication done by the reference + * implementation given in <code>multiply()</code>. This method chooses a + * random number by which the given point <code>p</code> is multiplied. + * + * @param p + * The point to be multiplied. + * @param numBits + * The bitlength of the random number by which <code>p</code> + * is multiplied. + */ + private void implTestMultiply(ECPoint p, int numBits) + { + BigInteger k = new BigInteger(numBits, secRand); + ECPoint ref = ECAlgorithms.referenceMultiply(p, k); + ECPoint q = p.multiply(k); + assertPointsEqual("ECPoint.multiply is incorrect", ref, q); + } + + /** + * Checks, if the point multiplication algorithm of the given point yields + * the same result as point multiplication done by the reference + * implementation given in <code>multiply()</code>. This method tests + * multiplication of <code>p</code> by every number of bitlength + * <code>numBits</code> or less. + * + * @param p + * The point to be multiplied. + * @param numBits + * Try every multiplier up to this bitlength + */ + private void implTestMultiplyAll(ECPoint p, int numBits) + { + BigInteger bound = BigInteger.ONE.shiftLeft(numBits); + BigInteger k = BigInteger.ZERO; + + do + { + ECPoint ref = ECAlgorithms.referenceMultiply(p, k); + ECPoint q = p.multiply(k); + assertPointsEqual("ECPoint.multiply is incorrect", ref, q); + k = k.add(BigInteger.ONE); + } + while (k.compareTo(bound) < 0); + } + + /** + * Tests <code>ECPoint.add()</code> and <code>ECPoint.subtract()</code> + * for the given point and the given point at infinity. + * + * @param p + * The point on which the tests are performed. + * @param infinity + * The point at infinity on the same curve as <code>p</code>. + */ + private void implTestAddSubtract(ECPoint p, ECPoint infinity) + { + assertPointsEqual("Twice and Add inconsistent", p.twice(), p.add(p)); + assertPointsEqual("Twice p - p is not p", p, p.twice().subtract(p)); + assertPointsEqual("TwicePlus(p, -p) is not p", p, p.twicePlus(p.negate())); + assertPointsEqual("p - p is not infinity", infinity, p.subtract(p)); + assertPointsEqual("p plus infinity is not p", p, p.add(infinity)); + assertPointsEqual("infinity plus p is not p", p, infinity.add(p)); + assertPointsEqual("infinity plus infinity is not infinity ", infinity, infinity.add(infinity)); + assertPointsEqual("Twice infinity is not infinity ", infinity, infinity.twice()); + } + + /** + * Calls <code>implTestAddSubtract()</code> for literature values, both + * for <code>Fp</code> and <code>F2m</code>. + */ + public void testAddSubtractMultiplySimple() + { + int fpBits = fp.curve.getOrder().bitLength(); + for (int iFp = 0; iFp < fp.pointSource.length / 2; iFp++) + { + implTestAddSubtract(fp.p[iFp], fp.infinity); + + implTestMultiplyAll(fp.p[iFp], fpBits); + implTestMultiplyAll(fp.infinity, fpBits); + } + + int f2mBits = f2m.curve.getOrder().bitLength(); + for (int iF2m = 0; iF2m < f2m.pointSource.length / 2; iF2m++) + { + implTestAddSubtract(f2m.p[iF2m], f2m.infinity); + + implTestMultiplyAll(f2m.p[iF2m], f2mBits); + implTestMultiplyAll(f2m.infinity, f2mBits); + } + } + + /** + * Test encoding with and without point compression. + * + * @param p + * The point to be encoded and decoded. + */ + private void implTestEncoding(ECPoint p) + { + // Not Point Compression + byte[] unCompBarr = p.getEncoded(false); + ECPoint decUnComp = p.getCurve().decodePoint(unCompBarr); + assertPointsEqual("Error decoding uncompressed point", p, decUnComp); + + // Point compression + byte[] compBarr = p.getEncoded(true); + ECPoint decComp = p.getCurve().decodePoint(compBarr); + assertPointsEqual("Error decoding compressed point", p, decComp); + } + + private void implAddSubtractMultiplyTwiceEncodingTest(ECCurve curve, ECPoint q, BigInteger n) + { + // Get point at infinity on the curve + ECPoint infinity = curve.getInfinity(); + + implTestAddSubtract(q, infinity); + implTestMultiply(q, n.bitLength()); + implTestMultiply(infinity, n.bitLength()); + + int logSize = 32 - Integers.numberOfLeadingZeros(curve.getFieldSize() - 1); + int rounds = Math.max(2, Math.min(10, 32 - 3 * logSize)); + + ECPoint p = q; + for (int i = 0; i < rounds; ++i) + { + implTestEncoding(p); + p = p.twice(); + } + } + + private void implSqrtTest(ECCurve c) + { + if (ECAlgorithms.isFpCurve(c)) + { + BigInteger p = c.getField().getCharacteristic(); + BigInteger pMinusOne = p.subtract(ECConstants.ONE); + BigInteger legendreExponent = p.shiftRight(1); + + ECFieldElement zero = c.fromBigInteger(BigInteger.ZERO); + assertEquals(zero, zero.sqrt()); + + ECFieldElement one = c.fromBigInteger(BigInteger.ONE); + assertEquals(one, one.sqrt()); + + for (int i = 0; i < 20; ++i) + { + BigInteger x = BigIntegers.createRandomInRange(ECConstants.TWO, pMinusOne, secRand); + ECFieldElement fe = c.fromBigInteger(x); + ECFieldElement root = fe.sqrt(); + + if (root == null) + { + assertEquals(pMinusOne, x.modPow(legendreExponent, p)); + } + else + { + assertEquals(fe, root.square()); + } + } + } + else if (ECAlgorithms.isF2mCurve(c)) + { + int m = c.getFieldSize(); + BigInteger x = new BigInteger(m, secRand); + ECFieldElement fe = c.fromBigInteger(x); + for (int i = 0; i < 100; ++i) + { + ECFieldElement sq = fe.square(); + ECFieldElement check = sq.sqrt(); + assertEquals(fe, check); + fe = sq; + } + } + } + + private void implValidityTest(ECCurve c, ECPoint g) + { + assertTrue(g.isValid()); + + if (ECAlgorithms.isF2mCurve(c)) + { + BigInteger h = c.getCofactor(); + if (null != h) + { + if (!h.testBit(0)) + { + ECFieldElement sqrtB = c.getB().sqrt(); + ECPoint order2 = c.createPoint(ECConstants.ZERO, sqrtB.toBigInteger()); + assertTrue(order2.twice().isInfinity()); + assertFalse(order2.isValid()); + ECPoint bad2 = g.add(order2); + assertFalse(bad2.isValid()); + ECPoint good2 = bad2.add(order2); + assertTrue(good2.isValid()); + + if (!h.testBit(1)) + { + ECFieldElement L = solveQuadraticEquation(c, c.getA()); + assertNotNull(L); + ECFieldElement T = sqrtB; + ECFieldElement x = T.sqrt(); + ECFieldElement y = T.add(x.multiply(L)); + ECPoint order4 = c.createPoint(x.toBigInteger(), y.toBigInteger()); + assertTrue(order4.twice().equals(order2)); + assertFalse(order4.isValid()); + ECPoint bad4_1 = g.add(order4); + assertFalse(bad4_1.isValid()); + ECPoint bad4_2 = bad4_1.add(order4); + assertFalse(bad4_2.isValid()); + ECPoint bad4_3 = bad4_2.add(order4); + assertFalse(bad4_3.isValid()); + ECPoint good4 = bad4_3.add(order4); + assertTrue(good4.isValid()); + } + } + } + } + } + + private void implAddSubtractMultiplyTwiceEncodingTestAllCoords(X9ECParameters x9ECParameters) + { + BigInteger n = x9ECParameters.getN(); + ECPoint G = x9ECParameters.getG(); + ECCurve C = x9ECParameters.getCurve(); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (C.supportsCoordinateSystem(coord)) + { + ECCurve c = C; + ECPoint g = G; + + if (c.getCoordinateSystem() != coord) + { + c = C.configure().setCoordinateSystem(coord).create(); + g = c.importPoint(G); + } + + // The generator is multiplied by random b to get random q + BigInteger b = new BigInteger(n.bitLength(), secRand); + ECPoint q = g.multiply(b).normalize(); + + implAddSubtractMultiplyTwiceEncodingTest(c, q, n); + + implSqrtTest(c); + + implValidityTest(c, g); + } + } + } + + /** + * Calls <code>implTestAddSubtract()</code>, + * <code>implTestMultiply</code> and <code>implTestEncoding</code> for + * the standard elliptic curves as given in <code>SECNamedCurves</code>. + */ + @LargeTest + public void testAddSubtractMultiplyTwiceEncoding() + { + Set names = new HashSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9A = ECNamedCurveTable.getByName(name); + X9ECParameters x9B = CustomNamedCurves.getByName(name); + + if (x9A != null && x9B != null) + { + assertEquals(x9A.getCurve().getField(), x9B.getCurve().getField()); + assertEquals(x9A.getCurve().getA().toBigInteger(), x9B.getCurve().getA().toBigInteger()); + assertEquals(x9A.getCurve().getB().toBigInteger(), x9B.getCurve().getB().toBigInteger()); + assertOptionalValuesAgree(x9A.getCurve().getCofactor(), x9B.getCurve().getCofactor()); + assertOptionalValuesAgree(x9A.getCurve().getOrder(), x9B.getCurve().getOrder()); + + assertPointsEqual("Custom curve base-point inconsistency", x9A.getG(), x9B.getG()); + + assertEquals(x9A.getH(), x9B.getH()); + assertEquals(x9A.getN(), x9B.getN()); + assertOptionalValuesAgree(x9A.getSeed(), x9B.getSeed()); + + BigInteger k = new BigInteger(x9A.getN().bitLength(), secRand); + ECPoint pA = x9A.getG().multiply(k); + ECPoint pB = x9B.getG().multiply(k); + assertPointsEqual("Custom curve multiplication inconsistency", pA, pB); + } + + if (x9A != null) + { + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9A); + } + + if (x9B != null) + { + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9B); + } + } + } + + public void testExampleFpB0() throws Exception + { + /* + * The supersingular curve y^2 = x^3 - 3.x (i.e. with 'B' == 0) from RFC 6508 2.1, with + * curve parameters from RFC 6509 Appendix A. + */ + BigInteger p = fromHex( + "997ABB1F0A563FDA65C61198DAD0657A" + + "416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0" + + "E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1" + + "B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB0" + + "80C5DF10AC7ACE87666D807AFEA85FEB"); + BigInteger a = p.subtract(BigInteger.valueOf(3)); + BigInteger b = BigInteger.valueOf(0); + byte[] S = null; + BigInteger n = p.add(BigInteger.valueOf(1)).shiftRight(2); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h)); + + X9ECPoint G = configureBasepoint(curve, "04" + // Px + + "53FC09EE332C29AD0A7990053ED9B52A" + + "2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E10" + + "43D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1" + + "A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC" + + "80EC46C4967E0979880DC8ABEAE63895" + // Py + + "0A8249063F6009F1F9F1F0533634A135" + + "D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F6" + + "6B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0" + + "AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636" + + "ADB9B5706A67DCDE75573FD71BEF16D7"); + + X9ECParameters x9 = new X9ECParameters(curve, G, n, h, S); + + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9); + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + // NOTE: We intentionally test points for equality in both directions + assertEquals(message, a, b); + assertEquals(message, b, a); + } + + private void assertOptionalValuesAgree(Object a, Object b) + { + if (a != null && b != null) + { + assertEquals(a, b); + } + } + + private void assertOptionalValuesAgree(byte[] a, byte[] b) + { + if (a != null && b != null) + { + assertTrue(Arrays.areEqual(a, b)); + } + } + + private static X9ECPoint configureBasepoint(ECCurve curve, String encoding) + { + X9ECPoint G = new X9ECPoint(curve, Hex.decode(encoding)); + WNafUtil.configureBasepoint(G.getPoint()); + return G; + } + + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + + private List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + private static BigInteger fromHex( + String hex) + { + return new BigInteger(1, Hex.decode(hex)); + } + + private static ECFieldElement solveQuadraticEquation(ECCurve c, ECFieldElement rhs) + { + if (rhs.isZero()) + { + return rhs; + } + + ECFieldElement gamma, z, zeroElement = c.fromBigInteger(ECConstants.ZERO); + + int m = c.getFieldSize(); + Random rand = new Random(); + do + { + ECFieldElement t = c.fromBigInteger(new BigInteger(m, rand)); + z = zeroElement; + ECFieldElement w = rhs; + for (int i = 1; i < m; i++) + { + ECFieldElement w2 = w.square(); + z = z.square().add(w2.multiply(t)); + w = w2.add(rhs); + } + if (!w.isZero()) + { + return null; + } + gamma = z.square().add(z); + } + while (gamma.isZero()); + + return z; + } + + public static Test suite() + { + return new TestSuite(ECPointTest.class); + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/F2mProofer.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/F2mProofer.java new file mode 100644 index 00000000..b6875203 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/F2mProofer.java @@ -0,0 +1,204 @@ +package org.bouncycastle.math.ec.test; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; + +public class F2mProofer +{ + private static final int NUM_SAMPLES = 1000; + + private static final String PATH = "crypto/test/src/org/bouncycastle/math/ec/test/samples/"; + + private static final String INPUT_FILE_NAME_PREFIX = "Input_"; + + private static final String RESULT_FILE_NAME_PREFIX = "Output_"; + + /** + * The standard curves on which the tests are done + */ + public static final String[] CURVES = { "sect163r2", "sect233r1", + "sect283r1", "sect409r1", "sect571r1" }; + + private String pointToString(ECPoint.F2m p) + { + ECFieldElement.F2m x = (ECFieldElement.F2m) p.getAffineXCoord(); + ECFieldElement.F2m y = (ECFieldElement.F2m) p.getAffineYCoord(); + + int m = x.getM(); + int len = m / 2 + 5; + + StringBuffer sb = new StringBuffer(len); + sb.append('('); + sb.append(x.toBigInteger().toString(16)); + sb.append(", "); + sb.append(y.toBigInteger().toString(16)); + sb.append(')'); + + return sb.toString(); + } + + private void generateRandomInput(X9ECParameters x9ECParameters) + throws NoSuchAlgorithmException, IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG"); + Properties inputProps = new Properties(); + for (int i = 0; i < NUM_SAMPLES; i++) + { + BigInteger rand = BigIntegers.createRandomBigInteger(m, secRand); + inputProps.put(Integer.toString(i), rand.toString(16)); + } + String bits = Integer.toString(m); + FileOutputStream fos = new FileOutputStream(PATH + + INPUT_FILE_NAME_PREFIX + bits + ".properties"); + inputProps.store(fos, "Input Samples of length" + bits); + } + + private void multiplyPoints(X9ECParameters x9ECParameters, + String classPrefix) throws IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + String inputFileName = PATH + INPUT_FILE_NAME_PREFIX + m + + ".properties"; + Properties inputProps = new Properties(); + inputProps.load(new FileInputStream(inputFileName)); + + Properties outputProps = new Properties(); + + for (int i = 0; i < NUM_SAMPLES; i++) + { + BigInteger rand = new BigInteger(inputProps.getProperty(Integer + .toString(i)), 16); + ECPoint.F2m result = (ECPoint.F2m) g.multiply(rand).normalize(); + String resultStr = pointToString(result); + outputProps.setProperty(Integer.toString(i), resultStr); + } + + String outputFileName = PATH + RESULT_FILE_NAME_PREFIX + classPrefix + + "_" + m + ".properties"; + FileOutputStream fos = new FileOutputStream(outputFileName); + outputProps.store(fos, "Output Samples of length" + m); + } + + private Properties loadResults(String classPrefix, int m) + throws IOException + { + FileInputStream fis = new FileInputStream(PATH + + RESULT_FILE_NAME_PREFIX + classPrefix + "_" + m + ".properties"); + Properties res = new Properties(); + res.load(fis); + return res; + + } + + private void compareResult(X9ECParameters x9ECParameters, + String classPrefix1, String classPrefix2) throws IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + Properties res1 = loadResults(classPrefix1, m); + Properties res2 = loadResults(classPrefix2, m); + + Set keys = res1.keySet(); + Iterator iter = keys.iterator(); + while (iter.hasNext()) + { + String key = (String) iter.next(); + String result1 = res1.getProperty(key); + String result2 = res2.getProperty(key); + if (!(result1.equals(result2))) + { + System.err.println("Difference found: m = " + m + ", " + + result1 + " does not equal " + result2); + } + } + + } + + private static void usage() + { + System.err.println("Usage: F2mProofer [-init | -multiply <className> " + + "| -compare <className1> <className2>]"); + } + + public static void main(String[] args) throws Exception + { + if (args.length == 0) + { + usage(); + return; + } + F2mProofer proofer = new F2mProofer(); + if (args[0].equals("-init")) + { + System.out.println("Generating random input..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.generateRandomInput(x9ECParameters); + } + System.out + .println("Successfully generated random input in " + PATH); + } + else if (args[0].equals("-compare")) + { + if (args.length < 3) + { + usage(); + return; + } + String classPrefix1 = args[1]; + String classPrefix2 = args[2]; + System.out.println("Comparing results..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.compareResult(x9ECParameters, classPrefix1, + classPrefix2); + } + System.out.println("Successfully compared results in " + PATH); + } + else if (args[0].equals("-multiply")) + { + if (args.length < 2) + { + usage(); + return; + } + String classPrefix = args[1]; + System.out.println("Multiplying points..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.multiplyPoints(x9ECParameters, classPrefix); + } + System.out.println("Successfully generated multiplied points in " + + PATH); + } + else + { + usage(); + } + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/ec/test/FixedPointTest.java b/bcprov/src/test/java/org/bouncycastle/math/ec/test/FixedPointTest.java new file mode 100644 index 00000000..08d4c087 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/ec/test/FixedPointTest.java @@ -0,0 +1,90 @@ +package org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.FixedPointCombMultiplier; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +public class FixedPointTest + extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final int TESTS_PER_CURVE = 5; + + public void testFixedPointMultiplier() + { + final FixedPointCombMultiplier M = new FixedPointCombMultiplier(); + + Set names = new HashSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9A = ECNamedCurveTable.getByName(name); + X9ECParameters x9B = CustomNamedCurves.getByName(name); + + X9ECParameters x9 = x9B != null ? x9B : x9A; + + for (int i = 0; i < TESTS_PER_CURVE; ++i) + { + BigInteger k = new BigInteger(x9.getN().bitLength(), RANDOM); + ECPoint pRef = ECAlgorithms.referenceMultiply(x9.getG(), k); + + if (x9A != null) + { + ECPoint pA = M.multiply(x9A.getG(), k); + assertPointsEqual("Standard curve fixed-point failure", pRef, pA); + } + + if (x9B != null) + { + ECPoint pB = M.multiply(x9B.getG(), k); + assertPointsEqual("Custom curve fixed-point failure", pRef, pB); + } + } + } + } + + private List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + // NOTE: We intentionally test points for equality in both directions + assertEquals(message, a, b); + assertEquals(message, b, a); + } + + public static Test suite() + { + return new TestSuite(FixedPointTest.class); + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/raw/test/AllTests.java b/bcprov/src/test/java/org/bouncycastle/math/raw/test/AllTests.java new file mode 100644 index 00000000..c15cb94d --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/raw/test/AllTests.java @@ -0,0 +1,46 @@ +package org.bouncycastle.math.raw.test; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.test.PrintTestResult; + +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("Raw math tests"); + + suite.addTest(InterleaveTest.suite()); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/raw/test/InterleaveTest.java b/bcprov/src/test/java/org/bouncycastle/math/raw/test/InterleaveTest.java new file mode 100644 index 00000000..d701030b --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/raw/test/InterleaveTest.java @@ -0,0 +1,86 @@ +package org.bouncycastle.math.raw.test; + +import java.security.SecureRandom; + +import org.bouncycastle.math.raw.Interleave; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +public class InterleaveTest extends TestCase +{ + private static final int ITERATIONS = 1000; + + private static final SecureRandom R = new SecureRandom(); + + public void testExpand8To16() + { + // NOTE: Just test all inputs here + for (int iteration = 0; iteration < 256; ++iteration) + { + // NOTE: Implementation is expected to mask input + int x = iteration | (R.nextInt() << 8); + int expected = (int)referenceShuffle(x & 0xFFL); + int actual = Interleave.expand8to16(x); + assertEquals(expected, actual); + } + } + + public void testExpand16To32() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + // NOTE: Implementation is expected to mask input + int x = R.nextInt(); + int expected = (int)referenceShuffle(x & 0xFFFFL); + int actual = Interleave.expand16to32(x); + assertEquals(expected, actual); + } + } + + public void testExpand32To64() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + int x = R.nextInt(); + long expected = referenceShuffle(x & 0xFFFFFFFFL); + long actual = Interleave.expand32to64(x); + assertEquals(expected, actual); + } + } + + public void testExpand64To128() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + long x = R.nextLong(); + long expected = referenceShuffle(x); + long[] actual = new long[9]; + int offset = iteration % 8; + // NOTE: Implementation must overwrite existing values + actual[offset ] = R.nextLong(); + actual[offset + 1] = R.nextLong(); + Interleave.expand64To128(x, actual, offset); + assertEquals((expected ) & 0x5555555555555555L, actual[offset ]); + assertEquals((expected >>> 1) & 0x5555555555555555L, actual[offset + 1]); + } + } + + public static Test suite() + { + return new TestSuite(InterleaveTest.class); + } + + private static long referenceShuffle(long x) + { + long result = 0, y = x >>> 32; + for (int bit = 0; bit < 32; ++bit) + { + long selector = 1L << bit; + result |= ((x & selector) << (bit )); + result |= ((y & selector) << (bit + 1)); + } + return result; + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/test/AllTests.java b/bcprov/src/test/java/org/bouncycastle/math/test/AllTests.java new file mode 100644 index 00000000..f849b508 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/test/AllTests.java @@ -0,0 +1,46 @@ +package org.bouncycastle.math.test; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.test.PrintTestResult; + +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("Math tests"); + + suite.addTestSuite(PrimesTest.class); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/math/test/PrimesTest.java b/bcprov/src/test/java/org/bouncycastle/math/test/PrimesTest.java new file mode 100644 index 00000000..13af4cac --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/math/test/PrimesTest.java @@ -0,0 +1,183 @@ +package org.bouncycastle.math.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.math.Primes; +import org.bouncycastle.math.Primes.MROutput; +import org.bouncycastle.math.Primes.STOutput; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +public class PrimesTest extends TestCase +{ + private static final int ITERATIONS = 10; + private static final int PRIME_BITS = 256; + private static final int PRIME_CERTAINTY = 100; + + private static final BigInteger TWO = BigInteger.valueOf(2); + + private static final SecureRandom R = new SecureRandom(); + + public void testHasAnySmallFactors() + { + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertFalse(Primes.hasAnySmallFactors(prime)); + + // NOTE: Loop through ALL small values to be sure no small primes are missing + for (int smallFactor = 2; smallFactor <= Primes.SMALL_FACTOR_LIMIT; ++smallFactor) + { + BigInteger nonPrimeWithSmallFactor = BigInteger.valueOf(smallFactor).multiply(prime); + assertTrue(Primes.hasAnySmallFactors(nonPrimeWithSmallFactor)); + } + } + } + + public void testEnhancedMRProbablePrime() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + MROutput mr = Primes.enhancedMRProbablePrimeTest(prime, R, mrIterations); + assertFalse(mr.isProvablyComposite()); + assertFalse(mr.isNotPrimePower()); + assertNull(mr.getFactor()); + + BigInteger primePower = prime; + for (int i = 0; i <= (iterations % 8); ++i) + { + primePower = primePower.multiply(prime); + } + + MROutput mr2 = Primes.enhancedMRProbablePrimeTest(primePower, R, mrIterations); + assertTrue(mr2.isProvablyComposite()); + assertFalse(mr2.isNotPrimePower()); + assertEquals(mr2.getFactor(), prime); + + BigInteger nonPrimePower = randomPrime().multiply(prime); + MROutput mr3 = Primes.enhancedMRProbablePrimeTest(nonPrimePower, R, mrIterations); + assertTrue(mr3.isProvablyComposite()); + assertTrue(mr3.isNotPrimePower()); + assertNull(mr.getFactor()); + } + } + + public void testMRProbablePrime() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertTrue(Primes.isMRProbablePrime(prime, R, mrIterations)); + + BigInteger nonPrime = randomPrime().multiply(prime); + assertFalse(Primes.isMRProbablePrime(nonPrime, R, mrIterations)); + } + } + + public void testMRProbablePrimeToBase() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertTrue(referenceIsMRProbablePrime(prime, mrIterations)); + + BigInteger nonPrime = randomPrime().multiply(prime); + assertFalse(referenceIsMRProbablePrime(nonPrime, mrIterations)); + } + } + + public void testSTRandomPrime() + { + Digest[] digests = new Digest[]{ new SHA1Digest(), new SHA256Digest() }; + for (int digestIndex = 0; digestIndex < digests.length; ++digestIndex) + { + int coincidenceCount = 0; + + Digest digest = digests[digestIndex]; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + try + { + byte[] inputSeed = new byte[16]; + R.nextBytes(inputSeed); + + STOutput st = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertTrue(isPrime(st.getPrime())); + + STOutput st2 = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertEquals(st.getPrime(), st2.getPrime()); + assertEquals(st.getPrimeGenCounter(), st2.getPrimeGenCounter()); + assertTrue(Arrays.areEqual(st.getPrimeSeed(), st2.getPrimeSeed())); + + for (int i = 0; i < inputSeed.length; ++i) + { + inputSeed[i] ^= 0xFF; + } + + STOutput st3 = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertTrue(!st.getPrime().equals(st3.getPrime())); + assertFalse(Arrays.areEqual(st.getPrimeSeed(), st3.getPrimeSeed())); + + if (st.getPrimeGenCounter() == st3.getPrimeGenCounter()) + { + ++coincidenceCount; + } + } + catch (IllegalStateException e) + { + if (e.getMessage().startsWith("Too many iterations")) + { + --iterations; + continue; + } + + throw e; + } + } + + assertTrue(coincidenceCount * coincidenceCount < ITERATIONS); + } + } + + public static Test suite() + { + return new TestSuite(PrimesTest.class); + } + + private static boolean referenceIsMRProbablePrime(BigInteger x, int numBases) + { + BigInteger xSubTwo = x.subtract(TWO); + + for (int i = 0; i < numBases; ++i) + { + BigInteger b = BigIntegers.createRandomInRange(TWO, xSubTwo, R); + if (!Primes.isMRProbablePrimeToBase(x, b)) + { + return false; + } + } + + return true; + } + + private static boolean isPrime(BigInteger x) + { + return x.isProbablePrime(PRIME_CERTAINTY); + } + + private static BigInteger randomPrime() + { + return BigIntegers.createRandomPrime(PRIME_BITS, PRIME_CERTAINTY, R); + } +} diff --git a/bcprov/src/test/java/org/bouncycastle/test/PrintTestResult.java b/bcprov/src/test/java/org/bouncycastle/test/PrintTestResult.java new file mode 100644 index 00000000..2cca70b2 --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/test/PrintTestResult.java @@ -0,0 +1,36 @@ +package org.bouncycastle.test; + + +import java.util.Enumeration; + +import junit.framework.TestResult; + +public class PrintTestResult +{ + public static void printResult(TestResult result) + { + Enumeration e = result.failures(); + if (e != null) + { + while (e.hasMoreElements()) + { + System.out.println(e.nextElement()); + } + } + + e = result.errors(); + if (e != null) + { + while (e.hasMoreElements()) + { + System.out.println(e.nextElement()); + } + } + + if (!result.wasSuccessful()) + { + System.exit(1); + } + } +} + diff --git a/bcprov/src/test/java/org/bouncycastle/util/Times.java b/bcprov/src/test/java/org/bouncycastle/util/Times.java new file mode 100644 index 00000000..617d00bd --- /dev/null +++ b/bcprov/src/test/java/org/bouncycastle/util/Times.java @@ -0,0 +1,9 @@ +package org.bouncycastle.util; + +public final class Times +{ + public static long nanoTime() + { + return System.nanoTime(); + } +} diff --git a/mts/Android.bp b/mts/Android.bp new file mode 100644 index 00000000..be0378fd --- /dev/null +++ b/mts/Android.bp @@ -0,0 +1,44 @@ +// +// Copyright (C) 2023 The Android Open Source Project +// +// 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 +// +// http://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 { + default_visibility: ["//visibility:private"], + default_applicable_licenses: ["external_bouncycastle_license"], +} + +// Tests used in ART MTS. +android_test { + name: "MtsLibcoreBouncyCastleTestCases", + platform_apis: true, + min_sdk_version: "31", + manifest: "AndroidManifest.xml", + static_libs: [ + "bouncycastle-test-lib", + "cts-core-test-runner-axt", + ], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + test_suites: [ + "general-tests", + "mts-art", + ], + host_required: ["cts-dalvik-host-test-runner"], + test_config: "MtsLibcoreBouncyCastleTestCases.xml", +} diff --git a/mts/AndroidManifest.xml b/mts/AndroidManifest.xml new file mode 100644 index 00000000..135a8611 --- /dev/null +++ b/mts/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2023 The Android Open Source Project + * + * 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 + * + * http://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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.libcore.mts.bouncycastle"> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-sdk android:minSdkVersion="31" /> + <application android:usesCleartextTraffic="true"></application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.libcore.mts.bouncycastle" + android:label="Bouncy Castle test cases"> + <meta-data android:name="listener" + android:value="com.android.cts.runner.CtsLibcoreTestRunListener" /> + </instrumentation> + +</manifest> diff --git a/mts/MtsLibcoreBouncyCastleTestCases.xml b/mts/MtsLibcoreBouncyCastleTestCases.xml new file mode 100644 index 00000000..541731c3 --- /dev/null +++ b/mts/MtsLibcoreBouncyCastleTestCases.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + 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 + + http://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. +--> +<configuration description="Config for MTS Libcore Bouncy Castle test cases"> + <option name="config-descriptor:metadata" key="component" value="libcore" /> + <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> + <!-- Test is eligible to run on Android Multiuser users other than SYSTEM. + See source.android.com/devices/tech/admin/multi-user#user_types --> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.android.art.apex" /> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <!-- This MTS test module requires wifi, ensure wifi is on --> + <option name="run-command" value="settings put global wifi_on 1" /> + <option name="run-command" value="svc wifi enable" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <!-- this has just the instrumentation which acts as the tests we want to run --> + <option name="test-file-name" value="MtsLibcoreBouncyCastleTestCases.apk" /> + </target_preparer> + <test class="com.android.compatibility.testtype.LibcoreTest" > + <option name="package" value="android.libcore.mts.bouncycastle" /> + <option name="instrumentation-arg" key="filter" + value="com.android.cts.core.runner.ExpectationBasedFilter" /> + <option name="core-expectation" value="/knownfailures.txt" /> + <option name="runtime-hint" value="15m"/> + <!-- 20x default timeout of 600sec --> + <option name="shell-timeout" value="12000000"/> + <option name="hidden-api-checks" value="false"/> + </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.TestFailureModuleController"> + <option name="screenshot-on-failure" value="false" /> + </object> + + <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if + one of the Mainline modules below is present on the device used for testing. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <!-- ART Mainline Module (internal version). --> + <option name="mainline-module-package-name" value="com.google.android.art" /> + <!-- ART Mainline Module (external (AOSP) version). --> + <option name="mainline-module-package-name" value="com.android.art" /> + </object> + + <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> +</configuration> diff --git a/proguard.flags b/proguard.flags new file mode 100644 index 00000000..4a4ff37a --- /dev/null +++ b/proguard.flags @@ -0,0 +1,185 @@ +-keep class com.android.org.bouncycastle.jce.provider.BouncyCastleProvider { public *; } + +# Keep classes for Android supported algorithms, and internal ones loaded +# through reflection (cf. calls to ConfigurableProvider.addAlgorithm and +# ConfigurableProvider.addPrivateAlgorithm). The *$Mappings classes are used +# internally through reflection to configure the algorithms. + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA1AndAES_128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA1AndAES_256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA224AndAES_128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA224AndAES_256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA256AndAES_128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA256AndAES_256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA384AndAES_128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA384AndAES_256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA512AndAES_128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBEWithHmacSHA512AndAES_256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA18BIT { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA1UTF8 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA224UTF8 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA256UTF8 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA384UTF8 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2WithHmacSHA512UTF8 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2withUTF8 { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$AlgParams { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA1AES128AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA1AES256AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA224AES128AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA224AES256AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA256AES128AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA256AES256AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA384AES128AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA384AES256AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA512AES128AlgorithmParameters { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$PBEWithHmacSHA512AES256AlgorithmParameters { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$CBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$ECB { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithAESCBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithAESCBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithAESCBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithMD5And128BitAESCBCOpenSSL { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithMD5And192BitAESCBCOpenSSL { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithMD5And256BitAESCBCOpenSSL { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA1AESCBC128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA1AESCBC192 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA1AESCBC256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256AESCBC128 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256AESCBC192 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256AESCBC256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256And128BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256And192BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHA256And256BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHAAnd128BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHAAnd192BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithSHAAnd256BitAESBC { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.AES$Wrap { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$KeyGen { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$PBEWithSHAAnd128Bit { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$PBEWithSHAAnd128BitKeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$PBEWithSHAAnd40Bit { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$PBEWithSHAAnd40BitKeyFactory { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish$AlgParams { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish$ECB { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish$KeyGen { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$ECB { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$KeyGenerator { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$PBEWithMD5 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$PBEWithMD5KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$PBEWithSHA1 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DES$PBEWithSHA1KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$ECB { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$PBEWithSHAAndDES2Key { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$PBEWithSHAAndDES2KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$PBEWithSHAAndDES3Key { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$PBEWithSHAAndDES3KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$Wrap { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithMD5AndRC2 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithMD5KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHA1AndRC2 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHA1KeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHAAnd128BitKeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHAAnd128BitRC2 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHAAnd40BitKeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$PBEWithSHAAnd40BitRC2 { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Twofish$PBEWithSHA { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.symmetric.Twofish$PBEWithSHAKeyFactory { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.AlgorithmParameterGeneratorSpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.AlgorithmParametersSpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner$dsa224 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner$dsa256 { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner$noneDSA { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner$stdDSA { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyFactorySpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyPairGeneratorSpi { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.AlgorithmParameterGeneratorSpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.AlgorithmParametersSpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyAgreementSpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyFactorySpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyPairGeneratorSpi { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.AlgorithmParametersSpi$PSS { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$NoPadding { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA1$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA1$PBEWithMacKeyFactory { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA1$SHA1Mac { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA224$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA224$HashMac { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA256$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA256$HashMac { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA384$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA384$HashMac { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA512$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.digest.SHA512$HashMac { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.keystore.BC$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$BouncyCastleStore { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$Std { public *; } + +-keep class com.android.org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings { public *; } +-keep class com.android.org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi$BCPKCS12KeyStore { public *; } + +-keep class com.android.org.bouncycastle.jce.provider.CertStoreCollectionSpi { public *; } +-keep class com.android.org.bouncycastle.jce.provider.PKIXCertPathBuilderSpi { public *; } +-keep class com.android.org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi { public *; } + +# Classes only accessed from tests in MtsLibcoreBouncyCastleTestCases +-keep class com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable { public *; } +-keep class com.android.org.bouncycastle.asn1.x9.X962NamedCurves { public *; } +-keep class com.android.org.bouncycastle.asn1.x9.X9ECParameters { public *; } +-keep class com.android.org.bouncycastle.asn1.x9.X9ECPoint { public *; } +-keep class com.android.org.bouncycastle.crypto.ec.CustomNamedCurves { public *; } +-keep class com.android.org.bouncycastle.math.Primes { public *; } +-keep class com.android.org.bouncycastle.math.Primes$* { public *; } +-keep class com.android.org.bouncycastle.math.ec.ECAlgorithms { public *; } +-keep class com.android.org.bouncycastle.math.ec.ECCurve { public *; } +-keep class com.android.org.bouncycastle.math.ec.ECCurve$Config { public *; } +-keep class com.android.org.bouncycastle.math.ec.ECPoint { public *; } +-keep class com.android.org.bouncycastle.math.ec.FixedPointCombMultiplier { public *; } +-keep class com.android.org.bouncycastle.math.raw.Interleave { public *; } +-keep class com.android.org.bouncycastle.math.raw.Nat { public *; } +-keep class com.android.org.bouncycastle.math.raw.Nat256 { public *; } +-keep class com.android.org.bouncycastle.util.Arrays { public *; } +-keep class com.android.org.bouncycastle.util.Integers { public *; } +-keep class com.android.org.bouncycastle.util.encoders.Hex { public *; } + +# Classes only accessed from tests in CtsLibcoreTestCases +# tests.com.android.org.bouncycastle.jce.provider.CertBlocklistTest +-keep class com.android.org.bouncycastle.jce.provider.CertBlocklist { public *; } +-keep class com.android.org.bouncycastle.util.encoders.Base64 { public *; } +# tests.com.android.org.bouncycastle.crypto.digests +-keep class com.android.org.bouncycastle.crypto.digests.*Digest { public *; } +-keep class com.android.org.bouncycastle.crypto.digests.OpenSSLDigest$* { public *; } diff --git a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 1eeb6e96..51310cb5 100644 --- a/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -961,56 +961,26 @@ public class BaseBlockCipher { byte[] iv = new byte[ivLength]; - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // ivRandom.nextBytes(iv); + // rejected outright in BC 1.54 (even if an IV was passed in params). + // See b/27995180 for historical details. if (!isBCPBEKeyWithoutIV(key)) { ivRandom.nextBytes(iv); } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // END Android-changed: Reject PBE keys with no IV param = new ParametersWithIV(param, iv); ivParam = (ParametersWithIV)param; } else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) { - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // throw new InvalidAlgorithmParameterException("no IV set when one expected"); - if (!isBCPBEKeyWithoutIV(key)) { - throw new InvalidAlgorithmParameterException("no IV set when one expected"); - } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); - // Mimic behaviour in 1.52 by using an IV of 0's - param = new ParametersWithIV(param, new byte[ivLength]); - ivParam = (ParametersWithIV)param; - } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // rejected outright in BC 1.54 (even if an IV was passed in params). + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); + // END Android-changed: Reject PBE keys with no IV } } diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java new file mode 100644 index 00000000..39c9fea4 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP256R1FieldTest.java @@ -0,0 +1,179 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.custom.sec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.android.org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECFieldElement; +import com.android.org.bouncycastle.math.raw.Nat256; + +import junit.framework.TestCase; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class SecP256R1FieldTest extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final X9ECParameters DP = CustomNamedCurves + .getByOID(SECObjectIdentifiers.secp256r1); + private static final BigInteger Q = DP.getCurve().getField().getCharacteristic(); + + public void testMultiply1() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + ECFieldElement y = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testMultiply2() + { + int COUNT = 100; + ECFieldElement[] inputs = new ECFieldElement[COUNT]; + BigInteger[] INPUTS = new BigInteger[COUNT]; + + for (int i = 0; i < inputs.length; ++i) + { + inputs[i] = generateMultiplyInput_Random(); + INPUTS[i] = inputs[i].toBigInteger(); + } + + for (int j = 0; j < inputs.length; ++j) + { + for (int k = 0; k < inputs.length; ++k) + { + BigInteger R = INPUTS[j].multiply(INPUTS[k]).mod(Q); + + ECFieldElement z = inputs[j].multiply(inputs[k]); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + } + + public void testSquare() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /** + * Test multiplication with specifically selected values that triggered a bug in the modular + * reduction in OpenSSL (last affected version 0.9.8g). + * + * See "Practical realisation and elimination of an ECC-related software bug attack", B. B. + * Brumley, M. Barbarosa, D. Page, F. Vercauteren. + */ + public void testMultiply_OpenSSLBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInputA_OpenSSLBug(); + ECFieldElement y = generateMultiplyInputB_OpenSSLBug(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /** + * Test squaring with specifically selected values that triggered a bug in the modular reduction + * in OpenSSL (last affected version 0.9.8g). + * + * See "Practical realisation and elimination of an ECC-related software bug attack", B. B. + * Brumley, M. Barbarosa, D. Page, F. Vercauteren. + */ + public void testSquare_OpenSSLBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateSquareInput_OpenSSLBug(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + private ECFieldElement fe(BigInteger x) + { + return DP.getCurve().fromBigInteger(x); + } + + private ECFieldElement generateMultiplyInput_Random() + { + return fe(new BigInteger(DP.getCurve().getFieldSize() + 32, RANDOM).mod(Q)); + } + + private ECFieldElement generateMultiplyInputA_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[4] = 3; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } + + private ECFieldElement generateMultiplyInputB_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[3] = 1; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } + + private ECFieldElement generateSquareInput_OpenSSLBug() + { + int[] x = Nat256.create(); + x[0] = RANDOM.nextInt() >>> 1; + x[4] = 2; + x[7] = -1; + + return fe(Nat256.toBigInteger(x)); + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java new file mode 100644 index 00000000..d43d57a7 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/custom/sec/test/SecP384R1FieldTest.java @@ -0,0 +1,144 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.custom.sec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.android.org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECFieldElement; +import com.android.org.bouncycastle.math.raw.Nat; + +import junit.framework.TestCase; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class SecP384R1FieldTest extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final X9ECParameters DP = CustomNamedCurves + .getByOID(SECObjectIdentifiers.secp384r1); + private static final BigInteger Q = DP.getCurve().getField().getCharacteristic(); + + public void testMultiply1() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + ECFieldElement y = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(), Y = y.toBigInteger(); + BigInteger R = X.multiply(Y).mod(Q); + + ECFieldElement z = x.multiply(y); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testMultiply2() + { + int COUNT = 100; + ECFieldElement[] inputs = new ECFieldElement[COUNT]; + BigInteger[] INPUTS = new BigInteger[COUNT]; + + for (int i = 0; i < inputs.length; ++i) + { + inputs[i] = generateMultiplyInput_Random(); + INPUTS[i] = inputs[i].toBigInteger(); + } + + for (int j = 0; j < inputs.length; ++j) + { + for (int k = 0; k < inputs.length; ++k) + { + BigInteger R = INPUTS[j].multiply(INPUTS[k]).mod(Q); + + ECFieldElement z = inputs[j].multiply(inputs[k]); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + } + + public void testSquare() + { + int COUNT = 1000; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateMultiplyInput_Random(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + public void testSquare_CarryBug() + { + int COUNT = 100; + + for (int i = 0; i < COUNT; ++i) + { + ECFieldElement x = generateSquareInput_CarryBug(); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + } + + /* + * Based on another example input demonstrating the carry propagation bug in Nat192.square, as + * reported by Joseph Friel on dev-crypto. + */ + public void testSquare_CarryBug_Reported() + { + ECFieldElement x = fe(new BigInteger("2fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", 16)); + + BigInteger X = x.toBigInteger(); + BigInteger R = X.multiply(X).mod(Q); + + ECFieldElement z = x.square(); + BigInteger Z = z.toBigInteger(); + + assertEquals(R, Z); + } + + private ECFieldElement fe(BigInteger x) + { + return DP.getCurve().fromBigInteger(x); + } + + private ECFieldElement generateMultiplyInput_Random() + { + return fe(new BigInteger(DP.getCurve().getFieldSize() + 32, RANDOM).mod(Q)); + } + + private ECFieldElement generateSquareInput_CarryBug() + { + int[] x = Nat.create(12); + x[0] = RANDOM.nextInt() >>> 1; + x[6] = 2; + x[10] = -1 << 16; + x[11] = -1; + + return fe(Nat.toBigInteger(12, x)); + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/AllTests.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/AllTests.java new file mode 100644 index 00000000..95dfdc82 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/AllTests.java @@ -0,0 +1,69 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import com.android.org.bouncycastle.test.PrintTestResult; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("EC Math tests"); + + // Android-changed: parameterized the test. + // suite.addTestSuite(ECAlgorithmsTest.class); + suite.addTestSuite(ECPointTest.class); + suite.addTestSuite(FixedPointTest.class); + + return new BCTestSetup(suite); + } + + static List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java new file mode 100644 index 00000000..63f9221c --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECAlgorithmsTest.java @@ -0,0 +1,243 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.asn1.x9.X9ECPoint; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECAlgorithms; +import com.android.org.bouncycastle.math.ec.ECCurve; +import com.android.org.bouncycastle.math.ec.ECPoint; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +// Android-changed: parameterized the test. +/** + * @hide This class is not part of the Android public SDK API + */ +@RunWith(Parameterized.class) +public class ECAlgorithmsTest +{ + private static final int SCALE = 4; + private static final SecureRandom RND = new SecureRandom(); + + + // BEGIN Android-added: parameterized the test. + @Parameterized.Parameters(name = "{0}") + public static String[] getAllX9ECParameters() { + Set<String> names = new HashSet<>(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + return names.toArray(new String[0]); + } + + @Parameterized.Parameter(0) + public String name; + + private ArrayList getX9s(String name) { + ArrayList<X9ECParameters> x9s = new ArrayList<>(); + + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + + x9 = CustomNamedCurves.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + return x9s; + } + // END Android-added: parameterized the test. + + @Ignore("secp256r1 is covered by testSumOfMultipliesComplete") + public void testSumOfMultiplies() + { + X9ECParameters x9 = CustomNamedCurves.getByName("secp256r1"); + assertNotNull(x9); + doTestSumOfMultiplies(x9); + } + + // TODO Ideally, mark this test not to run by default + @Test + public void testSumOfMultipliesComplete() + { + // Android-changed: parameterized the test. + // ArrayList x9s = getTestCurves(); + ArrayList<X9ECParameters> x9s = getX9s(name); + Iterator it = x9s.iterator(); + while (it.hasNext()) + { + X9ECParameters x9 = (X9ECParameters)it.next(); + doTestSumOfMultiplies(x9); + } + } + + @Ignore("secp256r1 is covered by testSumOfTwoMultipliesComplete") + public void testSumOfTwoMultiplies() + { + X9ECParameters x9 = CustomNamedCurves.getByName("secp256r1"); + assertNotNull(x9); + doTestSumOfTwoMultiplies(x9); + } + + // TODO Ideally, mark this test not to run by default + @Test + public void testSumOfTwoMultipliesComplete() + { + // Android-changed: parameterized the test. + // ArrayList x9s = getTestCurves(); + ArrayList<X9ECParameters> x9s = getX9s(name); + Iterator it = x9s.iterator(); + while (it.hasNext()) + { + X9ECParameters x9 = (X9ECParameters)it.next(); + doTestSumOfTwoMultiplies(x9); + } + } + + private void doTestSumOfMultiplies(X9ECParameters x9) + { + ECPoint[] points = new ECPoint[SCALE]; + BigInteger[] scalars = new BigInteger[SCALE]; + for (int i = 0; i < SCALE; ++i) + { + points[i] = getRandomPoint(x9); + scalars[i] = getRandomScalar(x9); + } + + ECPoint u = x9.getCurve().getInfinity(); + for (int i = 0; i < SCALE; ++i) + { + u = u.add(points[i].multiply(scalars[i])); + + ECPoint v = ECAlgorithms.sumOfMultiplies(copyPoints(points, i + 1), copyScalars(scalars, i + 1)); + + ECPoint[] results = new ECPoint[]{ u, v }; + x9.getCurve().normalizeAll(results); + + assertPointsEqual("ECAlgorithms.sumOfMultiplies is incorrect", results[0], results[1]); + } + } + + private void doTestSumOfTwoMultiplies(X9ECParameters x9) + { + ECPoint p = getRandomPoint(x9); + BigInteger a = getRandomScalar(x9); + + for (int i = 0; i < SCALE; ++i) + { + ECPoint q = getRandomPoint(x9); + BigInteger b = getRandomScalar(x9); + + ECPoint u = p.multiply(a).add(q.multiply(b)); + ECPoint v = ECAlgorithms.shamirsTrick(p, a, q, b); + ECPoint w = ECAlgorithms.sumOfTwoMultiplies(p, a, q, b); + + ECPoint[] results = new ECPoint[]{ u, v, w }; + x9.getCurve().normalizeAll(results); + + assertPointsEqual("ECAlgorithms.shamirsTrick is incorrect", results[0], results[1]); + assertPointsEqual("ECAlgorithms.sumOfTwoMultiplies is incorrect", results[0], results[2]); + + p = q; + a = b; + } + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + assertEquals(message, a, b); + } + + private ECPoint[] copyPoints(ECPoint[] ps, int len) + { + ECPoint[] result = new ECPoint[len]; + System.arraycopy(ps, 0, result, 0, len); + return result; + } + + private BigInteger[] copyScalars(BigInteger[] ks, int len) + { + BigInteger[] result = new BigInteger[len]; + System.arraycopy(ks, 0, result, 0, len); + return result; + } + + private ECPoint getRandomPoint(X9ECParameters x9) + { + return x9.getG().multiply(getRandomScalar(x9)); + } + + private BigInteger getRandomScalar(X9ECParameters x9) + { + return new BigInteger(x9.getN().bitLength(), RND); + } + + private ArrayList getTestCurves() + { + ArrayList x9s = new ArrayList(); + Set names = new HashSet(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9 = ECNamedCurveTable.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + + x9 = CustomNamedCurves.getByName(name); + if (x9 != null) + { + addTestCurves(x9s, x9); + } + } + return x9s; + } + + private void addTestCurves(ArrayList x9s, X9ECParameters x9) + { + ECCurve curve = x9.getCurve(); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (curve.getCoordinateSystem() == coord) + { + x9s.add(x9); + } + else if (curve.supportsCoordinateSystem(coord)) + { + ECCurve c = curve.configure().setCoordinateSystem(coord).create(); + x9s.add(new X9ECParameters(c, new X9ECPoint(c.importPoint(x9.getG()), false), x9.getN(), x9.getH())); + } + } + } + + // Android-changed: Use JUnit4. + /* + public static Test suite() + { + return new TestSuite(ECAlgorithmsTest.class); + } + */ +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java new file mode 100644 index 00000000..104b65fb --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointPerformanceTest.java @@ -0,0 +1,204 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import junit.framework.TestCase; +import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECCurve; +import com.android.org.bouncycastle.math.ec.ECPoint; +import com.android.org.bouncycastle.util.Times; + +/** + * Compares the performance of the the window NAF point multiplication against conventional point + * multiplication. + * @hide This class is not part of the Android public SDK API + */ +public class ECPointPerformanceTest extends TestCase +{ + static final int MILLIS_PER_ROUND = 200; + static final int MILLIS_WARMUP = 1000; + + static final int MULTS_PER_CHECK = 16; + static final int NUM_ROUNDS = 10; + + private static String[] COORD_NAMES = new String[]{ "AFFINE", "HOMOGENEOUS", "JACOBIAN", "JACOBIAN-CHUDNOVSKY", + "JACOBIAN-MODIFIED", "LAMBDA-AFFINE", "LAMBDA-PROJECTIVE", "SKEWED" }; + + private void randMult(String curveName) throws Exception + { + X9ECParameters spec = ECNamedCurveTable.getByName(curveName); + if (spec != null) + { + randMult(curveName, spec); + } + + spec = CustomNamedCurves.getByName(curveName); + if (spec != null) + { + randMult(curveName + " (custom)", spec); + } + } + + private void randMult(String label, X9ECParameters spec) throws Exception + { + ECCurve C = spec.getCurve(); + ECPoint G = (ECPoint)spec.getG(); + BigInteger n = spec.getN(); + + SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); + random.setSeed(System.currentTimeMillis()); + + System.out.println(label); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (C.supportsCoordinateSystem(coord)) + { + ECCurve c = C; + ECPoint g = G; + + boolean defaultCoord = (c.getCoordinateSystem() == coord); + if (!defaultCoord) + { + c = C.configure().setCoordinateSystem(coord).create(); + g = c.importPoint(G); + } + + double avgRate = randMult(random, g, n); + String coordName = COORD_NAMES[coord]; + StringBuffer sb = new StringBuffer(); + sb.append(" "); + sb.append(defaultCoord ? '*' : ' '); + sb.append(coordName); + for (int j = sb.length(); j < 30; ++j) + { + sb.append(' '); + } + sb.append(": "); + sb.append(avgRate); + sb.append(" mults/sec"); + for (int j = sb.length(); j < 64; ++j) + { + sb.append(' '); + } + sb.append('('); + sb.append(1000.0 / avgRate); + sb.append(" millis/mult)"); + System.out.println(sb.toString()); + } + } + } + + private double randMult(SecureRandom random, ECPoint g, BigInteger n) throws Exception + { + BigInteger[] ks = new BigInteger[128]; + for (int i = 0; i < ks.length; ++i) + { + ks[i] = new BigInteger(n.bitLength() - 1, random); + } + + int ki = 0; + ECPoint p = g; + + { + long startTime = Times.nanoTime(); + long goalTime = startTime + 1000000L * MILLIS_WARMUP; + + do + { + BigInteger k = ks[ki]; + p = g.multiply(k); + if ((ki & 1) != 0) + { + g = p; + } + if (++ki == ks.length) + { + ki = 0; + } + } + while (Times.nanoTime() < goalTime); + } + + double minRate = Double.MAX_VALUE, maxRate = Double.MIN_VALUE, totalRate = 0.0; + + for (int i = 1; i <= NUM_ROUNDS; i++) + { + long startTime = Times.nanoTime(); + long goalTime = startTime + 1000000L * MILLIS_PER_ROUND; + long count = 0, endTime; + + do + { + ++count; + + for (int j = 0; j < MULTS_PER_CHECK; ++j) + { + BigInteger k = ks[ki]; + p = g.multiply(k); + if ((ki & 1) != 0) + { + g = p; + } + if (++ki == ks.length) + { + ki = 0; + } + } + + endTime = Times.nanoTime(); + } + while (endTime < goalTime); + + double roundElapsed = (double)(endTime - startTime); + double roundRate = count * MULTS_PER_CHECK * 1000000000L / roundElapsed; + + minRate = Math.min(minRate, roundRate); + maxRate = Math.max(maxRate, roundRate); + totalRate += roundRate; + } + + return (totalRate - minRate - maxRate) / (NUM_ROUNDS - 2); + } + + public void testMultiply() throws Exception + { + // Android-changed: Skip this test as this perf test is timed out. + if (true) { + return; + } + SortedSet names = new TreeSet(AllTests.enumToList(ECNamedCurveTable.getNames())); + names.addAll(AllTests.enumToList(CustomNamedCurves.getNames())); + + Set oids = new HashSet(); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name); + if (oid == null) + { + oid = CustomNamedCurves.getOID(name); + } + if (oid != null && !oids.add(oid)) + { + continue; + } + + randMult(name); + } + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java new file mode 100644 index 00000000..0978d2ef --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/ECPointTest.java @@ -0,0 +1,744 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.asn1.x9.X9ECPoint; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECAlgorithms; +import com.android.org.bouncycastle.math.ec.ECConstants; +import com.android.org.bouncycastle.math.ec.ECCurve; +import com.android.org.bouncycastle.math.ec.ECFieldElement; +import com.android.org.bouncycastle.math.ec.ECPoint; +import com.android.org.bouncycastle.math.ec.WNafUtil; +import com.android.org.bouncycastle.util.Arrays; +import com.android.org.bouncycastle.util.BigIntegers; +import com.android.org.bouncycastle.util.Integers; +import com.android.org.bouncycastle.util.encoders.Hex; + +import android.platform.test.annotations.LargeTest; + +/** + * Test class for {@link com.android.org.bouncycastle.math.ec.ECPoint ECPoint}. All + * literature values are taken from "Guide to elliptic curve cryptography", + * Darrel Hankerson, Alfred J. Menezes, Scott Vanstone, 2004, Springer-Verlag + * New York, Inc. + * @hide This class is not part of the Android public SDK API + */ +public class ECPointTest extends TestCase +{ + /** + * Random source used to generate random points + */ + private SecureRandom secRand = new SecureRandom(); + + private ECPointTest.Fp fp = null; + + private ECPointTest.F2m f2m = null; + + /** + * Nested class containing sample literature values for <code>Fp</code>. + * @hide This class is not part of the Android public SDK API + */ + public static class Fp + { + private final BigInteger q = new BigInteger("1063"); + + private final BigInteger a = new BigInteger("4"); + + private final BigInteger b = new BigInteger("20"); + + private final BigInteger n = new BigInteger("38"); + + private final BigInteger h = new BigInteger("1"); + + private final ECCurve curve = new ECCurve.Fp(q, a, b, n, h); + + private final ECPoint infinity = curve.getInfinity(); + + private final int[] pointSource = { 1, 5, 4, 10, 234, 1024, 817, 912 }; + + private ECPoint[] p = new ECPoint[pointSource.length / 2]; + + /** + * Creates the points on the curve with literature values. + */ + private void createPoints() + { + for (int i = 0; i < pointSource.length / 2; i++) + { + p[i] = curve.createPoint( + new BigInteger(Integer.toString(pointSource[2 * i])), + new BigInteger(Integer.toString(pointSource[2 * i + 1]))); + } + } + } + + /** + * Nested class containing sample literature values for <code>F2m</code>. + * @hide This class is not part of the Android public SDK API + */ + public static class F2m + { + // Irreducible polynomial for TPB z^4 + z + 1 + private final int m = 4; + + private final int k1 = 1; + + // a = z^3 + private final BigInteger aTpb = new BigInteger("1000", 2); + + // b = z^3 + 1 + private final BigInteger bTpb = new BigInteger("1001", 2); + + private final BigInteger n = new BigInteger("23"); + + private final BigInteger h = new BigInteger("1"); + + private final ECCurve.F2m curve = new ECCurve.F2m(m, k1, aTpb, bTpb, n, h); + + private final ECPoint.F2m infinity = (ECPoint.F2m) curve.getInfinity(); + + private final String[] pointSource = { "0010", "1111", "1100", "1100", + "0001", "0001", "1011", "0010" }; + + private ECPoint[] p = new ECPoint[pointSource.length / 2]; + + /** + * Creates the points on the curve with literature values. + */ + private void createPoints() + { + for (int i = 0; i < pointSource.length / 2; i++) + { + p[i] = curve.createPoint( + new BigInteger(pointSource[2 * i], 2), + new BigInteger(pointSource[2 * i + 1], 2)); + } + } + } + + public void setUp() + { + fp = new ECPointTest.Fp(); + fp.createPoints(); + + f2m = new ECPointTest.F2m(); + f2m.createPoints(); + } + + /** + * Tests, if inconsistent points can be created, i.e. points with exactly + * one null coordinate (not permitted). + */ + public void testPointCreationConsistency() + { + try + { + ECPoint bad = fp.curve.createPoint(new BigInteger("12"), null); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = fp.curve.createPoint(null, new BigInteger("12")); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = f2m.curve.createPoint(new BigInteger("1011"), null); + fail(); + } + catch (IllegalArgumentException expected) + { + } + + try + { + ECPoint bad = f2m.curve.createPoint(null, new BigInteger("1011")); + fail(); + } + catch (IllegalArgumentException expected) + { + } + } + + /** + * Tests <code>ECPoint.add()</code> against literature values. + * + * @param p + * The array of literature values. + * @param infinity + * The point at infinity on the respective curve. + */ + private void implTestAdd(ECPoint[] p, ECPoint infinity) + { + assertPointsEqual("p0 plus p1 does not equal p2", p[2], p[0].add(p[1])); + assertPointsEqual("p1 plus p0 does not equal p2", p[2], p[1].add(p[0])); + for (int i = 0; i < p.length; i++) + { + assertPointsEqual("Adding infinity failed", p[i], p[i].add(infinity)); + assertPointsEqual("Adding to infinity failed", p[i], infinity.add(p[i])); + } + } + + /** + * Calls <code>implTestAdd()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testAdd() + { + implTestAdd(fp.p, fp.infinity); + implTestAdd(f2m.p, f2m.infinity); + } + + /** + * Tests <code>ECPoint.twice()</code> against literature values. + * + * @param p + * The array of literature values. + */ + private void implTestTwice(ECPoint[] p) + { + assertPointsEqual("Twice incorrect", p[3], p[0].twice()); + assertPointsEqual("Add same point incorrect", p[3], p[0].add(p[0])); + } + + /** + * Calls <code>implTestTwice()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testTwice() + { + implTestTwice(fp.p); + implTestTwice(f2m.p); + } + + private void implTestThreeTimes(ECPoint[] p) + { + ECPoint P = p[0]; + ECPoint _3P = P.add(P).add(P); + assertPointsEqual("ThreeTimes incorrect", _3P, P.threeTimes()); + assertPointsEqual("TwicePlus incorrect", _3P, P.twicePlus(P)); + } + + /** + * Calls <code>implTestThreeTimes()</code> for <code>Fp</code> and + * <code>F2m</code>. + */ + public void testThreeTimes() + { + implTestThreeTimes(fp.p); + implTestThreeTimes(f2m.p); + } + + /** + * Goes through all points on an elliptic curve and checks, if adding a + * point <code>k</code>-times is the same as multiplying the point by + * <code>k</code>, for all <code>k</code>. Should be called for points + * on very small elliptic curves only. + * + * @param p + * The base point on the elliptic curve. + * @param infinity + * The point at infinity on the elliptic curve. + */ + private void implTestAllPoints(ECPoint p, ECPoint infinity) + { + ECPoint adder = infinity; + ECPoint multiplier = infinity; + + BigInteger i = BigInteger.valueOf(1); + do + { + adder = adder.add(p); + multiplier = p.multiply(i); + assertPointsEqual("Results of add() and multiply() are inconsistent " + + i, adder, multiplier); + i = i.add(BigInteger.ONE); + } + while (!(adder.equals(infinity))); + } + + /** + * Calls <code>implTestAllPoints()</code> for the small literature curves, + * both for <code>Fp</code> and <code>F2m</code>. + */ + public void testAllPoints() + { + for (int i = 0; i < fp.p.length; i++) + { + implTestAllPoints(fp.p[i], fp.infinity); + } + + for (int i = 0; i < f2m.p.length; i++) + { + implTestAllPoints(f2m.p[i], f2m.infinity); + } + } + + /** + * Checks, if the point multiplication algorithm of the given point yields + * the same result as point multiplication done by the reference + * implementation given in <code>multiply()</code>. This method chooses a + * random number by which the given point <code>p</code> is multiplied. + * + * @param p + * The point to be multiplied. + * @param numBits + * The bitlength of the random number by which <code>p</code> + * is multiplied. + */ + private void implTestMultiply(ECPoint p, int numBits) + { + BigInteger k = new BigInteger(numBits, secRand); + ECPoint ref = ECAlgorithms.referenceMultiply(p, k); + ECPoint q = p.multiply(k); + assertPointsEqual("ECPoint.multiply is incorrect", ref, q); + } + + /** + * Checks, if the point multiplication algorithm of the given point yields + * the same result as point multiplication done by the reference + * implementation given in <code>multiply()</code>. This method tests + * multiplication of <code>p</code> by every number of bitlength + * <code>numBits</code> or less. + * + * @param p + * The point to be multiplied. + * @param numBits + * Try every multiplier up to this bitlength + */ + private void implTestMultiplyAll(ECPoint p, int numBits) + { + BigInteger bound = BigInteger.ONE.shiftLeft(numBits); + BigInteger k = BigInteger.ZERO; + + do + { + ECPoint ref = ECAlgorithms.referenceMultiply(p, k); + ECPoint q = p.multiply(k); + assertPointsEqual("ECPoint.multiply is incorrect", ref, q); + k = k.add(BigInteger.ONE); + } + while (k.compareTo(bound) < 0); + } + + /** + * Tests <code>ECPoint.add()</code> and <code>ECPoint.subtract()</code> + * for the given point and the given point at infinity. + * + * @param p + * The point on which the tests are performed. + * @param infinity + * The point at infinity on the same curve as <code>p</code>. + */ + private void implTestAddSubtract(ECPoint p, ECPoint infinity) + { + assertPointsEqual("Twice and Add inconsistent", p.twice(), p.add(p)); + assertPointsEqual("Twice p - p is not p", p, p.twice().subtract(p)); + assertPointsEqual("TwicePlus(p, -p) is not p", p, p.twicePlus(p.negate())); + assertPointsEqual("p - p is not infinity", infinity, p.subtract(p)); + assertPointsEqual("p plus infinity is not p", p, p.add(infinity)); + assertPointsEqual("infinity plus p is not p", p, infinity.add(p)); + assertPointsEqual("infinity plus infinity is not infinity ", infinity, infinity.add(infinity)); + assertPointsEqual("Twice infinity is not infinity ", infinity, infinity.twice()); + } + + /** + * Calls <code>implTestAddSubtract()</code> for literature values, both + * for <code>Fp</code> and <code>F2m</code>. + */ + public void testAddSubtractMultiplySimple() + { + int fpBits = fp.curve.getOrder().bitLength(); + for (int iFp = 0; iFp < fp.pointSource.length / 2; iFp++) + { + implTestAddSubtract(fp.p[iFp], fp.infinity); + + implTestMultiplyAll(fp.p[iFp], fpBits); + implTestMultiplyAll(fp.infinity, fpBits); + } + + int f2mBits = f2m.curve.getOrder().bitLength(); + for (int iF2m = 0; iF2m < f2m.pointSource.length / 2; iF2m++) + { + implTestAddSubtract(f2m.p[iF2m], f2m.infinity); + + implTestMultiplyAll(f2m.p[iF2m], f2mBits); + implTestMultiplyAll(f2m.infinity, f2mBits); + } + } + + /** + * Test encoding with and without point compression. + * + * @param p + * The point to be encoded and decoded. + */ + private void implTestEncoding(ECPoint p) + { + // Not Point Compression + byte[] unCompBarr = p.getEncoded(false); + ECPoint decUnComp = p.getCurve().decodePoint(unCompBarr); + assertPointsEqual("Error decoding uncompressed point", p, decUnComp); + + // Point compression + byte[] compBarr = p.getEncoded(true); + ECPoint decComp = p.getCurve().decodePoint(compBarr); + assertPointsEqual("Error decoding compressed point", p, decComp); + } + + private void implAddSubtractMultiplyTwiceEncodingTest(ECCurve curve, ECPoint q, BigInteger n) + { + // Get point at infinity on the curve + ECPoint infinity = curve.getInfinity(); + + implTestAddSubtract(q, infinity); + implTestMultiply(q, n.bitLength()); + implTestMultiply(infinity, n.bitLength()); + + int logSize = 32 - Integers.numberOfLeadingZeros(curve.getFieldSize() - 1); + int rounds = Math.max(2, Math.min(10, 32 - 3 * logSize)); + + ECPoint p = q; + for (int i = 0; i < rounds; ++i) + { + implTestEncoding(p); + p = p.twice(); + } + } + + private void implSqrtTest(ECCurve c) + { + if (ECAlgorithms.isFpCurve(c)) + { + BigInteger p = c.getField().getCharacteristic(); + BigInteger pMinusOne = p.subtract(ECConstants.ONE); + BigInteger legendreExponent = p.shiftRight(1); + + ECFieldElement zero = c.fromBigInteger(BigInteger.ZERO); + assertEquals(zero, zero.sqrt()); + + ECFieldElement one = c.fromBigInteger(BigInteger.ONE); + assertEquals(one, one.sqrt()); + + for (int i = 0; i < 20; ++i) + { + BigInteger x = BigIntegers.createRandomInRange(ECConstants.TWO, pMinusOne, secRand); + ECFieldElement fe = c.fromBigInteger(x); + ECFieldElement root = fe.sqrt(); + + if (root == null) + { + assertEquals(pMinusOne, x.modPow(legendreExponent, p)); + } + else + { + assertEquals(fe, root.square()); + } + } + } + else if (ECAlgorithms.isF2mCurve(c)) + { + int m = c.getFieldSize(); + BigInteger x = new BigInteger(m, secRand); + ECFieldElement fe = c.fromBigInteger(x); + for (int i = 0; i < 100; ++i) + { + ECFieldElement sq = fe.square(); + ECFieldElement check = sq.sqrt(); + assertEquals(fe, check); + fe = sq; + } + } + } + + private void implValidityTest(ECCurve c, ECPoint g) + { + assertTrue(g.isValid()); + + if (ECAlgorithms.isF2mCurve(c)) + { + BigInteger h = c.getCofactor(); + if (null != h) + { + if (!h.testBit(0)) + { + ECFieldElement sqrtB = c.getB().sqrt(); + ECPoint order2 = c.createPoint(ECConstants.ZERO, sqrtB.toBigInteger()); + assertTrue(order2.twice().isInfinity()); + assertFalse(order2.isValid()); + ECPoint bad2 = g.add(order2); + assertFalse(bad2.isValid()); + ECPoint good2 = bad2.add(order2); + assertTrue(good2.isValid()); + + if (!h.testBit(1)) + { + ECFieldElement L = solveQuadraticEquation(c, c.getA()); + assertNotNull(L); + ECFieldElement T = sqrtB; + ECFieldElement x = T.sqrt(); + ECFieldElement y = T.add(x.multiply(L)); + ECPoint order4 = c.createPoint(x.toBigInteger(), y.toBigInteger()); + assertTrue(order4.twice().equals(order2)); + assertFalse(order4.isValid()); + ECPoint bad4_1 = g.add(order4); + assertFalse(bad4_1.isValid()); + ECPoint bad4_2 = bad4_1.add(order4); + assertFalse(bad4_2.isValid()); + ECPoint bad4_3 = bad4_2.add(order4); + assertFalse(bad4_3.isValid()); + ECPoint good4 = bad4_3.add(order4); + assertTrue(good4.isValid()); + } + } + } + } + } + + private void implAddSubtractMultiplyTwiceEncodingTestAllCoords(X9ECParameters x9ECParameters) + { + BigInteger n = x9ECParameters.getN(); + ECPoint G = x9ECParameters.getG(); + ECCurve C = x9ECParameters.getCurve(); + + int[] coords = ECCurve.getAllCoordinateSystems(); + for (int i = 0; i < coords.length; ++i) + { + int coord = coords[i]; + if (C.supportsCoordinateSystem(coord)) + { + ECCurve c = C; + ECPoint g = G; + + if (c.getCoordinateSystem() != coord) + { + c = C.configure().setCoordinateSystem(coord).create(); + g = c.importPoint(G); + } + + // The generator is multiplied by random b to get random q + BigInteger b = new BigInteger(n.bitLength(), secRand); + ECPoint q = g.multiply(b).normalize(); + + implAddSubtractMultiplyTwiceEncodingTest(c, q, n); + + implSqrtTest(c); + + implValidityTest(c, g); + } + } + } + + /** + * Calls <code>implTestAddSubtract()</code>, + * <code>implTestMultiply</code> and <code>implTestEncoding</code> for + * the standard elliptic curves as given in <code>SECNamedCurves</code>. + */ + @LargeTest + public void testAddSubtractMultiplyTwiceEncoding() + { + Set names = new HashSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9A = ECNamedCurveTable.getByName(name); + X9ECParameters x9B = CustomNamedCurves.getByName(name); + + if (x9A != null && x9B != null) + { + assertEquals(x9A.getCurve().getField(), x9B.getCurve().getField()); + assertEquals(x9A.getCurve().getA().toBigInteger(), x9B.getCurve().getA().toBigInteger()); + assertEquals(x9A.getCurve().getB().toBigInteger(), x9B.getCurve().getB().toBigInteger()); + assertOptionalValuesAgree(x9A.getCurve().getCofactor(), x9B.getCurve().getCofactor()); + assertOptionalValuesAgree(x9A.getCurve().getOrder(), x9B.getCurve().getOrder()); + + assertPointsEqual("Custom curve base-point inconsistency", x9A.getG(), x9B.getG()); + + assertEquals(x9A.getH(), x9B.getH()); + assertEquals(x9A.getN(), x9B.getN()); + assertOptionalValuesAgree(x9A.getSeed(), x9B.getSeed()); + + BigInteger k = new BigInteger(x9A.getN().bitLength(), secRand); + ECPoint pA = x9A.getG().multiply(k); + ECPoint pB = x9B.getG().multiply(k); + assertPointsEqual("Custom curve multiplication inconsistency", pA, pB); + } + + if (x9A != null) + { + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9A); + } + + if (x9B != null) + { + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9B); + } + } + } + + public void testExampleFpB0() throws Exception + { + /* + * The supersingular curve y^2 = x^3 - 3.x (i.e. with 'B' == 0) from RFC 6508 2.1, with + * curve parameters from RFC 6509 Appendix A. + */ + BigInteger p = fromHex( + "997ABB1F0A563FDA65C61198DAD0657A" + + "416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0" + + "E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1" + + "B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB0" + + "80C5DF10AC7ACE87666D807AFEA85FEB"); + BigInteger a = p.subtract(BigInteger.valueOf(3)); + BigInteger b = BigInteger.valueOf(0); + byte[] S = null; + BigInteger n = p.add(BigInteger.valueOf(1)).shiftRight(2); + BigInteger h = BigInteger.valueOf(4); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h)); + + X9ECPoint G = configureBasepoint(curve, "04" + // Px + + "53FC09EE332C29AD0A7990053ED9B52A" + + "2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E10" + + "43D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1" + + "A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC" + + "80EC46C4967E0979880DC8ABEAE63895" + // Py + + "0A8249063F6009F1F9F1F0533634A135" + + "D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F6" + + "6B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0" + + "AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636" + + "ADB9B5706A67DCDE75573FD71BEF16D7"); + + X9ECParameters x9 = new X9ECParameters(curve, G, n, h, S); + + implAddSubtractMultiplyTwiceEncodingTestAllCoords(x9); + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + // NOTE: We intentionally test points for equality in both directions + assertEquals(message, a, b); + assertEquals(message, b, a); + } + + private void assertOptionalValuesAgree(Object a, Object b) + { + if (a != null && b != null) + { + assertEquals(a, b); + } + } + + private void assertOptionalValuesAgree(byte[] a, byte[] b) + { + if (a != null && b != null) + { + assertTrue(Arrays.areEqual(a, b)); + } + } + + private static X9ECPoint configureBasepoint(ECCurve curve, String encoding) + { + X9ECPoint G = new X9ECPoint(curve, Hex.decode(encoding)); + WNafUtil.configureBasepoint(G.getPoint()); + return G; + } + + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + + private List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + private static BigInteger fromHex( + String hex) + { + return new BigInteger(1, Hex.decode(hex)); + } + + private static ECFieldElement solveQuadraticEquation(ECCurve c, ECFieldElement rhs) + { + if (rhs.isZero()) + { + return rhs; + } + + ECFieldElement gamma, z, zeroElement = c.fromBigInteger(ECConstants.ZERO); + + int m = c.getFieldSize(); + Random rand = new Random(); + do + { + ECFieldElement t = c.fromBigInteger(new BigInteger(m, rand)); + z = zeroElement; + ECFieldElement w = rhs; + for (int i = 1; i < m; i++) + { + ECFieldElement w2 = w.square(); + z = z.square().add(w2.multiply(t)); + w = w2.add(rhs); + } + if (!w.isZero()) + { + return null; + } + gamma = z.square().add(z); + } + while (gamma.isZero()); + + return z; + } + + public static Test suite() + { + return new TestSuite(ECPointTest.class); + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/F2mProofer.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/F2mProofer.java new file mode 100644 index 00000000..062d2091 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/F2mProofer.java @@ -0,0 +1,208 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; + +import com.android.org.bouncycastle.asn1.sec.SECNamedCurves; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.math.ec.ECFieldElement; +import com.android.org.bouncycastle.math.ec.ECPoint; +import com.android.org.bouncycastle.util.BigIntegers; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class F2mProofer +{ + private static final int NUM_SAMPLES = 1000; + + private static final String PATH = "crypto/test/src/com/android/org/bouncycastle/math/ec/test/samples/"; + + private static final String INPUT_FILE_NAME_PREFIX = "Input_"; + + private static final String RESULT_FILE_NAME_PREFIX = "Output_"; + + /** + * The standard curves on which the tests are done + */ + public static final String[] CURVES = { "sect163r2", "sect233r1", + "sect283r1", "sect409r1", "sect571r1" }; + + private String pointToString(ECPoint.F2m p) + { + ECFieldElement.F2m x = (ECFieldElement.F2m) p.getAffineXCoord(); + ECFieldElement.F2m y = (ECFieldElement.F2m) p.getAffineYCoord(); + + int m = x.getM(); + int len = m / 2 + 5; + + StringBuffer sb = new StringBuffer(len); + sb.append('('); + sb.append(x.toBigInteger().toString(16)); + sb.append(", "); + sb.append(y.toBigInteger().toString(16)); + sb.append(')'); + + return sb.toString(); + } + + private void generateRandomInput(X9ECParameters x9ECParameters) + throws NoSuchAlgorithmException, IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG"); + Properties inputProps = new Properties(); + for (int i = 0; i < NUM_SAMPLES; i++) + { + BigInteger rand = BigIntegers.createRandomBigInteger(m, secRand); + inputProps.put(Integer.toString(i), rand.toString(16)); + } + String bits = Integer.toString(m); + FileOutputStream fos = new FileOutputStream(PATH + + INPUT_FILE_NAME_PREFIX + bits + ".properties"); + inputProps.store(fos, "Input Samples of length" + bits); + } + + private void multiplyPoints(X9ECParameters x9ECParameters, + String classPrefix) throws IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + String inputFileName = PATH + INPUT_FILE_NAME_PREFIX + m + + ".properties"; + Properties inputProps = new Properties(); + inputProps.load(new FileInputStream(inputFileName)); + + Properties outputProps = new Properties(); + + for (int i = 0; i < NUM_SAMPLES; i++) + { + BigInteger rand = new BigInteger(inputProps.getProperty(Integer + .toString(i)), 16); + ECPoint.F2m result = (ECPoint.F2m) g.multiply(rand).normalize(); + String resultStr = pointToString(result); + outputProps.setProperty(Integer.toString(i), resultStr); + } + + String outputFileName = PATH + RESULT_FILE_NAME_PREFIX + classPrefix + + "_" + m + ".properties"; + FileOutputStream fos = new FileOutputStream(outputFileName); + outputProps.store(fos, "Output Samples of length" + m); + } + + private Properties loadResults(String classPrefix, int m) + throws IOException + { + FileInputStream fis = new FileInputStream(PATH + + RESULT_FILE_NAME_PREFIX + classPrefix + "_" + m + ".properties"); + Properties res = new Properties(); + res.load(fis); + return res; + + } + + private void compareResult(X9ECParameters x9ECParameters, + String classPrefix1, String classPrefix2) throws IOException + { + ECPoint.F2m g = (ECPoint.F2m) x9ECParameters.getG(); + int m = ((ECFieldElement.F2m) (g.getAffineXCoord())).getM(); + + Properties res1 = loadResults(classPrefix1, m); + Properties res2 = loadResults(classPrefix2, m); + + Set keys = res1.keySet(); + Iterator iter = keys.iterator(); + while (iter.hasNext()) + { + String key = (String) iter.next(); + String result1 = res1.getProperty(key); + String result2 = res2.getProperty(key); + if (!(result1.equals(result2))) + { + System.err.println("Difference found: m = " + m + ", " + + result1 + " does not equal " + result2); + } + } + + } + + private static void usage() + { + System.err.println("Usage: F2mProofer [-init | -multiply <className> " + + "| -compare <className1> <className2>]"); + } + + public static void main(String[] args) throws Exception + { + if (args.length == 0) + { + usage(); + return; + } + F2mProofer proofer = new F2mProofer(); + if (args[0].equals("-init")) + { + System.out.println("Generating random input..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.generateRandomInput(x9ECParameters); + } + System.out + .println("Successfully generated random input in " + PATH); + } + else if (args[0].equals("-compare")) + { + if (args.length < 3) + { + usage(); + return; + } + String classPrefix1 = args[1]; + String classPrefix2 = args[2]; + System.out.println("Comparing results..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.compareResult(x9ECParameters, classPrefix1, + classPrefix2); + } + System.out.println("Successfully compared results in " + PATH); + } + else if (args[0].equals("-multiply")) + { + if (args.length < 2) + { + usage(); + return; + } + String classPrefix = args[1]; + System.out.println("Multiplying points..."); + for (int i = 0; i < CURVES.length; i++) + { + X9ECParameters x9ECParameters = SECNamedCurves + .getByName(CURVES[i]); + proofer.multiplyPoints(x9ECParameters, classPrefix); + } + System.out.println("Successfully generated multiplied points in " + + PATH); + } + else + { + usage(); + } + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/FixedPointTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/FixedPointTest.java new file mode 100644 index 00000000..e72ca170 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/ec/test/FixedPointTest.java @@ -0,0 +1,94 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.ec.test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.android.org.bouncycastle.asn1.x9.ECNamedCurveTable; +import com.android.org.bouncycastle.asn1.x9.X9ECParameters; +import com.android.org.bouncycastle.crypto.ec.CustomNamedCurves; +import com.android.org.bouncycastle.math.ec.ECAlgorithms; +import com.android.org.bouncycastle.math.ec.ECPoint; +import com.android.org.bouncycastle.math.ec.FixedPointCombMultiplier; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class FixedPointTest + extends TestCase +{ + private static final SecureRandom RANDOM = new SecureRandom(); + + private static final int TESTS_PER_CURVE = 5; + + public void testFixedPointMultiplier() + { + final FixedPointCombMultiplier M = new FixedPointCombMultiplier(); + + Set names = new HashSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + + X9ECParameters x9A = ECNamedCurveTable.getByName(name); + X9ECParameters x9B = CustomNamedCurves.getByName(name); + + X9ECParameters x9 = x9B != null ? x9B : x9A; + + for (int i = 0; i < TESTS_PER_CURVE; ++i) + { + BigInteger k = new BigInteger(x9.getN().bitLength(), RANDOM); + ECPoint pRef = ECAlgorithms.referenceMultiply(x9.getG(), k); + + if (x9A != null) + { + ECPoint pA = M.multiply(x9A.getG(), k); + assertPointsEqual("Standard curve fixed-point failure", pRef, pA); + } + + if (x9B != null) + { + ECPoint pB = M.multiply(x9B.getG(), k); + assertPointsEqual("Custom curve fixed-point failure", pRef, pB); + } + } + } + } + + private List enumToList(Enumeration en) + { + List rv = new ArrayList(); + + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + + return rv; + } + + private void assertPointsEqual(String message, ECPoint a, ECPoint b) + { + // NOTE: We intentionally test points for equality in both directions + assertEquals(message, a, b); + assertEquals(message, b, a); + } + + public static Test suite() + { + return new TestSuite(FixedPointTest.class); + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/AllTests.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/AllTests.java new file mode 100644 index 00000000..560dd7d1 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/AllTests.java @@ -0,0 +1,50 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.raw.test; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import com.android.org.bouncycastle.test.PrintTestResult; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("Raw math tests"); + + suite.addTest(InterleaveTest.suite()); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/InterleaveTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/InterleaveTest.java new file mode 100644 index 00000000..355e139a --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/raw/test/InterleaveTest.java @@ -0,0 +1,90 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.raw.test; + +import java.security.SecureRandom; + +import com.android.org.bouncycastle.math.raw.Interleave; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class InterleaveTest extends TestCase +{ + private static final int ITERATIONS = 1000; + + private static final SecureRandom R = new SecureRandom(); + + public void testExpand8To16() + { + // NOTE: Just test all inputs here + for (int iteration = 0; iteration < 256; ++iteration) + { + // NOTE: Implementation is expected to mask input + int x = iteration | (R.nextInt() << 8); + int expected = (int)referenceShuffle(x & 0xFFL); + int actual = Interleave.expand8to16(x); + assertEquals(expected, actual); + } + } + + public void testExpand16To32() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + // NOTE: Implementation is expected to mask input + int x = R.nextInt(); + int expected = (int)referenceShuffle(x & 0xFFFFL); + int actual = Interleave.expand16to32(x); + assertEquals(expected, actual); + } + } + + public void testExpand32To64() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + int x = R.nextInt(); + long expected = referenceShuffle(x & 0xFFFFFFFFL); + long actual = Interleave.expand32to64(x); + assertEquals(expected, actual); + } + } + + public void testExpand64To128() + { + for (int iteration = 0; iteration < ITERATIONS; ++iteration) + { + long x = R.nextLong(); + long expected = referenceShuffle(x); + long[] actual = new long[9]; + int offset = iteration % 8; + // NOTE: Implementation must overwrite existing values + actual[offset ] = R.nextLong(); + actual[offset + 1] = R.nextLong(); + Interleave.expand64To128(x, actual, offset); + assertEquals((expected ) & 0x5555555555555555L, actual[offset ]); + assertEquals((expected >>> 1) & 0x5555555555555555L, actual[offset + 1]); + } + } + + public static Test suite() + { + return new TestSuite(InterleaveTest.class); + } + + private static long referenceShuffle(long x) + { + long result = 0, y = x >>> 32; + for (int bit = 0; bit < 32; ++bit) + { + long selector = 1L << bit; + result |= ((x & selector) << (bit )); + result |= ((y & selector) << (bit + 1)); + } + return result; + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/AllTests.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/AllTests.java new file mode 100644 index 00000000..34377f2d --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/AllTests.java @@ -0,0 +1,50 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.test; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import com.android.org.bouncycastle.test.PrintTestResult; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class AllTests + extends TestCase +{ + public static void main (String[] args) + throws Exception + { + PrintTestResult.printResult( junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + throws Exception + { + TestSuite suite = new TestSuite("Math tests"); + + suite.addTestSuite(PrimesTest.class); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + + } + + protected void tearDown() + { + + } + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/PrimesTest.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/PrimesTest.java new file mode 100644 index 00000000..f690484f --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/math/test/PrimesTest.java @@ -0,0 +1,187 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.math.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import com.android.org.bouncycastle.crypto.Digest; +import com.android.org.bouncycastle.crypto.digests.SHA1Digest; +import com.android.org.bouncycastle.crypto.digests.SHA256Digest; +import com.android.org.bouncycastle.math.Primes; +import com.android.org.bouncycastle.math.Primes.MROutput; +import com.android.org.bouncycastle.math.Primes.STOutput; +import com.android.org.bouncycastle.util.Arrays; +import com.android.org.bouncycastle.util.BigIntegers; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class PrimesTest extends TestCase +{ + private static final int ITERATIONS = 10; + private static final int PRIME_BITS = 256; + private static final int PRIME_CERTAINTY = 100; + + private static final BigInteger TWO = BigInteger.valueOf(2); + + private static final SecureRandom R = new SecureRandom(); + + public void testHasAnySmallFactors() + { + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertFalse(Primes.hasAnySmallFactors(prime)); + + // NOTE: Loop through ALL small values to be sure no small primes are missing + for (int smallFactor = 2; smallFactor <= Primes.SMALL_FACTOR_LIMIT; ++smallFactor) + { + BigInteger nonPrimeWithSmallFactor = BigInteger.valueOf(smallFactor).multiply(prime); + assertTrue(Primes.hasAnySmallFactors(nonPrimeWithSmallFactor)); + } + } + } + + public void testEnhancedMRProbablePrime() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + MROutput mr = Primes.enhancedMRProbablePrimeTest(prime, R, mrIterations); + assertFalse(mr.isProvablyComposite()); + assertFalse(mr.isNotPrimePower()); + assertNull(mr.getFactor()); + + BigInteger primePower = prime; + for (int i = 0; i <= (iterations % 8); ++i) + { + primePower = primePower.multiply(prime); + } + + MROutput mr2 = Primes.enhancedMRProbablePrimeTest(primePower, R, mrIterations); + assertTrue(mr2.isProvablyComposite()); + assertFalse(mr2.isNotPrimePower()); + assertEquals(mr2.getFactor(), prime); + + BigInteger nonPrimePower = randomPrime().multiply(prime); + MROutput mr3 = Primes.enhancedMRProbablePrimeTest(nonPrimePower, R, mrIterations); + assertTrue(mr3.isProvablyComposite()); + assertTrue(mr3.isNotPrimePower()); + assertNull(mr.getFactor()); + } + } + + public void testMRProbablePrime() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertTrue(Primes.isMRProbablePrime(prime, R, mrIterations)); + + BigInteger nonPrime = randomPrime().multiply(prime); + assertFalse(Primes.isMRProbablePrime(nonPrime, R, mrIterations)); + } + } + + public void testMRProbablePrimeToBase() + { + int mrIterations = (PRIME_CERTAINTY + 1) / 2; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + BigInteger prime = randomPrime(); + assertTrue(referenceIsMRProbablePrime(prime, mrIterations)); + + BigInteger nonPrime = randomPrime().multiply(prime); + assertFalse(referenceIsMRProbablePrime(nonPrime, mrIterations)); + } + } + + public void testSTRandomPrime() + { + Digest[] digests = new Digest[]{ new SHA1Digest(), new SHA256Digest() }; + for (int digestIndex = 0; digestIndex < digests.length; ++digestIndex) + { + int coincidenceCount = 0; + + Digest digest = digests[digestIndex]; + for (int iterations = 0; iterations < ITERATIONS; ++iterations) + { + try + { + byte[] inputSeed = new byte[16]; + R.nextBytes(inputSeed); + + STOutput st = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertTrue(isPrime(st.getPrime())); + + STOutput st2 = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertEquals(st.getPrime(), st2.getPrime()); + assertEquals(st.getPrimeGenCounter(), st2.getPrimeGenCounter()); + assertTrue(Arrays.areEqual(st.getPrimeSeed(), st2.getPrimeSeed())); + + for (int i = 0; i < inputSeed.length; ++i) + { + inputSeed[i] ^= 0xFF; + } + + STOutput st3 = Primes.generateSTRandomPrime(digest, PRIME_BITS, inputSeed); + assertTrue(!st.getPrime().equals(st3.getPrime())); + assertFalse(Arrays.areEqual(st.getPrimeSeed(), st3.getPrimeSeed())); + + if (st.getPrimeGenCounter() == st3.getPrimeGenCounter()) + { + ++coincidenceCount; + } + } + catch (IllegalStateException e) + { + if (e.getMessage().startsWith("Too many iterations")) + { + --iterations; + continue; + } + + throw e; + } + } + + assertTrue(coincidenceCount * coincidenceCount < ITERATIONS); + } + } + + public static Test suite() + { + return new TestSuite(PrimesTest.class); + } + + private static boolean referenceIsMRProbablePrime(BigInteger x, int numBases) + { + BigInteger xSubTwo = x.subtract(TWO); + + for (int i = 0; i < numBases; ++i) + { + BigInteger b = BigIntegers.createRandomInRange(TWO, xSubTwo, R); + if (!Primes.isMRProbablePrimeToBase(x, b)) + { + return false; + } + } + + return true; + } + + private static boolean isPrime(BigInteger x) + { + return x.isProbablePrime(PRIME_CERTAINTY); + } + + private static BigInteger randomPrime() + { + return BigIntegers.createRandomPrime(PRIME_BITS, PRIME_CERTAINTY, R); + } +} diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/test/PrintTestResult.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/test/PrintTestResult.java new file mode 100644 index 00000000..83f266f6 --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/test/PrintTestResult.java @@ -0,0 +1,40 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.test; + + +import java.util.Enumeration; + +import junit.framework.TestResult; + +/** + * @hide This class is not part of the Android public SDK API + */ +public class PrintTestResult +{ + public static void printResult(TestResult result) + { + Enumeration e = result.failures(); + if (e != null) + { + while (e.hasMoreElements()) + { + System.out.println(e.nextElement()); + } + } + + e = result.errors(); + if (e != null) + { + while (e.hasMoreElements()) + { + System.out.println(e.nextElement()); + } + } + + if (!result.wasSuccessful()) + { + System.exit(1); + } + } +} + diff --git a/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/util/Times.java b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/util/Times.java new file mode 100644 index 00000000..05ca4b1b --- /dev/null +++ b/repackaged/bcprov/src/test/java/com/android/org/bouncycastle/util/Times.java @@ -0,0 +1,13 @@ +/* GENERATED SOURCE. DO NOT MODIFY. */ +package com.android.org.bouncycastle.util; + +/** + * @hide This class is not part of the Android public SDK API + */ +public final class Times +{ + public static long nanoTime() + { + return System.nanoTime(); + } +} diff --git a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 7eaf8ac8..50417c7a 100644 --- a/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/repackaged_platform/bcprov/src/main/java/com/android/internal/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -961,56 +961,26 @@ public class BaseBlockCipher { byte[] iv = new byte[ivLength]; - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // ivRandom.nextBytes(iv); + // rejected outright in BC 1.54 (even if an IV was passed in params). + // See b/27995180 for historical details. if (!isBCPBEKeyWithoutIV(key)) { ivRandom.nextBytes(iv); } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // END Android-changed: Reject PBE keys with no IV param = new ParametersWithIV(param, iv); ivParam = (ParametersWithIV)param; } else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) { - // BEGIN Android-changed: For PBE keys with no IV, log and use IV of 0 + // BEGIN Android-changed: Reject PBE keys with no IV // These keys were accepted in BC 1.52 (and treated as having an IV of 0) but - // rejected outright in BC 1.54 (even if an IV was passed in params). We - // want the eventual state to be that an IV can be passed in params, but the key - // is rejected otherwise. For now, log that these will be rejected in a future - // release. See b/27995180 for historical details. - // throw new InvalidAlgorithmParameterException("no IV set when one expected"); - if (!isBCPBEKeyWithoutIV(key)) { - throw new InvalidAlgorithmParameterException("no IV set when one expected"); - } else { - // TODO(b/70275132): Change to rejecting these keys - System.err.println(" ******** DEPRECATED FUNCTIONALITY ********"); - System.err.println(" * You have initialized a cipher with a PBE key with no IV and"); - System.err.println(" * have not provided an IV in the AlgorithmParameterSpec. This"); - System.err.println(" * configuration is deprecated. The cipher will be initialized"); - System.err.println(" * with an all-zero IV, but in a future release this call will"); - System.err.println(" * throw an exception."); - new InvalidAlgorithmParameterException("No IV set when using PBE key") - .printStackTrace(System.err); - // Mimic behaviour in 1.52 by using an IV of 0's - param = new ParametersWithIV(param, new byte[ivLength]); - ivParam = (ParametersWithIV)param; - } - // END Android-changed: For PBE keys with no IV, log and use IV of 0 + // rejected outright in BC 1.54 (even if an IV was passed in params). + throw new InvalidAlgorithmParameterException("No IV set when using PBE key"); + // END Android-changed: Reject PBE keys with no IV } } diff --git a/srcgen/generate_android_src.sh b/srcgen/generate_android_src.sh index 6498bc79..6bd894b3 100755 --- a/srcgen/generate_android_src.sh +++ b/srcgen/generate_android_src.sh @@ -31,6 +31,7 @@ DEFAULT_CONSTRUCTORS_FILE=${BOUNCY_CASTLE_DIR}/srcgen/default-constructors.txt SOURCE_DIRS="\ src/main/java \ + src/test/java \ " # Repackage the project's source. |