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