# HG changeset patch # User valeriep # Date 1475032681 0 # Node ID 591cd107586cd75a3ec3322815f152b371a2b6d8 # Parent 2867bb7f5b53c1da2ccf0c1ad6ed2e4a9b40fd42 6946830: javax.crypto.Cipher.doFinal behavior differs depending on platform Summary: Updated OracleUcrypto and SunPKCS11 providers with SunJCE provider behavior Reviewed-by: xuelei diff -r 2867bb7f5b53 -r 591cd107586c jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java --- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Wed Sep 28 03:10:37 2016 +0000 +++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Wed Sep 28 03:18:01 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -344,7 +344,7 @@ private void implInit(int opmode, Key key, byte[] iv, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - cancelOperation(); + reset(true); if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) { throw new InvalidKeyException("Key size is invalid"); } @@ -404,23 +404,26 @@ if (initialized == false) { return; } - initialized = false; + if ((session == null) || (token.explicitCancel == false)) { return; } - // cancel operation by finishing it - int bufLen = doFinalLength(0); - byte[] buffer = new byte[bufLen]; try { - if (encrypt) { - token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen); + if (session.hasObjects() == false) { + session = token.killSession(session); + return; } else { - token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen); + // cancel operation by finishing it + int bufLen = doFinalLength(0); + byte[] buffer = new byte[bufLen]; + if (encrypt) { + token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen); + } else { + token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen); + } } } catch (PKCS11Exception e) { throw new ProviderException("Cancel failed", e); - } finally { - reset(); } } @@ -483,7 +486,9 @@ } // reset the states to the pre-initialized values - private void reset() { + private void reset(boolean doCancel) { + if (doCancel) cancelOperation(); + initialized = false; bytesBuffered = 0; padBufferLen = 0; @@ -610,7 +615,7 @@ throw (ShortBufferException) (new ShortBufferException().initCause(e)); } - reset(); + reset(false); throw new ProviderException("update() failed", e); } } @@ -728,7 +733,7 @@ throw (ShortBufferException) (new ShortBufferException().initCause(e)); } - reset(); + reset(false); throw new ProviderException("update() failed", e); } } @@ -740,6 +745,7 @@ if (outLen < requiredOutLen) { throw new ShortBufferException(); } + boolean doCancel = true; try { ensureInitialized(); int k = 0; @@ -753,7 +759,12 @@ } k += token.p11.C_EncryptFinal(session.id(), 0, out, (outOfs + k), (outLen - k)); + doCancel = false; } else { + // Special handling to match SunJCE provider behavior + if (bytesBuffered == 0 && padBufferLen == 0) { + return 0; + } if (paddingObj != null) { if (padBufferLen != 0) { k = token.p11.C_DecryptUpdate(session.id(), 0, @@ -762,20 +773,24 @@ } k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k); + doCancel = false; + int actualPadLen = paddingObj.unpad(padBuffer, k); k -= actualPadLen; System.arraycopy(padBuffer, 0, out, outOfs, k); } else { k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs, outLen); + doCancel = false; } } return k; } catch (PKCS11Exception e) { + doCancel = false; handleException(e); throw new ProviderException("doFinal() failed", e); } finally { - reset(); + reset(doCancel); } } @@ -788,6 +803,7 @@ throw new ShortBufferException(); } + boolean doCancel = true; try { ensureInitialized(); @@ -818,7 +834,13 @@ } k += token.p11.C_EncryptFinal(session.id(), outAddr, outArray, (outOfs + k), (outLen - k)); + doCancel = false; } else { + // Special handling to match SunJCE provider behavior + if (bytesBuffered == 0 && padBufferLen == 0) { + return 0; + } + if (paddingObj != null) { if (padBufferLen != 0) { k = token.p11.C_DecryptUpdate(session.id(), @@ -828,6 +850,8 @@ } k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, padBuffer.length - k); + doCancel = false; + int actualPadLen = paddingObj.unpad(padBuffer, k); k -= actualPadLen; outArray = padBuffer; @@ -835,6 +859,7 @@ } else { k = token.p11.C_DecryptFinal(session.id(), outAddr, outArray, outOfs, outLen); + doCancel = false; } } if ((!encrypt && paddingObj != null) || @@ -846,10 +871,11 @@ } return k; } catch (PKCS11Exception e) { + doCancel = false; handleException(e); throw new ProviderException("doFinal() failed", e); } finally { - reset(); + reset(doCancel); } } diff -r 2867bb7f5b53 -r 591cd107586c jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeCipherWithJavaPadding.java --- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeCipherWithJavaPadding.java Wed Sep 28 03:10:37 2016 +0000 +++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeCipherWithJavaPadding.java Wed Sep 28 03:18:01 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2016, 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 @@ -160,8 +160,11 @@ ShortBufferException { int tbSize = (trailingBytes == null? 0:trailingBytes.position()); int dataLen = tbSize + lastData.length; - // check total length - if ((dataLen < 1) || (dataLen % blockSize != 0)) { + + // Special handling to match SunJCE provider behavior + if (dataLen <= 0) { + return 0; + } else if (dataLen % blockSize != 0) { UcryptoProvider.debug("PKCS5Padding: unpad, buffered " + tbSize + " bytes, last block " + lastData.length + " bytes"); @@ -402,7 +405,6 @@ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { int estimatedOutLen = engineGetOutputSize(inLen); - if (out.length - outOfs < estimatedOutLen) { throw new ShortBufferException("Actual: " + (out.length - outOfs) + ". Estimated Out Length: " + estimatedOutLen); diff -r 2867bb7f5b53 -r 591cd107586c jdk/test/javax/crypto/Cipher/EmptyFinalBuffer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/crypto/Cipher/EmptyFinalBuffer.java Wed Sep 28 03:18:01 2016 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016, 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. + * + * 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. + */ + +/** + * @test + * @bug 6946830 + * @summary Test the Cipher.doFinal() with 0-length buffer + * @key randomness + */ + +import java.util.*; +import java.nio.*; + +import java.security.*; + +import javax.crypto.*; +import javax.crypto.spec.*; + +public class EmptyFinalBuffer { + + private static final String[] ALGOS = { + "AES/ECB/PKCS5Padding", "AES/CBC/PKCS5Padding" + }; + + public static void main(String[] args) throws Exception { + + Provider[] provs = Security.getProviders(); + + SecretKey key = new SecretKeySpec(new byte[16], "AES"); + + boolean testFailed = false; + for (Provider p : provs) { + System.out.println("Testing: " + p.getName()); + for (String algo : ALGOS) { + System.out.print("Algo: " + algo); + Cipher c; + try { + c = Cipher.getInstance(algo, p); + } catch (NoSuchAlgorithmException nsae) { + // skip + System.out.println("=> No Support"); + continue; + } + c.init(Cipher.ENCRYPT_MODE, key); + AlgorithmParameters params = c.getParameters(); + c.init(Cipher.DECRYPT_MODE, key, params); + try { + byte[] out = c.doFinal(new byte[0]); + System.out.println("=> Accepted w/ " + + (out == null? "null" : (out.length + "-byte")) + + " output"); + } catch (Exception e) { + testFailed = true; + System.out.println("=> Rejected w/ Exception"); + e.printStackTrace(); + } + } + } + if (testFailed) { + throw new Exception("One or more tests failed"); + } else { + System.out.println("All tests passed"); + } + } +}