# HG changeset patch # User valeriep # Date 1531449402 0 # Node ID 080776992b2998c6f36ca7458494df877e2c9f3a # Parent 4e98b465d706c1fee9e63811805af555a7fa8739 8179098: Crypto AES/ECB encryption/decryption performance regression (introduced in jdk9b73) Summary: Do bounds check per encryption/decryption call instead of per block Reviewed-by: ascarpino, redestad diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java --- a/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/AESCrypt.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, 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 @@ -39,7 +39,6 @@ import java.security.InvalidKeyException; import java.security.MessageDigest; import java.util.Arrays; -import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; @@ -351,8 +350,8 @@ */ void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) { - Objects.checkFromIndexSize(inOffset, AES_BLOCK_SIZE, in.length); - Objects.checkFromIndexSize(outOffset, AES_BLOCK_SIZE, out.length); + // Array bound checks are done in caller code, i.e. + // FeedbackCipher.encrypt/decrypt(...) to improve performance. implEncryptBlock(in, inOffset, out, outOffset); } @@ -430,8 +429,8 @@ */ void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) { - Objects.checkFromIndexSize(inOffset, AES_BLOCK_SIZE, in.length); - Objects.checkFromIndexSize(outOffset, AES_BLOCK_SIZE, out.length); + // Array bound checks are done in caller code, i.e. + // FeedbackCipher.encrypt/decrypt(...) to improve performance. implDecryptBlock(in, inOffset, out, outOffset); } diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherBlockChaining.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -30,6 +30,7 @@ import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import sun.security.util.ArrayUtil; /** @@ -145,9 +146,9 @@ if (plainLen <= 0) { return plainLen; } - cryptBlockSizeCheck(plainLen); - cryptNullAndBoundsCheck(plain, plainOffset, plainLen); - cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen); + ArrayUtil.blockSizeCheck(plainLen, blockSize); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen); return implEncrypt(plain, plainOffset, plainLen, cipher, cipherOffset); } @@ -196,9 +197,9 @@ if (cipherLen <= 0) { return cipherLen; } - cryptBlockSizeCheck(cipherLen); - cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen); - cryptNullAndBoundsCheck(plain, plainOffset, cipherLen); + ArrayUtil.blockSizeCheck(cipherLen, blockSize); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen); return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset); } @@ -218,23 +219,4 @@ } return cipherLen; } - - private void cryptBlockSizeCheck(int len) { - if ((len % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } - } - - private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) { - Objects.requireNonNull(array); - - if (offset < 0 || offset >= array.length) { - throw new ArrayIndexOutOfBoundsException(offset); - } - - int endIndex = offset + len - 1; - if (endIndex < 0 || endIndex >= array.length) { - throw new ArrayIndexOutOfBoundsException(endIndex); - } - } } diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java --- a/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/CipherFeedback.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -27,6 +27,7 @@ import java.security.InvalidKeyException; import java.security.ProviderException; +import sun.security.util.ArrayUtil; /** * This class represents ciphers in cipher-feedback (CFB) mode. @@ -149,9 +150,9 @@ */ int encrypt(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset) { - if ((plainLen % numBytes) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(plainLen, numBytes); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen); int nShift = blockSize - numBytes; int loopCount = plainLen / numBytes; @@ -225,9 +226,10 @@ */ int decrypt(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset) { - if ((cipherLen % numBytes) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + + ArrayUtil.blockSizeCheck(cipherLen, numBytes); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen); int nShift = blockSize - numBytes; int loopCount = cipherLen / numBytes; diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java --- a/src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, 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 @@ -26,9 +26,9 @@ package com.sun.crypto.provider; import java.security.InvalidKeyException; -import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import sun.security.util.ArrayUtil; /** * This class represents ciphers in counter (CTR) mode. @@ -175,8 +175,9 @@ if (len == 0) { return 0; } - Objects.checkFromIndexSize(inOff, len, in.length); - Objects.checkFromIndexSize(outOff, len, out.length); + + ArrayUtil.nullAndBoundsCheck(in, inOff, len); + ArrayUtil.nullAndBoundsCheck(out, outOff, len); return implCrypt(in, inOff, len, out, outOff); } diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/ElectronicCodeBook.java --- a/src/java.base/share/classes/com/sun/crypto/provider/ElectronicCodeBook.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/ElectronicCodeBook.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -27,6 +27,7 @@ import java.security.InvalidKeyException; import java.security.ProviderException; +import sun.security.util.ArrayUtil; /** * This class represents ciphers in electronic codebook (ECB) mode. @@ -112,9 +113,10 @@ * @return the length of the encrypted data */ int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) { - if ((len % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(len, blockSize); + ArrayUtil.nullAndBoundsCheck(in, inOff, len); + ArrayUtil.nullAndBoundsCheck(out, outOff, len); + for (int i = len; i >= blockSize; i -= blockSize) { embeddedCipher.encryptBlock(in, inOff, out, outOff); inOff += blockSize; @@ -141,9 +143,10 @@ * @return the length of the decrypted data */ int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) { - if ((len % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(len, blockSize); + ArrayUtil.nullAndBoundsCheck(in, inOff, len); + ArrayUtil.nullAndBoundsCheck(out, outOff, len); + for (int i = len; i >= blockSize; i -= blockSize) { embeddedCipher.decryptBlock(in, inOff, out, outOff); inOff += blockSize; diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java --- a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, 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 @@ -30,6 +30,8 @@ import java.security.*; import javax.crypto.*; import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; +import sun.security.util.ArrayUtil; + /** * This class represents ciphers in GaloisCounter (GCM) mode. @@ -406,8 +408,8 @@ /** * Performs encryption operation. * - *

