changeset 13318:70160c527861

8209862: CipherCore performance improvement Reviewed-by: apetcher, ascarpino Contributed-by: sergey.kuksenko@oracle.com, sean.coffey@oracle.com
author coffeys
date Mon, 15 Oct 2018 14:42:31 +0100
parents 0d59f544aaf3
children 9cd4a2714d8b
files src/share/classes/com/sun/crypto/provider/CipherCore.java
diffstat 1 files changed, 126 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/CipherCore.java	Tue Oct 16 13:09:51 2018 -0700
+++ b/src/share/classes/com/sun/crypto/provider/CipherCore.java	Mon Oct 15 14:42:31 2018 +0100
@@ -253,11 +253,10 @@
         return result;
     }
 
-
     /**
      * Sets the padding mechanism of this cipher.
      *
-     * @param padding the padding mechanism
+     * @param paddingScheme the padding mechanism
      *
      * @exception NoSuchPaddingException if the requested padding mechanism
      * does not exist
@@ -660,10 +659,7 @@
      * (e.g., has not been initialized)
      */
     byte[] update(byte[] input, int inputOffset, int inputLen) {
-        if (requireReinit) {
-            throw new IllegalStateException
-                ("Must use either different key or iv for GCM encryption");
-        }
+        checkReinit();
 
         byte[] output = null;
         try {
@@ -711,10 +707,7 @@
      */
     int update(byte[] input, int inputOffset, int inputLen, byte[] output,
                int outputOffset) throws ShortBufferException {
-        if (requireReinit) {
-            throw new IllegalStateException
-                ("Must use either different key or iv for GCM encryption");
-        }
+        checkReinit();
 
         // figure out how much can be sent to crypto function
         int len = Math.addExact(buffered, inputLen);
@@ -849,12 +842,20 @@
      */
     byte[] doFinal(byte[] input, int inputOffset, int inputLen)
         throws IllegalBlockSizeException, BadPaddingException {
-        byte[] output = null;
         try {
-            output = new byte[getOutputSizeByOperation(inputLen, true)];
-            int len = doFinal(input, inputOffset, inputLen, output, 0);
-            if (len < output.length) {
-                byte[] copy = Arrays.copyOf(output, len);
+            checkReinit();
+            byte[] output = new byte[getOutputSizeByOperation(inputLen, true)];
+            byte[] finalBuf = prepareInputBuffer(input, inputOffset,
+                    inputLen, output, 0);
+            int finalOffset = (finalBuf == input) ? inputOffset : 0;
+            int finalBufLen = (finalBuf == input) ? inputLen : finalBuf.length;
+
+            int outLen = fillOutputBuffer(finalBuf, finalOffset, output, 0,
+                    finalBufLen, input);
+
+            endDoFinal();
+            if (outLen < output.length) {
+                byte[] copy = Arrays.copyOf(output, outLen);
                 if (decrypting) {
                     // Zero out internal (ouput) array
                     Arrays.fill(output, (byte) 0x00);
@@ -909,26 +910,81 @@
                 int outputOffset)
         throws IllegalBlockSizeException, ShortBufferException,
                BadPaddingException {
-        if (requireReinit) {
-            throw new IllegalStateException
-                ("Must use either different key or iv for GCM encryption");
-        }
+        checkReinit();
 
         int estOutSize = getOutputSizeByOperation(inputLen, true);
-        // check output buffer capacity.
-        // if we are decrypting with padding applied, we can perform this
-        // check only after we have determined how many padding bytes there
-        // are.
-        int outputCapacity = output.length - outputOffset;
-        int minOutSize = (decrypting? (estOutSize - blockSize):estOutSize);
-        if ((output == null) || (outputCapacity < minOutSize)) {
-            throw new ShortBufferException("Output buffer must be "
-                + "(at least) " + minOutSize + " bytes long");
+        int outputCapacity = checkOutputCapacity(output, outputOffset,
+                estOutSize);
+        int offset = decrypting ? 0 : outputOffset; // 0 for decrypting
+        byte[] finalBuf = prepareInputBuffer(input, inputOffset,
+                inputLen, output, outputOffset);
+        byte[] outWithPadding = null; // for decrypting only
+
+        int finalOffset = (finalBuf == input) ? inputOffset : 0;
+        int finalBufLen = (finalBuf == input) ? inputLen : finalBuf.length;
+
+        if (decrypting) {
+            // if the size of specified output buffer is less than
+            // the length of the cipher text, then the current
+            // content of cipher has to be preserved in order for
+            // users to retry the call with a larger buffer in the
+            // case of ShortBufferException.
+            if (outputCapacity < estOutSize) {
+                cipher.save();
+            }
+            // create temporary output buffer so that only "real"
+            // data bytes are passed to user's output buffer.
+            outWithPadding = new byte[estOutSize];
         }
+        byte[] outBuffer = decrypting ? outWithPadding : output;
 
+        int outLen = fillOutputBuffer(finalBuf, finalOffset, outBuffer,
+                offset, finalBufLen, input);
+
+        if (decrypting) {
+
+            if (outputCapacity < outLen) {
+                // restore so users can retry with a larger buffer
+                cipher.restore();
+                throw new ShortBufferException("Output buffer too short: "
+                                               + (outputCapacity)
+                                               + " bytes given, " + outLen
+                                               + " bytes needed");
+            }
+            // copy the result into user-supplied output buffer
+            System.arraycopy(outWithPadding, 0, output, outputOffset, outLen);
+            // decrypt mode. Zero out output data that's not required
+            Arrays.fill(outWithPadding, (byte) 0x00);
+        }
+        endDoFinal();
+        return outLen;
+    }
+
+    private void endDoFinal() {
+        buffered = 0;
+        diffBlocksize = blockSize;
+        if (cipherMode != ECB_MODE) {
+            cipher.reset();
+        }
+    }
+
+    private int unpad(int outLen, byte[] outWithPadding)
+            throws BadPaddingException {
+        int padStart = padding.unpad(outWithPadding, 0, outLen);
+        if (padStart < 0) {
+            throw new BadPaddingException("Given final block not " +
+            "properly padded. Such issues can arise if a bad key " +
+            "is used during decryption.");
+        }
+        outLen = padStart;
+        return outLen;
+    }
+
+    private byte[] prepareInputBuffer(byte[] input, int inputOffset,
+                      int inputLen, byte[] output, int outputOffset)
+                      throws IllegalBlockSizeException, ShortBufferException {
         // calculate total input length
         int len = Math.addExact(buffered, inputLen);
-
         // calculate padding length
         int totalLen = Math.addExact(len, cipher.getBufferedLength());
         int paddingLen = 0;
@@ -958,18 +1014,15 @@
          *  - there are internally buffered bytes
          *  - doing encryption and padding is needed
          */
-        byte[] finalBuf = input;
-        int finalOffset = inputOffset;
-        int finalBufLen = inputLen;
         if ((buffered != 0) || (!decrypting && padding != null) ||
             ((input == output)
               && (outputOffset - inputOffset < inputLen)
               && (inputOffset - outputOffset < buffer.length))) {
+            byte[] finalBuf;
             if (decrypting || padding == null) {
                 paddingLen = 0;
             }
             finalBuf = new byte[Math.addExact(len, paddingLen)];
-            finalOffset = 0;
             if (buffered != 0) {
                 System.arraycopy(buffer, 0, finalBuf, 0, buffered);
                 if (!decrypting) {
@@ -980,56 +1033,31 @@
             }
             if (inputLen != 0) {
                 System.arraycopy(input, inputOffset, finalBuf,
-                                 buffered, inputLen);
+                        buffered, inputLen);
             }
             if (paddingLen != 0) {
                 padding.padWithLen(finalBuf, Math.addExact(buffered, inputLen), paddingLen);
             }
-            finalBufLen = finalBuf.length;
+            return finalBuf;
         }
-        int outLen = 0;
-        if (decrypting) {
-            // if the size of specified output buffer is less than
-            // the length of the cipher text, then the current
-            // content of cipher has to be preserved in order for
-            // users to retry the call with a larger buffer in the
-            // case of ShortBufferException.
-            if (outputCapacity < estOutSize) {
-                cipher.save();
+        return input;
+    }
+
+    private int fillOutputBuffer(byte[] finalBuf, int finalOffset,
+                                 byte[] output, int outOfs, int finalBufLen,
+                                 byte[] input)
+            throws ShortBufferException, BadPaddingException,
+            IllegalBlockSizeException {
+        int len;
+        try {
+            len = finalNoPadding(finalBuf, finalOffset, output,
+                    outOfs, finalBufLen);
+            if (decrypting && padding != null) {
+                len = unpad(len, output);
             }
-            // create temporary output buffer so that only "real"
-            // data bytes are passed to user's output buffer.
-            byte[] outWithPadding = new byte[estOutSize];
-            outLen = finalNoPadding(finalBuf, finalOffset, outWithPadding,
-                                    0, finalBufLen);
-
-            if (padding != null) {
-                int padStart = padding.unpad(outWithPadding, 0, outLen);
-                if (padStart < 0) {
-                    throw new BadPaddingException("Given final block not " +
-                    "properly padded. Such issues can arise if a bad key " +
-                    "is used during decryption.");
-                }
-                outLen = padStart;
-            }
-
-            if (outputCapacity < outLen) {
-                // restore so users can retry with a larger buffer
-                cipher.restore();
-                throw new ShortBufferException("Output buffer too short: "
-                                               + (outputCapacity)
-                                               + " bytes given, " + outLen
-                                               + " bytes needed");
-            }
-            // copy the result into user-supplied output buffer
-            System.arraycopy(outWithPadding, 0, output, outputOffset, outLen);
-            // decrypt mode. Zero out output data that's not required
-            Arrays.fill(outWithPadding, (byte) 0x00);
-        } else { // encrypting
-            try {
-                outLen = finalNoPadding(finalBuf, finalOffset, output,
-                                        outputOffset, finalBufLen);
-            } finally {
+            return len;
+        } finally {
+            if (!decrypting) {
                 // reset after doFinal() for GCM encryption
                 requireReinit = (cipherMode == GCM_MODE);
                 if (finalBuf != input) {
@@ -1038,13 +1066,28 @@
                 }
             }
         }
+    }
 
-        buffered = 0;
-        diffBlocksize = blockSize;
-        if (cipherMode != ECB_MODE) {
-            cipher.reset();
+    private int checkOutputCapacity(byte[] output, int outputOffset,
+                            int estOutSize) throws ShortBufferException {
+        // check output buffer capacity.
+        // if we are decrypting with padding applied, we can perform this
+        // check only after we have determined how many padding bytes there
+        // are.
+        int outputCapacity = output.length - outputOffset;
+        int minOutSize = decrypting ? (estOutSize - blockSize) : estOutSize;
+        if ((output == null) || (outputCapacity < minOutSize)) {
+            throw new ShortBufferException("Output buffer must be "
+                + "(at least) " + minOutSize + " bytes long");
         }
-        return outLen;
+        return outputCapacity;
+    }
+
+    private void checkReinit() {
+        if (requireReinit) {
+            throw new IllegalStateException
+                ("Must use either different key or iv for GCM encryption");
+        }
     }
 
     private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
@@ -1177,10 +1220,7 @@
      * @since 1.8
      */
     void updateAAD(byte[] src, int offset, int len) {
-        if (requireReinit) {
-            throw new IllegalStateException
-                ("Must use either different key or iv for GCM encryption");
-        }
+        checkReinit();
         cipher.updateAAD(src, offset, len);
     }
 }