1 /*
   2  * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider;
  27 
  28 import static java.lang.Integer.reverseBytes;
  29 import static java.lang.Long.reverseBytes;
  30 
  31 import java.nio.ByteOrder;
  32 
  33 import sun.misc.Unsafe;
  34 
  35 /**
  36  * Optimized methods for converting between byte[] and int[]/long[], both for
  37  * big endian and little endian byte orders.
  38  *
  39  * Currently, it includes a default code path plus two optimized code paths.
  40  * One is for little endian architectures that support full speed int/long
  41  * access at unaligned addresses (i.e. x86/amd64). The second is for big endian
  42  * architectures (that only support correctly aligned access), such as SPARC.
  43  * These are the only platforms we currently support, but other optimized
  44  * variants could be added as needed.
  45  *
  46  * NOTE that ArrayIndexOutOfBoundsException will be thrown if the bounds checks
  47  * failed.
  48  *
  49  * This class may also be helpful in improving the performance of the
  50  * crypto code in the SunJCE provider. However, for now it is only accessible by
  51  * the message digest implementation in the SUN provider.
  52  *
  53  * @since   1.6
  54  * @author  Andreas Sterbenz
  55  */
  56 final class ByteArrayAccess {
  57 
  58     private ByteArrayAccess() {
  59         // empty
  60     }
  61 
  62     private static final Unsafe unsafe = Unsafe.getUnsafe();
  63 
  64     // whether to use the optimized path for little endian platforms that
  65     // support full speed unaligned memory access.
  66     private static final boolean littleEndianUnaligned;
  67 
  68     // whether to use the optimzied path for big endian platforms that
  69     // support only correctly aligned full speed memory access.
  70     // (Note that on SPARC unaligned memory access is possible, but it is
  71     // implemented using a software trap and therefore very slow)
  72     private static final boolean bigEndian;
  73 
  74     private final static int byteArrayOfs = unsafe.arrayBaseOffset(byte[].class);
  75 
  76     static {
  77         boolean scaleOK = ((unsafe.arrayIndexScale(byte[].class) == 1)
  78             && (unsafe.arrayIndexScale(int[].class) == 4)
  79             && (unsafe.arrayIndexScale(long[].class) == 8)
  80             && ((byteArrayOfs & 3) == 0));
  81 
  82         ByteOrder byteOrder = ByteOrder.nativeOrder();
  83         littleEndianUnaligned =
  84             scaleOK && unaligned() && (byteOrder == ByteOrder.LITTLE_ENDIAN);
  85         bigEndian =
  86             scaleOK && (byteOrder == ByteOrder.BIG_ENDIAN);
  87     }
  88 
  89     // Return whether this platform supports full speed int/long memory access
  90     // at unaligned addresses.
  91     // This code was copied from java.nio.Bits because there is no equivalent
  92     // public API.
  93     private static boolean unaligned() {
  94         String arch = java.security.AccessController.doPrivileged
  95             (new sun.security.action.GetPropertyAction("os.arch", ""));
  96         return arch.equals("i386") || arch.equals("x86") || arch.equals("amd64")
  97             || arch.equals("x86_64");
  98     }
  99 
 100     /**
 101      * byte[] to int[] conversion, little endian byte order.
 102      */
 103     static void b2iLittle(byte[] in, int inOfs, int[] out, int outOfs, int len) {
 104         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 105             (outOfs < 0) || ((out.length - outOfs) < len/4)) {
 106             throw new ArrayIndexOutOfBoundsException();
 107         }
 108         if (littleEndianUnaligned) {
 109             inOfs += byteArrayOfs;
 110             len += inOfs;
 111             while (inOfs < len) {
 112                 out[outOfs++] = unsafe.getInt(in, (long)inOfs);
 113                 inOfs += 4;
 114             }
 115         } else if (bigEndian && ((inOfs & 3) == 0)) {
 116             inOfs += byteArrayOfs;
 117             len += inOfs;
 118             while (inOfs < len) {
 119                 out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs));
 120                 inOfs += 4;
 121             }
 122         } else {
 123             len += inOfs;
 124             while (inOfs < len) {
 125                 out[outOfs++] = ((in[inOfs    ] & 0xff)      )
 126                               | ((in[inOfs + 1] & 0xff) <<  8)
 127                               | ((in[inOfs + 2] & 0xff) << 16)
 128                               | ((in[inOfs + 3]       ) << 24);
 129                 inOfs += 4;
 130             }
 131         }
 132     }
 133 
 134     // Special optimization of b2iLittle(in, inOfs, out, 0, 64)
 135     static void b2iLittle64(byte[] in, int inOfs, int[] out) {
 136         if ((inOfs < 0) || ((in.length - inOfs) < 64) ||
 137             (out.length < 16)) {
 138             throw new ArrayIndexOutOfBoundsException();
 139         }
 140         if (littleEndianUnaligned) {
 141             inOfs += byteArrayOfs;
 142             out[ 0] = unsafe.getInt(in, (long)(inOfs     ));
 143             out[ 1] = unsafe.getInt(in, (long)(inOfs +  4));
 144             out[ 2] = unsafe.getInt(in, (long)(inOfs +  8));
 145             out[ 3] = unsafe.getInt(in, (long)(inOfs + 12));
 146             out[ 4] = unsafe.getInt(in, (long)(inOfs + 16));
 147             out[ 5] = unsafe.getInt(in, (long)(inOfs + 20));
 148             out[ 6] = unsafe.getInt(in, (long)(inOfs + 24));
 149             out[ 7] = unsafe.getInt(in, (long)(inOfs + 28));
 150             out[ 8] = unsafe.getInt(in, (long)(inOfs + 32));
 151             out[ 9] = unsafe.getInt(in, (long)(inOfs + 36));
 152             out[10] = unsafe.getInt(in, (long)(inOfs + 40));
 153             out[11] = unsafe.getInt(in, (long)(inOfs + 44));
 154             out[12] = unsafe.getInt(in, (long)(inOfs + 48));
 155             out[13] = unsafe.getInt(in, (long)(inOfs + 52));
 156             out[14] = unsafe.getInt(in, (long)(inOfs + 56));
 157             out[15] = unsafe.getInt(in, (long)(inOfs + 60));
 158         } else if (bigEndian && ((inOfs & 3) == 0)) {
 159             inOfs += byteArrayOfs;
 160             out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs     )));
 161             out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  4)));
 162             out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  8)));
 163             out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12)));
 164             out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16)));
 165             out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20)));
 166             out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24)));
 167             out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28)));
 168             out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32)));
 169             out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36)));
 170             out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40)));
 171             out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44)));
 172             out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48)));
 173             out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52)));
 174             out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56)));
 175             out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60)));
 176         } else {
 177             b2iLittle(in, inOfs, out, 0, 64);
 178         }
 179     }
 180 
 181     /**
 182      * int[] to byte[] conversion, little endian byte order.
 183      */
 184     static void i2bLittle(int[] in, int inOfs, byte[] out, int outOfs, int len) {
 185         if ((inOfs < 0) || ((in.length - inOfs) < len/4) ||
 186             (outOfs < 0) || ((out.length - outOfs) < len)) {
 187             throw new ArrayIndexOutOfBoundsException();
 188         }
 189         if (littleEndianUnaligned) {
 190             outOfs += byteArrayOfs;
 191             len += outOfs;
 192             while (outOfs < len) {
 193                 unsafe.putInt(out, (long)outOfs, in[inOfs++]);
 194                 outOfs += 4;
 195             }
 196         } else if (bigEndian && ((outOfs & 3) == 0)) {
 197             outOfs += byteArrayOfs;
 198             len += outOfs;
 199             while (outOfs < len) {
 200                 unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++]));
 201                 outOfs += 4;
 202             }
 203         } else {
 204             len += outOfs;
 205             while (outOfs < len) {
 206                 int i = in[inOfs++];
 207                 out[outOfs++] = (byte)(i      );
 208                 out[outOfs++] = (byte)(i >>  8);
 209                 out[outOfs++] = (byte)(i >> 16);
 210                 out[outOfs++] = (byte)(i >> 24);
 211             }
 212         }
 213     }
 214 
 215     // Store one 32-bit value into out[outOfs..outOfs+3] in little endian order.
 216     static void i2bLittle4(int val, byte[] out, int outOfs) {
 217         if ((outOfs < 0) || ((out.length - outOfs) < 4)) {
 218             throw new ArrayIndexOutOfBoundsException();
 219         }
 220         if (littleEndianUnaligned) {
 221             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val);
 222         } else if (bigEndian && ((outOfs & 3) == 0)) {
 223             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val));
 224         } else {
 225             out[outOfs    ] = (byte)(val      );
 226             out[outOfs + 1] = (byte)(val >>  8);
 227             out[outOfs + 2] = (byte)(val >> 16);
 228             out[outOfs + 3] = (byte)(val >> 24);
 229         }
 230     }
 231 
 232     /**
 233      * byte[] to int[] conversion, big endian byte order.
 234      */
 235     static void b2iBig(byte[] in, int inOfs, int[] out, int outOfs, int len) {
 236         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 237             (outOfs < 0) || ((out.length - outOfs) < len/4)) {
 238             throw new ArrayIndexOutOfBoundsException();
 239         }
 240         if (littleEndianUnaligned) {
 241             inOfs += byteArrayOfs;
 242             len += inOfs;
 243             while (inOfs < len) {
 244                 out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs));
 245                 inOfs += 4;
 246             }
 247         } else if (bigEndian && ((inOfs & 3) == 0)) {
 248             inOfs += byteArrayOfs;
 249             len += inOfs;
 250             while (inOfs < len) {
 251                 out[outOfs++] = unsafe.getInt(in, (long)inOfs);
 252                 inOfs += 4;
 253             }
 254         } else {
 255             len += inOfs;
 256             while (inOfs < len) {
 257                 out[outOfs++] = ((in[inOfs + 3] & 0xff)      )
 258                               | ((in[inOfs + 2] & 0xff) <<  8)
 259                               | ((in[inOfs + 1] & 0xff) << 16)
 260                               | ((in[inOfs    ]       ) << 24);
 261                 inOfs += 4;
 262             }
 263         }
 264     }
 265 
 266     // Special optimization of b2iBig(in, inOfs, out, 0, 64)
 267     static void b2iBig64(byte[] in, int inOfs, int[] out) {
 268         if ((inOfs < 0) || ((in.length - inOfs) < 64) ||
 269             (out.length < 16)) {
 270             throw new ArrayIndexOutOfBoundsException();
 271         }
 272         if (littleEndianUnaligned) {
 273             inOfs += byteArrayOfs;
 274             out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs     )));
 275             out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  4)));
 276             out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  8)));
 277             out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12)));
 278             out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16)));
 279             out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20)));
 280             out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24)));
 281             out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28)));
 282             out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32)));
 283             out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36)));
 284             out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40)));
 285             out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44)));
 286             out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48)));
 287             out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52)));
 288             out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56)));
 289             out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60)));
 290         } else if (bigEndian && ((inOfs & 3) == 0)) {
 291             inOfs += byteArrayOfs;
 292             out[ 0] = unsafe.getInt(in, (long)(inOfs     ));
 293             out[ 1] = unsafe.getInt(in, (long)(inOfs +  4));
 294             out[ 2] = unsafe.getInt(in, (long)(inOfs +  8));
 295             out[ 3] = unsafe.getInt(in, (long)(inOfs + 12));
 296             out[ 4] = unsafe.getInt(in, (long)(inOfs + 16));
 297             out[ 5] = unsafe.getInt(in, (long)(inOfs + 20));
 298             out[ 6] = unsafe.getInt(in, (long)(inOfs + 24));
 299             out[ 7] = unsafe.getInt(in, (long)(inOfs + 28));
 300             out[ 8] = unsafe.getInt(in, (long)(inOfs + 32));
 301             out[ 9] = unsafe.getInt(in, (long)(inOfs + 36));
 302             out[10] = unsafe.getInt(in, (long)(inOfs + 40));
 303             out[11] = unsafe.getInt(in, (long)(inOfs + 44));
 304             out[12] = unsafe.getInt(in, (long)(inOfs + 48));
 305             out[13] = unsafe.getInt(in, (long)(inOfs + 52));
 306             out[14] = unsafe.getInt(in, (long)(inOfs + 56));
 307             out[15] = unsafe.getInt(in, (long)(inOfs + 60));
 308         } else {
 309             b2iBig(in, inOfs, out, 0, 64);
 310         }
 311     }
 312 
 313     /**
 314      * int[] to byte[] conversion, big endian byte order.
 315      */
 316     static void i2bBig(int[] in, int inOfs, byte[] out, int outOfs, int len) {
 317         if ((inOfs < 0) || ((in.length - inOfs) < len/4) ||
 318             (outOfs < 0) || ((out.length - outOfs) < len)) {
 319             throw new ArrayIndexOutOfBoundsException();
 320         }
 321         if (littleEndianUnaligned) {
 322             outOfs += byteArrayOfs;
 323             len += outOfs;
 324             while (outOfs < len) {
 325                 unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++]));
 326                 outOfs += 4;
 327             }
 328         } else if (bigEndian && ((outOfs & 3) == 0)) {
 329             outOfs += byteArrayOfs;
 330             len += outOfs;
 331             while (outOfs < len) {
 332                 unsafe.putInt(out, (long)outOfs, in[inOfs++]);
 333                 outOfs += 4;
 334             }
 335         } else {
 336             len += outOfs;
 337             while (outOfs < len) {
 338                 int i = in[inOfs++];
 339                 out[outOfs++] = (byte)(i >> 24);
 340                 out[outOfs++] = (byte)(i >> 16);
 341                 out[outOfs++] = (byte)(i >>  8);
 342                 out[outOfs++] = (byte)(i      );
 343             }
 344         }
 345     }
 346 
 347     // Store one 32-bit value into out[outOfs..outOfs+3] in big endian order.
 348     static void i2bBig4(int val, byte[] out, int outOfs) {
 349         if ((outOfs < 0) || ((out.length - outOfs) < 4)) {
 350             throw new ArrayIndexOutOfBoundsException();
 351         }
 352         if (littleEndianUnaligned) {
 353             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val));
 354         } else if (bigEndian && ((outOfs & 3) == 0)) {
 355             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val);
 356         } else {
 357             out[outOfs    ] = (byte)(val >> 24);
 358             out[outOfs + 1] = (byte)(val >> 16);
 359             out[outOfs + 2] = (byte)(val >>  8);
 360             out[outOfs + 3] = (byte)(val      );
 361         }
 362     }
 363 
 364     /**
 365      * byte[] to long[] conversion, big endian byte order.
 366      */
 367     static void b2lBig(byte[] in, int inOfs, long[] out, int outOfs, int len) {
 368         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 369             (outOfs < 0) || ((out.length - outOfs) < len/8)) {
 370             throw new ArrayIndexOutOfBoundsException();
 371         }
 372         if (littleEndianUnaligned) {
 373             inOfs += byteArrayOfs;
 374             len += inOfs;
 375             while (inOfs < len) {
 376                 out[outOfs++] = reverseBytes(unsafe.getLong(in, (long)inOfs));
 377                 inOfs += 8;
 378             }
 379         } else if (bigEndian && ((inOfs & 3) == 0)) {
 380             // In the current HotSpot memory layout, the first element of a
 381             // byte[] is only 32-bit aligned, not 64-bit.
 382             // That means we could use getLong() only for offset 4, 12, etc.,
 383             // which would rarely occur in practice. Instead, we use an
 384             // optimization that uses getInt() so that it works for offset 0.
 385             inOfs += byteArrayOfs;
 386             len += inOfs;
 387             while (inOfs < len) {
 388                 out[outOfs++] =
 389                       ((long)unsafe.getInt(in, (long)inOfs) << 32)
 390                           | (unsafe.getInt(in, (long)(inOfs + 4)) & 0xffffffffL);
 391                 inOfs += 8;
 392             }
 393         } else {
 394             len += inOfs;
 395             while (inOfs < len) {
 396                 int i1 = ((in[inOfs + 3] & 0xff)      )
 397                        | ((in[inOfs + 2] & 0xff) <<  8)
 398                        | ((in[inOfs + 1] & 0xff) << 16)
 399                        | ((in[inOfs    ]       ) << 24);
 400                 inOfs += 4;
 401                 int i2 = ((in[inOfs + 3] & 0xff)      )
 402                        | ((in[inOfs + 2] & 0xff) <<  8)
 403                        | ((in[inOfs + 1] & 0xff) << 16)
 404                        | ((in[inOfs    ]       ) << 24);
 405                 out[outOfs++] = ((long)i1 << 32) | (i2 & 0xffffffffL);
 406                 inOfs += 4;
 407             }
 408         }
 409     }
 410 
 411     // Special optimization of b2lBig(in, inOfs, out, 0, 128)
 412     static void b2lBig128(byte[] in, int inOfs, long[] out) {
 413         if ((inOfs < 0) || ((in.length - inOfs) < 128) ||
 414             (out.length < 16)) {
 415             throw new ArrayIndexOutOfBoundsException();
 416         }
 417         if (littleEndianUnaligned) {
 418             inOfs += byteArrayOfs;
 419             out[ 0] = reverseBytes(unsafe.getLong(in, (long)(inOfs      )));
 420             out[ 1] = reverseBytes(unsafe.getLong(in, (long)(inOfs +   8)));
 421             out[ 2] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  16)));
 422             out[ 3] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  24)));
 423             out[ 4] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  32)));
 424             out[ 5] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  40)));
 425             out[ 6] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  48)));
 426             out[ 7] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  56)));
 427             out[ 8] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  64)));
 428             out[ 9] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  72)));
 429             out[10] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  80)));
 430             out[11] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  88)));
 431             out[12] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  96)));
 432             out[13] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 104)));
 433             out[14] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 112)));
 434             out[15] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 120)));
 435         } else {
 436             // no optimization for big endian, see comments in b2lBig
 437             b2lBig(in, inOfs, out, 0, 128);
 438         }
 439     }
 440 
 441     /**
 442      * long[] to byte[] conversion, big endian byte order.
 443      */
 444     static void l2bBig(long[] in, int inOfs, byte[] out, int outOfs, int len) {
 445         if ((inOfs < 0) || ((in.length - inOfs) < len/8) ||
 446             (outOfs < 0) || ((out.length - outOfs) < len)) {
 447             throw new ArrayIndexOutOfBoundsException();
 448         }
 449         len += outOfs;
 450         while (outOfs < len) {
 451             long i = in[inOfs++];
 452             out[outOfs++] = (byte)(i >> 56);
 453             out[outOfs++] = (byte)(i >> 48);
 454             out[outOfs++] = (byte)(i >> 40);
 455             out[outOfs++] = (byte)(i >> 32);
 456             out[outOfs++] = (byte)(i >> 24);
 457             out[outOfs++] = (byte)(i >> 16);
 458             out[outOfs++] = (byte)(i >>  8);
 459             out[outOfs++] = (byte)(i      );
 460         }
 461     }
 462 }