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