| /* | |
| * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * @test id=IntegerPolynomial25519 | |
| * @bug 8181594 8208648 | |
| * @summary Test proper operation of integer field arithmetic | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 32 0 | |
| */ | |
| /* | |
| * @test id=IntegerPolynomial448 | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial448 56 1 | |
| */ | |
| /* | |
| * @test id=IntegerPolynomial1305 | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial1305 16 2 | |
| */ | |
| /* | |
| * @test id=IntegerPolynomialP256 | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP256 32 5 | |
| */ | |
| /* | |
| * @test id=IntegerPolynomialP384 | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP384 48 6 | |
| */ | |
| /* | |
| * @test id=IntegerPolynomialP521 | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP521 66 7 | |
| */ | |
| /* | |
| * @test id=P256OrderField | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.P256OrderField 32 8 | |
| */ | |
| /* | |
| * @test id=P384OrderField | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.P384OrderField 48 9 | |
| */ | |
| /* | |
| * @test id=P521OrderField | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.P521OrderField 66 10 | |
| */ | |
| /* | |
| * @test id=Curve25519OrderField | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve25519OrderField 32 11 | |
| */ | |
| /* | |
| * @test id=Curve448OrderField | |
| * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly | |
| * @build BigIntegerModuloP | |
| * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve448OrderField 56 12 | |
| */ | |
| import sun.security.util.math.*; | |
| import sun.security.util.math.intpoly.*; | |
| import java.util.function.*; | |
| import java.util.*; | |
| import java.math.*; | |
| import java.nio.*; | |
| public class TestIntegerModuloP { | |
| static BigInteger TWO = BigInteger.valueOf(2); | |
| // The test has a list of functions, and it selects randomly from that list | |
| // The function types | |
| interface ElemFunction extends BiFunction | |
| <MutableIntegerModuloP, IntegerModuloP, IntegerModuloP> { } | |
| interface ElemArrayFunction extends BiFunction | |
| <MutableIntegerModuloP, IntegerModuloP, byte[]> { } | |
| interface TriConsumer <T, U, V> { | |
| void accept(T t, U u, V v); | |
| } | |
| interface ElemSetFunction extends TriConsumer | |
| <MutableIntegerModuloP, IntegerModuloP, byte[]> { } | |
| // The lists of functions. Multiple lists are needed because the test | |
| // respects the limitations of the arithmetic implementations. | |
| static final List<ElemFunction> ADD_FUNCTIONS = new ArrayList<>(); | |
| static final List<ElemFunction> MULT_FUNCTIONS = new ArrayList<>(); | |
| static final List<ElemArrayFunction> ARRAY_FUNCTIONS = new ArrayList<>(); | |
| static final List<ElemSetFunction> SET_FUNCTIONS = new ArrayList<>(); | |
| static void setUpFunctions(IntegerFieldModuloP field, int length) { | |
| ADD_FUNCTIONS.clear(); | |
| MULT_FUNCTIONS.clear(); | |
| SET_FUNCTIONS.clear(); | |
| ARRAY_FUNCTIONS.clear(); | |
| byte highByte = (byte) | |
| (field.getSize().bitLength() > length * 8 ? 1 : 0); | |
| // add functions are (im)mutable add/subtract | |
| ADD_FUNCTIONS.add(IntegerModuloP::add); | |
| ADD_FUNCTIONS.add(IntegerModuloP::subtract); | |
| ADD_FUNCTIONS.add(MutableIntegerModuloP::setSum); | |
| ADD_FUNCTIONS.add(MutableIntegerModuloP::setDifference); | |
| // also include functions that return the first/second argument | |
| ADD_FUNCTIONS.add((a, b) -> a); | |
| ADD_FUNCTIONS.add((a, b) -> b); | |
| // mult functions are (im)mutable multiply and square | |
| MULT_FUNCTIONS.add(IntegerModuloP::multiply); | |
| MULT_FUNCTIONS.add((a, b) -> a.square()); | |
| MULT_FUNCTIONS.add((a, b) -> b.square()); | |
| MULT_FUNCTIONS.add(MutableIntegerModuloP::setProduct); | |
| MULT_FUNCTIONS.add((a, b) -> a.setSquare()); | |
| // also test multiplication by a small value | |
| MULT_FUNCTIONS.add((a, b) -> a.setProduct(b.getField().getSmallValue( | |
| b.asBigInteger().mod(BigInteger.valueOf(262144)).intValue()))); | |
| // set functions are setValue with various argument types | |
| SET_FUNCTIONS.add((a, b, c) -> a.setValue(b)); | |
| SET_FUNCTIONS.add((a, b, c) -> | |
| a.setValue(c, 0, c.length, (byte) 0)); | |
| SET_FUNCTIONS.add((a, b, c) -> | |
| a.setValue(c, 0, c.length / 2, (byte) 0)); | |
| SET_FUNCTIONS.add((a, b, c) -> | |
| a.setValue(ByteBuffer.wrap(c, 0, c.length / 2).order(ByteOrder.LITTLE_ENDIAN), | |
| c.length / 2, highByte)); | |
| // array functions return the (possibly modified) value as byte array | |
| ARRAY_FUNCTIONS.add((a, b ) -> a.asByteArray(length)); | |
| ARRAY_FUNCTIONS.add((a, b) -> a.addModPowerTwo(b, length)); | |
| } | |
| public static void main(String[] args) { | |
| String className = args[0]; | |
| final int length = Integer.parseInt(args[1]); | |
| int seed = Integer.parseInt(args[2]); | |
| try { | |
| Class<?> clazz = Class.forName(className); | |
| IntegerFieldModuloP field = (IntegerFieldModuloP) | |
| clazz.getDeclaredField("ONE").get(null); | |
| setUpFunctions(field, length); | |
| runFieldTest(field, length, seed); | |
| } catch (Exception ex) { | |
| throw new RuntimeException(ex); | |
| } | |
| System.out.println("All tests passed"); | |
| } | |
| static void assertEqual(IntegerModuloP e1, IntegerModuloP e2) { | |
| if (!e1.asBigInteger().equals(e2.asBigInteger())) { | |
| throw new RuntimeException("values not equal: " | |
| + e1.asBigInteger() + " != " + e2.asBigInteger()); | |
| } | |
| } | |
| // A class that holds pairs of actual/expected values, and allows | |
| // computation on these pairs. | |
| static class TestPair<T extends IntegerModuloP> { | |
| private final T test; | |
| private final T baseline; | |
| public TestPair(T test, T baseline) { | |
| this.test = test; | |
| this.baseline = baseline; | |
| } | |
| public T getTest() { | |
| return test; | |
| } | |
| public T getBaseline() { | |
| return baseline; | |
| } | |
| private void assertEqual() { | |
| TestIntegerModuloP.assertEqual(test, baseline); | |
| } | |
| public TestPair<MutableIntegerModuloP> mutable() { | |
| return new TestPair<>(test.mutable(), baseline.mutable()); | |
| } | |
| public | |
| <R extends IntegerModuloP, X extends IntegerModuloP> | |
| TestPair<X> apply(BiFunction<T, R, X> func, TestPair<R> right) { | |
| X testResult = func.apply(test, right.test); | |
| X baselineResult = func.apply(baseline, right.baseline); | |
| return new TestPair(testResult, baselineResult); | |
| } | |
| public | |
| <U extends IntegerModuloP, V> | |
| void apply(TriConsumer<T, U, V> func, TestPair<U> right, V argV) { | |
| func.accept(test, right.test, argV); | |
| func.accept(baseline, right.baseline, argV); | |
| } | |
| public | |
| <R extends IntegerModuloP> | |
| void applyAndCheckArray(BiFunction<T, R, byte[]> func, | |
| TestPair<R> right) { | |
| byte[] testResult = func.apply(test, right.test); | |
| byte[] baselineResult = func.apply(baseline, right.baseline); | |
| if (!Arrays.equals(testResult, baselineResult)) { | |
| throw new RuntimeException("Array values do not match: " | |
| + HexFormat.of().withUpperCase().formatHex(testResult) + " != " | |
| + HexFormat.of().withUpperCase().formatHex(baselineResult)); | |
| } | |
| } | |
| } | |
| static TestPair<IntegerModuloP> | |
| applyAndCheck(ElemFunction func, TestPair<MutableIntegerModuloP> left, | |
| TestPair<IntegerModuloP> right) { | |
| TestPair<IntegerModuloP> result = left.apply(func, right); | |
| result.assertEqual(); | |
| left.assertEqual(); | |
| right.assertEqual(); | |
| return result; | |
| } | |
| static void | |
| setAndCheck(ElemSetFunction func, TestPair<MutableIntegerModuloP> left, | |
| TestPair<IntegerModuloP> right, byte[] argV) { | |
| left.apply(func, right, argV); | |
| left.assertEqual(); | |
| right.assertEqual(); | |
| } | |
| static TestPair<MutableIntegerModuloP> | |
| applyAndCheckMutable(ElemFunction func, | |
| TestPair<MutableIntegerModuloP> left, | |
| TestPair<IntegerModuloP> right) { | |
| TestPair<IntegerModuloP> result = applyAndCheck(func, left, right); | |
| TestPair<MutableIntegerModuloP> mutableResult = result.mutable(); | |
| mutableResult.assertEqual(); | |
| result.assertEqual(); | |
| left.assertEqual(); | |
| right.assertEqual(); | |
| return mutableResult; | |
| } | |
| static void | |
| cswapAndCheck(int swap, TestPair<MutableIntegerModuloP> left, | |
| TestPair<MutableIntegerModuloP> right) { | |
| left.getTest().conditionalSwapWith(right.getTest(), swap); | |
| left.getBaseline().conditionalSwapWith(right.getBaseline(), swap); | |
| left.assertEqual(); | |
| right.assertEqual(); | |
| } | |
| // Request arithmetic that should overflow, and ensure that overflow is | |
| // detected. | |
| static void runOverflowTest(TestPair<IntegerModuloP> elem) { | |
| TestPair<MutableIntegerModuloP> mutableElem = elem.mutable(); | |
| try { | |
| for (int i = 0; i < 1000; i++) { | |
| applyAndCheck(MutableIntegerModuloP::setSum, mutableElem, elem); | |
| } | |
| applyAndCheck(MutableIntegerModuloP::setProduct, mutableElem, elem); | |
| } catch (ArithmeticException ex) { | |
| // this is expected | |
| } | |
| mutableElem = elem.mutable(); | |
| try { | |
| for (int i = 0; i < 1000; i++) { | |
| elem = applyAndCheck(IntegerModuloP::add, | |
| mutableElem, elem); | |
| } | |
| applyAndCheck(IntegerModuloP::multiply, mutableElem, elem); | |
| } catch (ArithmeticException ex) { | |
| // this is expected | |
| } | |
| } | |
| // Run a large number of random operations and ensure that | |
| // results are correct | |
| static void runOperationsTest(Random random, int length, | |
| TestPair<IntegerModuloP> elem, | |
| TestPair<IntegerModuloP> right) { | |
| TestPair<MutableIntegerModuloP> left = elem.mutable(); | |
| for (int i = 0; i < 10000; i++) { | |
| ElemFunction addFunc1 = | |
| ADD_FUNCTIONS.get(random.nextInt(ADD_FUNCTIONS.size())); | |
| TestPair<MutableIntegerModuloP> result1 = | |
| applyAndCheckMutable(addFunc1, left, right); | |
| // left could have been modified, so turn it back into a summand | |
| applyAndCheckMutable((a, b) -> a.setSquare(), left, right); | |
| ElemFunction addFunc2 = | |
| ADD_FUNCTIONS.get(random.nextInt(ADD_FUNCTIONS.size())); | |
| TestPair<IntegerModuloP> result2 = | |
| applyAndCheck(addFunc2, left, right); | |
| if (elem.test.getField() instanceof IntegerPolynomial) { | |
| IntegerPolynomial field = | |
| (IntegerPolynomial) elem.test.getField(); | |
| int numAdds = 10; // check for addition overflow | |
| for (int j = 1; j < numAdds; j++) { | |
| ElemFunction addFunc3 = ADD_FUNCTIONS. | |
| get(random.nextInt(ADD_FUNCTIONS.size())); | |
| result2 = applyAndCheck(addFunc3, left, right); | |
| } | |
| } | |
| ElemFunction multFunc2 = | |
| MULT_FUNCTIONS.get(random.nextInt(MULT_FUNCTIONS.size())); | |
| TestPair<MutableIntegerModuloP> multResult = | |
| applyAndCheckMutable(multFunc2, result1, result2); | |
| int swap = random.nextInt(2); | |
| cswapAndCheck(swap, left, multResult); | |
| ElemSetFunction setFunc = | |
| SET_FUNCTIONS.get(random.nextInt(SET_FUNCTIONS.size())); | |
| byte[] valueArr = new byte[2 * length]; | |
| random.nextBytes(valueArr); | |
| setAndCheck(setFunc, result1, result2, valueArr); | |
| // left could have been modified, so to turn it back into a summand | |
| applyAndCheckMutable((a, b) -> a.setSquare(), left, right); | |
| ElemArrayFunction arrayFunc = | |
| ARRAY_FUNCTIONS.get(random.nextInt(ARRAY_FUNCTIONS.size())); | |
| left.applyAndCheckArray(arrayFunc, right); | |
| } | |
| } | |
| // Run all the tests for a given field | |
| static void runFieldTest(IntegerFieldModuloP testField, | |
| int length, int seed) { | |
| System.out.println("Testing: " + testField.getClass().getSimpleName()); | |
| Random random = new Random(seed); | |
| IntegerFieldModuloP baselineField = | |
| new BigIntegerModuloP(testField.getSize()); | |
| int numBits = testField.getSize().bitLength(); | |
| BigInteger r = | |
| new BigInteger(numBits, random).mod(testField.getSize()); | |
| TestPair<IntegerModuloP> rand = | |
| new TestPair(testField.getElement(r), baselineField.getElement(r)); | |
| runOverflowTest(rand); | |
| // check combinations of operations for different kinds of elements | |
| List<TestPair<IntegerModuloP>> testElements = new ArrayList<>(); | |
| testElements.add(rand); | |
| testElements.add(new TestPair(testField.get0(), baselineField.get0())); | |
| testElements.add(new TestPair(testField.get1(), baselineField.get1())); | |
| byte[] testArr = {121, 37, -100, -5, 76, 33}; | |
| testElements.add(new TestPair(testField.getElement(testArr), | |
| baselineField.getElement(testArr))); | |
| testArr = new byte[length]; | |
| random.nextBytes(testArr); | |
| testElements.add(new TestPair(testField.getElement(testArr), | |
| baselineField.getElement(testArr))); | |
| random.nextBytes(testArr); | |
| byte highByte = (byte) (numBits > length * 8 ? 1 : 0); | |
| testElements.add( | |
| new TestPair( | |
| testField.getElement(testArr, 0, testArr.length, highByte), | |
| baselineField.getElement(testArr, 0, testArr.length, highByte) | |
| ) | |
| ); | |
| for (int i = 0; i < testElements.size(); i++) { | |
| for (int j = 0; j < testElements.size(); j++) { | |
| runOperationsTest(random, length, testElements.get(i), | |
| testElements.get(j)); | |
| } | |
| } | |
| } | |
| } | |