The input plain text in, starting at inOff - * and ending at (inOff + len - 1), is encrypted. The result + *

The input plain text in, starting at inOfs + * and ending at (inOfs + len - 1), is encrypted. The result * is stored in out, starting at outOfs. * * @param in the buffer with the input data to be encrypted @@ -420,18 +422,21 @@ * @return the number of bytes placed into the out buffer */ int encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) { - if ((len % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(len, blockSize); checkDataLength(processed, len); processAAD(); + if (len > 0) { + ArrayUtil.nullAndBoundsCheck(in, inOfs, len); + ArrayUtil.nullAndBoundsCheck(out, outOfs, len); + gctrPAndC.update(in, inOfs, len, out, outOfs); processed += len; ghashAllToS.update(out, outOfs, len); } + return len; } @@ -451,7 +456,10 @@ throw new ShortBufferException ("Can't fit both data and tag into one buffer"); } - if (out.length - outOfs < (len + tagLenBytes)) { + try { + ArrayUtil.nullAndBoundsCheck(out, outOfs, + (len + tagLenBytes)); + } catch (ArrayIndexOutOfBoundsException aiobe) { throw new ShortBufferException("Output buffer too small"); } @@ -459,6 +467,8 @@ processAAD(); if (len > 0) { + ArrayUtil.nullAndBoundsCheck(in, inOfs, len); + doLastBlock(in, inOfs, len, out, outOfs, true); } @@ -492,9 +502,7 @@ * @return the number of bytes placed into the out buffer */ int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) { - if ((len % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(len, blockSize); checkDataLength(ibuffer.size(), len); @@ -504,6 +512,7 @@ // store internally until decryptFinal is called because // spec mentioned that only return recovered data after tag // is successfully verified + ArrayUtil.nullAndBoundsCheck(in, inOfs, len); ibuffer.write(in, inOfs, len); } return 0; @@ -532,22 +541,28 @@ if (len < tagLenBytes) { throw new AEADBadTagException("Input too short - need tag"); } + // do this check here can also catch the potential integer overflow // scenario for the subsequent output buffer capacity check. checkDataLength(ibuffer.size(), (len - tagLenBytes)); - if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) { + try { + ArrayUtil.nullAndBoundsCheck(out, outOfs, + (ibuffer.size() + len) - tagLenBytes); + } catch (ArrayIndexOutOfBoundsException aiobe) { throw new ShortBufferException("Output buffer too small"); } processAAD(); + ArrayUtil.nullAndBoundsCheck(in, inOfs, len); + // get the trailing tag bytes from 'in' byte[] tag = new byte[tagLenBytes]; System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes); len -= tagLenBytes; - if (len != 0) { + if (len > 0) { ibuffer.write(in, inOfs, len); } diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java --- a/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/OutputFeedback.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -27,6 +27,7 @@ import java.security.InvalidKeyException; import java.security.ProviderException; +import sun.security.util.ArrayUtil; /** * This class represents ciphers in output-feedback (OFB) mode. @@ -148,10 +149,10 @@ */ int encrypt(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset) { + ArrayUtil.blockSizeCheck(plainLen, numBytes); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen); - if ((plainLen % numBytes) != 0) { - throw new ProviderException("Internal error in input buffering"); - } int nShift = blockSize - numBytes; int loopCount = plainLen / numBytes; @@ -189,6 +190,9 @@ */ int encryptFinal(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset) { + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen); + int oddBytes = plainLen % numBytes; int len = encrypt(plain, plainOffset, (plainLen - oddBytes), cipher, cipherOffset); diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/com/sun/crypto/provider/PCBC.java --- a/src/java.base/share/classes/com/sun/crypto/provider/PCBC.java Thu Jul 12 08:44:39 2018 +0800 +++ b/src/java.base/share/classes/com/sun/crypto/provider/PCBC.java Fri Jul 13 02:36:42 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -27,6 +27,7 @@ import java.security.InvalidKeyException; import java.security.ProviderException; +import sun.security.util.ArrayUtil; /** @@ -136,9 +137,10 @@ int encrypt(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset) { - if ((plainLen % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(plainLen, blockSize); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen); + int i; int endIndex = plainOffset + plainLen; @@ -176,9 +178,10 @@ int decrypt(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset) { - if ((cipherLen % blockSize) != 0) { - throw new ProviderException("Internal error in input buffering"); - } + ArrayUtil.blockSizeCheck(cipherLen, blockSize); + ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen); + ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen); + int i; int endIndex = cipherOffset + cipherLen; diff -r 4e98b465d706 -r 080776992b29 src/java.base/share/classes/sun/security/util/ArrayUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/util/ArrayUtil.java Fri Jul 13 02:36:42 2018 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.util; + +import java.util.List; +import java.util.function.BiFunction; +import java.security.*; +import jdk.internal.util.Preconditions; + + +/** + * This class holds the various utility methods for array range checks. + */ + +public final class ArrayUtil { + + private static final BiFunction, + ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER = + Preconditions.outOfBoundsExceptionFormatter + (ArrayIndexOutOfBoundsException::new); + + public static void blockSizeCheck(int len, int blockSize) { + if ((len % blockSize) != 0) { + throw new ProviderException("Internal error in input buffering"); + } + } + + public static void nullAndBoundsCheck(byte[] array, int offset, int len) { + // NPE is thrown when array is null + Preconditions.checkFromIndexSize(offset, len, array.length, AIOOBE_SUPPLIER); + } +}