1 //
   2 // Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
   3 // Copyright (c) 2015, Red Hat Inc. All rights reserved.
   4 // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 //
   6 // This code is free software; you can redistribute it and/or modify it
   7 // under the terms of the GNU General Public License version 2 only, as
   8 // published by the Free Software Foundation.
   9 //
  10 // This code is distributed in the hope that it will be useful, but WITHOUT
  11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13 // version 2 for more details (a copy is included in the LICENSE file that
  14 // accompanied this code).
  15 //
  16 // You should have received a copy of the GNU General Public License version
  17 // 2 along with this work; if not, write to the Free Software Foundation,
  18 // Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19 //
  20 // Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21 // or visit www.oracle.com if you need additional information or have any
  22 // questions.
  23 //
  24 //
  25 
  26 import java.lang.invoke.MethodHandle;
  27 import java.lang.invoke.MethodHandles;
  28 import java.lang.invoke.MethodType;
  29 import java.lang.reflect.Constructor;
  30 import java.lang.reflect.Executable;
  31 import java.lang.reflect.Field;
  32 import java.lang.reflect.Method;
  33 import java.math.BigInteger;
  34 import java.util.Arrays;
  35 import java.util.Random;
  36 
  37 import sun.hotspot.WhiteBox;
  38 
  39 import jdk.test.lib.Platform;
  40 
  41 /**
  42  * @test
  43  * @bug 8130150 8131779 8139907
  44  * @summary Verify that the Montgomery multiply and square intrinsic works and correctly checks their arguments.
  45  * @library /testlibrary /../../test/lib
  46  * @library /testlibrary
  47  * @build MontgomeryMultiplyTest
  48  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  49  *                              sun.hotspot.WhiteBox$WhiteBoxPermission
  50  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI MontgomeryMultiplyTest
  51  */
  52 
  53 public class MontgomeryMultiplyTest {
  54 
  55     private static final WhiteBox wb = WhiteBox.getWhiteBox();
  56 
  57     /* Compilation level corresponding to C2. */
  58     private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
  59 
  60     static final MethodHandles.Lookup lookup = MethodHandles.lookup();
  61 
  62     static final MethodHandle montgomeryMultiplyHandle, montgomerySquareHandle;
  63     static final MethodHandle bigIntegerConstructorHandle;
  64     static final Field bigIntegerMagField;
  65 
  66     static {
  67        // Use reflection to gain access to the methods we want to test.
  68         try {
  69             Method m = BigInteger.class.getDeclaredMethod("montgomeryMultiply",
  70                 /*a*/int[].class, /*b*/int[].class, /*n*/int[].class, /*len*/int.class,
  71                 /*inv*/long.class, /*product*/int[].class);
  72             m.setAccessible(true);
  73             montgomeryMultiplyHandle = lookup.unreflect(m);
  74 
  75             m = BigInteger.class.getDeclaredMethod("montgomerySquare",
  76                 /*a*/int[].class, /*n*/int[].class, /*len*/int.class,
  77                 /*inv*/long.class, /*product*/int[].class);
  78             m.setAccessible(true);
  79             montgomerySquareHandle = lookup.unreflect(m);
  80 
  81             Constructor c
  82                 = BigInteger.class.getDeclaredConstructor(int.class, int[].class);
  83             c.setAccessible(true);
  84             bigIntegerConstructorHandle = lookup.unreflectConstructor(c);
  85 
  86             bigIntegerMagField = BigInteger.class.getDeclaredField("mag");
  87             bigIntegerMagField.setAccessible(true);
  88 
  89         } catch (Throwable ex) {
  90             throw new RuntimeException(ex);
  91         }
  92     }
  93 
  94     /* Obtain executable for the intrinsics tested. Depending on the
  95      * value of 'isMultiply', the executable corresponding to either
  96      * implMontgomerMultiply or implMontgomerySqure is returned. */
  97     static Executable getExecutable(boolean isMultiply) throws RuntimeException {
  98         try {
  99             Class aClass = Class.forName("java.math.BigInteger");
 100             Method aMethod;
 101             if (isMultiply) {
 102                 aMethod = aClass.getDeclaredMethod("implMontgomeryMultiply",
 103                                                    int[].class,
 104                                                    int[].class,
 105                                                    int[].class,
 106                                                    int.class,
 107                                                    long.class,
 108                                                    int[].class);
 109             } else {
 110                 aMethod = aClass.getDeclaredMethod("implMontgomerySquare",
 111                                                    int[].class,
 112                                                    int[].class,
 113                                                    int.class,
 114                                                    long.class,
 115                                                    int[].class);
 116             }
 117             return aMethod;
 118         } catch (NoSuchMethodException e) {
 119             throw new RuntimeException("Test bug, method is unavailable. " + e);
 120         } catch (ClassNotFoundException e) {
 121             throw new RuntimeException("Test bug, class is unavailable. " + e);
 122         }
 123     }
 124 
 125     // Invoke either BigInteger.montgomeryMultiply or BigInteger.montgomerySquare.
 126     int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,
 127                              int[] product) throws Throwable {
 128         int[] result =
 129             (a == b) ? (int[]) montgomerySquareHandle.invokeExact(a, n, len, inv, product)
 130                      : (int[]) montgomeryMultiplyHandle.invokeExact(a, b, n, len, inv, product);
 131         return Arrays.copyOf(result, len);
 132     }
 133 
 134     // Invoke the private constructor BigInteger(int[]).
 135     BigInteger newBigInteger(int[] val) throws Throwable {
 136         return (BigInteger) bigIntegerConstructorHandle.invokeExact(1, val);
 137     }
 138 
 139     // Get the private field BigInteger.mag
 140     int[] mag(BigInteger n) {
 141         try {
 142             return (int[]) bigIntegerMagField.get(n);
 143         } catch (Exception ex) {
 144             throw new RuntimeException(ex);
 145         }
 146     }
 147 
 148     // Montgomery multiplication
 149     // Calculate a * b * r^-1 mod n)
 150     //
 151     // R is a power of the word size
 152     // N' = R^-1 mod N
 153     //
 154     // T := ab
 155     // m := (T mod R)N' mod R [so 0 <= m < R]
 156     // t := (T + mN)/R
 157     // if t >= N then return t - N else return t
 158     //
 159     BigInteger montgomeryMultiply(BigInteger a, BigInteger b, BigInteger N,
 160             int len, BigInteger n_prime)
 161             throws Throwable {
 162         BigInteger T = a.multiply(b);
 163         BigInteger R = BigInteger.ONE.shiftLeft(len*32);
 164         BigInteger mask = R.subtract(BigInteger.ONE);
 165         BigInteger m = (T.and(mask)).multiply(n_prime);
 166         m = m.and(mask); // i.e. m.mod(R)
 167         T = T.add(m.multiply(N));
 168         T = T.shiftRight(len*32); // i.e. T.divide(R)
 169         if (T.compareTo(N) > 0) {
 170             T = T.subtract(N);
 171         }
 172         return T;
 173     }
 174 
 175     // Call the Montgomery multiply intrinsic.
 176     BigInteger montgomeryMultiply(int[] a_words, int[] b_words, int[] n_words,
 177             int len, BigInteger inv)
 178             throws Throwable {
 179         BigInteger t = montgomeryMultiply(
 180                 newBigInteger(a_words),
 181                 newBigInteger(b_words),
 182                 newBigInteger(n_words),
 183                 len, inv);
 184         return t;
 185     }
 186 
 187     // Check that the Montgomery multiply intrinsic returns the same
 188     // result as the longhand calculation.
 189     void check(int[] a_words, int[] b_words, int[] n_words, int len, BigInteger inv)
 190             throws Throwable {
 191         BigInteger n = newBigInteger(n_words);
 192         BigInteger slow = montgomeryMultiply(a_words, b_words, n_words, len, inv);
 193         BigInteger fast
 194             = newBigInteger(montgomeryMultiply
 195                             (a_words, b_words, n_words, len, inv.longValue(), null));
 196         // The intrinsic may not return the same value as the longhand
 197         // calculation but they must have the same residue mod N.
 198         if (!slow.mod(n).equals(fast.mod(n))) {
 199             throw new RuntimeException();
 200         }
 201     }
 202 
 203     Random rnd = new Random(0);
 204 
 205     // Return a random value of length <= bits in an array of even length
 206     int[] random_val(int bits) {
 207         int len = (bits+63)/64;  // i.e. length in longs
 208         int[] val = new int[len*2];
 209         for (int i = 0; i < val.length; i++)
 210             val[i] = rnd.nextInt();
 211         int leadingZeros = 64 - (bits & 64);
 212         if (leadingZeros >= 32) {
 213             val[0] = 0;
 214             val[1] &= ~(-1l << (leadingZeros & 31));
 215         } else {
 216             val[0] &= ~(-1l << leadingZeros);
 217         }
 218         return val;
 219     }
 220 
 221     void testOneLength(int lenInBits, int lenInInts) throws Throwable {
 222         BigInteger mod = new BigInteger(lenInBits, 2, rnd);
 223         BigInteger r = BigInteger.ONE.shiftLeft(lenInInts * 32);
 224         BigInteger n_prime = mod.modInverse(r).negate();
 225 
 226         // Make n.length even, padding with a zero if necessary
 227         int[] n = mag(mod);
 228         if (n.length < lenInInts) {
 229             int[] x = new int[lenInInts];
 230             System.arraycopy(n, 0, x, lenInInts-n.length, n.length);
 231             n = x;
 232         }
 233 
 234         for (int i = 0; i < 10000; i++) {
 235             // multiply
 236             check(random_val(lenInBits), random_val(lenInBits), n, lenInInts, n_prime);
 237             // square
 238             int[] tmp = random_val(lenInBits);
 239             check(tmp, tmp, n, lenInInts, n_prime);
 240         }
 241     }
 242 
 243     // Test the Montgomery multiply intrinsic with a bunch of random
 244     // values of varying lengths.  Do this for long enough that the
 245     // caller of the intrinsic is C2-compiled.
 246     void testResultValues() throws Throwable {
 247         // Test a couple of interesting edge cases.
 248         testOneLength(1024, 32);
 249         testOneLength(1025, 34);
 250         for (int j = 10; j > 0; j--) {
 251             // Construct a random prime whose length in words is even
 252             int lenInBits = rnd.nextInt(2048) + 64;
 253             int lenInInts = (lenInBits + 63)/64*2;
 254             testOneLength(lenInBits, lenInInts);
 255         }
 256     }
 257 
 258     // Range checks
 259     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, int[] n, int len, long inv,
 260                                         int[] product, Class klass) {
 261         try {
 262             montgomeryMultiply(a, b, n, len, inv, product);
 263         } catch (Throwable ex) {
 264             if (klass.isAssignableFrom(ex.getClass()))
 265                 return;
 266             throw new RuntimeException(klass + " expected, " + ex + " was thrown");
 267         }
 268         throw new RuntimeException(klass + " expected, was not thrown");
 269     }
 270 
 271     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,
 272             Class klass) {
 273         testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), null, klass);
 274     }
 275 
 276     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,
 277             int[] product, Class klass) {
 278         testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), product, klass);
 279     }
 280 
 281     void testMontgomeryMultiplyChecks() {
 282         int[] blah = random_val(40);
 283         int[] small = random_val(39);
 284         BigInteger mod = new BigInteger(40*32 , 2, rnd);
 285         BigInteger r = BigInteger.ONE.shiftLeft(40*32);
 286         BigInteger n_prime = mod.modInverse(r).negate();
 287 
 288         // Length out of range: square
 289         testOneMontgomeryMultiplyCheck(blah, blah, mod, 41, n_prime, IllegalArgumentException.class);
 290         testOneMontgomeryMultiplyCheck(blah, blah, mod, 0, n_prime, IllegalArgumentException.class);
 291         testOneMontgomeryMultiplyCheck(blah, blah, mod, -1, n_prime, IllegalArgumentException.class);
 292         // As above, but for multiply
 293         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 41, n_prime, IllegalArgumentException.class);
 294         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);
 295         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);
 296 
 297         // Length odd
 298         testOneMontgomeryMultiplyCheck(small, small, mod, 39, n_prime, IllegalArgumentException.class);
 299         testOneMontgomeryMultiplyCheck(small, small, mod, 0, n_prime, IllegalArgumentException.class);
 300         testOneMontgomeryMultiplyCheck(small, small, mod, -1, n_prime, IllegalArgumentException.class);
 301         // As above, but for multiply
 302         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 39, n_prime, IllegalArgumentException.class);
 303         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 0, n_prime, IllegalArgumentException.class);
 304         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, -1, n_prime, IllegalArgumentException.class);
 305 
 306         // array too small
 307         testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);
 308         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 40, n_prime, small, IllegalArgumentException.class);
 309         testOneMontgomeryMultiplyCheck(small, blah, mod, 40, n_prime, blah, IllegalArgumentException.class);
 310         testOneMontgomeryMultiplyCheck(blah, small, mod, 40, n_prime, blah, IllegalArgumentException.class);
 311         testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);
 312         testOneMontgomeryMultiplyCheck(small, small, mod, 40, n_prime, blah, IllegalArgumentException.class);
 313     }
 314 
 315     public static void main(String args[]) {
 316         if (Platform.isServer() &&
 317             wb.isIntrinsicAvailable(getExecutable(true), COMP_LEVEL_FULL_OPTIMIZATION) &&
 318             wb.isIntrinsicAvailable(getExecutable(false), COMP_LEVEL_FULL_OPTIMIZATION)) {
 319             try {
 320                 new MontgomeryMultiplyTest().testMontgomeryMultiplyChecks();
 321                 new MontgomeryMultiplyTest().testResultValues();
 322             } catch (Throwable ex) {
 323                 throw new RuntimeException(ex);
 324             }
 325         }
 326     }
 327 }