Merge
diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index cb07f9a..a3359f0 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -416,3 +416,4 @@
d99f3f935397fdc65dcb776a97110e8ff5cc519c jdk8u51-b10
29612174658436601ba833993227ae96117f632c jdk8u51-b11
f07a9ef02d513435e19fd70abcce2871d1c91342 jdk8u51-b12
+6ef21f3e0946aaab8dc1f4e9cad21dacb97c4f8c jdk8u51-b13
diff --git a/corba/.hgtags b/corba/.hgtags
index 12380e3..3ba7d60 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -414,3 +414,4 @@
0011162b38bf4dab36c72bf25640c59d7128274a jdk8u51-b10
4d59046bdb8a05cfb9e07d8e18d44956f700fe29 jdk8u51-b11
e51a2deadf774452d98b339d65d33c72a466a453 jdk8u51-b12
+4886143e8749caf2ec42a6e77c70a98516e140a3 jdk8u51-b13
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index 79f179f..200b9ea 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -628,3 +628,4 @@
928e1994ad43272f808ca22b9cc1b08a7ce2824f jdk8u51-b10
1a122beb9dc6881850ef1d1250f40a83709b8b72 jdk8u51-b11
05c80f1060f0c0d5720de9eadd09162af1168eab jdk8u51-b12
+07e103f3f43886a3b47945e5295eb5accad505de jdk8u51-b13
diff --git a/jaxp/.hgtags b/jaxp/.hgtags
index 41338f3..1a0b833 100644
--- a/jaxp/.hgtags
+++ b/jaxp/.hgtags
@@ -416,3 +416,4 @@
7aacd8c67160af67f7c9d81974d021eeb229929e jdk8u51-b10
04005432fba4982e5c073be55b917f8a11c838f0 jdk8u51-b11
966c04d5702882603a02f5ba4a4e5d19d47960f6 jdk8u51-b12
+3f5353208a226a31d9ad86018a17fe5f3b102d4b jdk8u51-b13
diff --git a/jaxws/.hgtags b/jaxws/.hgtags
index d4a8013..ad1a4e3 100644
--- a/jaxws/.hgtags
+++ b/jaxws/.hgtags
@@ -414,3 +414,4 @@
783b917616ab6977f9b3157667d1493636ba66d1 jdk8u51-b10
5a69995912aae8bd61758e95a3b5edac167b6acc jdk8u51-b11
1a855f69de645c4bd485d5fb6d9a524994278a16 jdk8u51-b12
+880b67345f557d5613071d09c1cf234a7e1b8759 jdk8u51-b13
diff --git a/jdk/.hgtags b/jdk/.hgtags
index 19b38eb..93f6ad9 100644
--- a/jdk/.hgtags
+++ b/jdk/.hgtags
@@ -419,3 +419,4 @@
dcc75a75d3a30270fbf52d0d0b0504319882e419 jdk8u51-b10
3ed614d4eee7c3225d48ed7c90622dd888cd143e jdk8u51-b11
0010682d9a2b81daf7c08239161f7c2a91977299 jdk8u51-b12
+217fa7205549d196c60f814bf3fc9795d756f493 jdk8u51-b13
diff --git a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java
index d4cd740..f8a3eaa 100644
--- a/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java
+++ b/jdk/src/share/classes/com/sun/crypto/provider/GCTR.java
@@ -38,7 +38,17 @@
* under section 6.5. It needs to be constructed w/ an initialized
* cipher object, and initial counter block(ICB). Given an input X
* of arbitrary length, it processes and returns an output which has
- * the same length as X.
+ * the same length as X. The invariants of this class are:
+ *
+ * (1) The length of intialCounterBlk (and also of its clones, e.g.,
+ * fields counter and counterSave) is equal to AES_BLOCK_SIZE.
+ *
+ * (2) After construction, the field counter never becomes null, it
+ * always contains a byte array of length AES_BLOCK_SIZE.
+ *
+ * If any invariant is broken, failures can occur because the
+ * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM
+ * (see JDK-8067648 for details).
*
* <p>This function is used in the implementation of GCM mode.
*
@@ -59,6 +69,10 @@
// NOTE: cipher should already be initialized
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
this.aes = cipher;
+ if (initialCounterBlk.length != AES_BLOCK_SIZE) {
+ throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length +
+ ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")");
+ }
this.icb = initialCounterBlk;
this.counter = icb.clone();
}
@@ -137,6 +151,8 @@
* Restores the content of this object to the previous saved one.
*/
void restore() {
- this.counter = this.counterSave;
+ if (this.counterSave != null) {
+ this.counter = this.counterSave;
+ }
}
}
diff --git a/jdk/src/share/classes/javax/crypto/CipherInputStream.java b/jdk/src/share/classes/javax/crypto/CipherInputStream.java
index d777ca0..ded3b24 100644
--- a/jdk/src/share/classes/javax/crypto/CipherInputStream.java
+++ b/jdk/src/share/classes/javax/crypto/CipherInputStream.java
@@ -25,7 +25,11 @@
package javax.crypto;
-import java.io.*;
+import java.io.InputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
/**
* A CipherInputStream is composed of an InputStream and a Cipher so
@@ -88,8 +92,6 @@
private int ofinish = 0;
// stream status
private boolean closed = false;
- // The stream has been read from. False if the stream has never been read.
- private boolean read = false;
/**
* private convenience function.
@@ -101,11 +103,15 @@
* return (ofinish-ostart) (we have this many bytes for you)
* return 0 (no data now, but could have more later)
* return -1 (absolutely no more data)
+ *
+ * Note: Exceptions are only thrown after the stream is completely read.
+ * For AEAD ciphers a read() of any length will internally cause the
+ * whole stream to be read fully and verify the authentication tag before
+ * returning decrypted data or exceptions.
*/
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
- read = true;
if (readin == -1) {
done = true;
try {
@@ -308,17 +314,16 @@
closed = true;
input.close();
- try {
- // throw away the unprocessed data
- if (!done) {
+
+ // Throw away the unprocessed data and throw no crypto exceptions.
+ // AEAD ciphers are fully readed before closing. Any authentication
+ // exceptions would occur while reading.
+ if (!done) {
+ try {
cipher.doFinal();
}
- }
- catch (BadPaddingException | IllegalBlockSizeException ex) {
- /* If no data has been read from the stream to be en/decrypted,
- we supress any exceptions, and close quietly. */
- if (read) {
- throw new IOException(ex);
+ catch (BadPaddingException | IllegalBlockSizeException ex) {
+ // Catch exceptions as the rest of the stream is unused.
}
}
ostart = 0;
diff --git a/jdk/test/javax/crypto/Cipher/CipherInputStreamExceptions.java b/jdk/test/javax/crypto/Cipher/CipherInputStreamExceptions.java
new file mode 100644
index 0000000..783967a
--- /dev/null
+++ b/jdk/test/javax/crypto/Cipher/CipherInputStreamExceptions.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2015, 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 8064546
+ * @summary Throw exceptions during reading but not closing of a
+ * CipherInputStream:
+ * - Make sure authenticated algorithms continue to throwing exceptions
+ * when the authentication tag fails verification.
+ * - Make sure other algorithms do not throw exceptions when the stream
+ * calls close() and only throw when read() errors.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.lang.Exception;
+import java.lang.RuntimeException;
+import java.lang.Throwable;
+import java.security.AlgorithmParameters;
+import javax.crypto.AEADBadTagException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.crypto.spec.GCMParameterSpec;
+
+public class CipherInputStreamExceptions {
+
+ static SecretKeySpec key = new SecretKeySpec(new byte[16], "AES");
+ static GCMParameterSpec gcmspec = new GCMParameterSpec(128, new byte[16]);
+ static IvParameterSpec iv = new IvParameterSpec(new byte[16]);
+ static boolean failure = false;
+
+ /* Full read stream, check that getMoreData() is throwing an exception
+ * This test
+ * 1) Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ * 2) Changes the last byte to invalidate the authetication tag.
+ * 3) Fully reads CipherInputStream to decrypt the message and closes
+ */
+
+ static void gcm_AEADBadTag() throws Exception {
+ Cipher c;
+ byte[] read = new byte[200];
+
+ System.out.println("Running gcm_AEADBadTag");
+
+ // Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ byte[] ct = encryptedText("GCM", 100);
+ // Corrupt the encrypted message
+ ct = corruptGCM(ct);
+ // Create stream for decryption
+ CipherInputStream in = getStream("GCM", ct);
+
+ try {
+ int size = in.read(read);
+ throw new RuntimeException("Fail: CipherInputStream.read() " +
+ "returned " + size + " and didn't throw an exception.");
+ } catch (IOException e) {
+ Throwable ec = e.getCause();
+ if (ec instanceof AEADBadTagException) {
+ System.out.println(" Pass.");
+ } else {
+ System.out.println(" Fail: " + ec.getMessage());
+ throw new RuntimeException(ec);
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ /* Short read stream,
+ * This test
+ * 1) Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ * 2) Reads 100 bytes from stream to decrypt the message and closes
+ * 3) Make sure no value is returned by read()
+ * 4) Make sure no exception is thrown
+ */
+
+ static void gcm_shortReadAEAD() throws Exception {
+ Cipher c;
+ byte[] read = new byte[100];
+
+ System.out.println("Running gcm_shortReadAEAD");
+
+ byte[] pt = new byte[600];
+ pt[0] = 1;
+ // Encrypt provided 600 bytes with AES/GCM/PKCS5Padding
+ byte[] ct = encryptedText("GCM", pt);
+ // Create stream for decryption
+ CipherInputStream in = getStream("GCM", ct);
+
+ int size = 0;
+ try {
+ size = in.read(read);
+ in.close();
+ if (read.length != 100) {
+ throw new RuntimeException("Fail: read size = " + read.length +
+ "should be 100.");
+ }
+ if (read[0] != 1) {
+ throw new RuntimeException("Fail: The decrypted text does " +
+ "not match the plaintext: '" + read[0] +"'");
+ }
+ } catch (IOException e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ System.out.println(" Pass.");
+ }
+
+ /*
+ * Verify doFinal() exception is suppressed when input stream is not
+ * read before it is closed.
+ * This test:
+ * 1) Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ * 2) Changes the last byte to invalidate the authetication tag.
+ * 3) Opens a CipherInputStream and the closes it. Never reads from it.
+ *
+ * There should be no exception thrown.
+ */
+ static void gcm_suppressUnreadCorrupt() throws Exception {
+ Cipher c;
+ byte[] read = new byte[200];
+
+ System.out.println("Running supressUnreadCorrupt test");
+
+ // Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ byte[] ct = encryptedText("GCM", 100);
+ // Corrupt the encrypted message
+ ct = corruptGCM(ct);
+ // Create stream for decryption
+ CipherInputStream in = getStream("GCM", ct);
+
+ try {
+ in.close();
+ System.out.println(" Pass.");
+ } catch (IOException e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /*
+ * Verify noexception thrown when 1 byte is read from a GCM stream
+ * and then closed
+ * This test:
+ * 1) Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ * 2) Read one byte from the stream, expect no exception thrown.
+ * 4) Close stream,expect no exception thrown.
+ */
+ static void gcm_oneReadByte() throws Exception {
+
+ System.out.println("Running gcm_oneReadByte test");
+
+ // Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ byte[] ct = encryptedText("GCM", 100);
+ // Create stream for decryption
+ CipherInputStream in = getStream("GCM", ct);
+
+ try {
+ in.read();
+ System.out.println(" Pass.");
+ } catch (Exception e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /*
+ * Verify exception thrown when 1 byte is read from a corrupted GCM stream
+ * and then closed
+ * This test:
+ * 1) Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ * 2) Changes the last byte to invalidate the authetication tag.
+ * 3) Read one byte from the stream, expect exception thrown.
+ * 4) Close stream,expect no exception thrown.
+ */
+ static void gcm_oneReadByteCorrupt() throws Exception {
+
+ System.out.println("Running gcm_oneReadByteCorrupt test");
+
+ // Encrypt 100 bytes with AES/GCM/PKCS5Padding
+ byte[] ct = encryptedText("GCM", 100);
+ // Corrupt the encrypted message
+ ct = corruptGCM(ct);
+ // Create stream for decryption
+ CipherInputStream in = getStream("GCM", ct);
+
+ try {
+ in.read();
+ System.out.println(" Fail. No exception thrown.");
+ } catch (IOException e) {
+ Throwable ec = e.getCause();
+ if (ec instanceof AEADBadTagException) {
+ System.out.println(" Pass.");
+ } else {
+ System.out.println(" Fail: " + ec.getMessage());
+ throw new RuntimeException(ec);
+ }
+ }
+ }
+
+ /* Check that close() does not throw an exception with full message in
+ * CipherInputStream's ibuffer.
+ * This test:
+ * 1) Encrypts a 97 byte message with AES/CBC/PKCS5Padding
+ * 2) Create a stream that sends 96 bytes.
+ * 3) Read stream once,
+ * 4) Close and expect no exception
+ */
+
+ static void cbc_shortStream() throws Exception {
+ Cipher c;
+ AlgorithmParameters params;
+ byte[] read = new byte[200];
+
+ System.out.println("Running cbc_shortStream");
+
+ // Encrypt 97 byte with AES/CBC/PKCS5Padding
+ byte[] ct = encryptedText("CBC", 97);
+ // Create stream with only 96 bytes of encrypted data
+ CipherInputStream in = getStream("CBC", ct, 96);
+
+ try {
+ int size = in.read(read);
+ in.close();
+ if (size != 80) {
+ throw new RuntimeException("Fail: CipherInputStream.read() " +
+ "returned " + size + ". Should have been 80");
+ }
+ System.out.println(" Pass.");
+ } catch (IOException e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /* Check that close() does not throw an exception when the whole message is
+ * inside the internal buffer (ibuffer) in CipherInputStream and we read
+ * one byte and close the stream.
+ * This test:
+ * 1) Encrypts a 400 byte message with AES/CBC/PKCS5Padding
+ * 2) Read one byte from the stream
+ * 3) Close and expect no exception
+ */
+
+ static void cbc_shortRead400() throws Exception {
+ System.out.println("Running cbc_shortRead400");
+
+ // Encrypt 400 byte with AES/CBC/PKCS5Padding
+ byte[] ct = encryptedText("CBC", 400);
+ // Create stream with encrypted data
+ CipherInputStream in = getStream("CBC", ct);
+
+ try {
+ in.read();
+ in.close();
+ System.out.println(" Pass.");
+ } catch (IOException e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /* Check that close() does not throw an exception when the inside the
+ * internal buffer (ibuffer) in CipherInputStream does not contain the
+ * whole message.
+ * This test:
+ * 1) Encrypts a 600 byte message with AES/CBC/PKCS5Padding
+ * 2) Read one byte from the stream
+ * 3) Close and expect no exception
+ */
+
+ static void cbc_shortRead600() throws Exception {
+ System.out.println("Running cbc_shortRead600");
+
+ // Encrypt 600 byte with AES/CBC/PKCS5Padding
+ byte[] ct = encryptedText("CBC", 600);
+ // Create stream with encrypted data
+ CipherInputStream in = getStream("CBC", ct);
+
+ try {
+ in.read();
+ in.close();
+ System.out.println(" Pass.");
+ } catch (IOException e) {
+ System.out.println(" Fail: " + e.getMessage());
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /* Check that exception is thrown when message is fully read
+ * This test:
+ * 1) Encrypts a 96 byte message with AES/CBC/PKCS5Padding
+ * 2) Create a stream that sends 95 bytes.
+ * 3) Read stream to the end
+ * 4) Expect IllegalBlockSizeException thrown
+ */
+
+ static void cbc_readAllIllegalBlockSize() throws Exception {
+ byte[] read = new byte[200];
+
+ System.out.println("Running cbc_readAllIllegalBlockSize test");
+
+ // Encrypt 96 byte with AES/CBC/PKCS5Padding
+ byte[] ct = encryptedText("CBC", 96);
+ // Create a stream with only 95 bytes of encrypted data
+ CipherInputStream in = getStream("CBC", ct, 95);
+
+ try {
+ int s, size = 0;
+ while ((s = in.read(read)) != -1) {
+ size += s;
+ }
+ throw new RuntimeException("Fail: No IllegalBlockSizeException. " +
+ "CipherInputStream.read() returned " + size);
+
+ } catch (IOException e) {
+ Throwable ec = e.getCause();
+ if (ec instanceof IllegalBlockSizeException) {
+ System.out.println(" Pass.");
+ } else {
+ System.out.println(" Fail: " + ec.getMessage());
+ throw new RuntimeException(ec);
+ }
+ }
+ }
+
+ /* Generic method to create encrypted text */
+ static byte[] encryptedText(String mode, int length) throws Exception{
+ return encryptedText(mode, new byte[length]);
+ }
+
+ /* Generic method to create encrypted text */
+ static byte[] encryptedText(String mode, byte[] pt) throws Exception{
+ Cipher c;
+ if (mode.compareTo("GCM") == 0) {
+ c = Cipher.getInstance("AES/GCM/PKCS5Padding", "SunJCE");
+ c.init(Cipher.ENCRYPT_MODE, key, gcmspec);
+ } else if (mode.compareTo("CBC") == 0) {
+ c = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
+ c.init(Cipher.ENCRYPT_MODE, key, iv);
+ } else {
+ return null;
+ }
+
+ return c.doFinal(pt);
+ }
+
+ /* Generic method to get a properly setup CipherInputStream */
+ static CipherInputStream getStream(String mode, byte[] ct) throws Exception {
+ return getStream(mode, ct, ct.length);
+ }
+
+ /* Generic method to get a properly setup CipherInputStream */
+ static CipherInputStream getStream(String mode, byte[] ct, int length)
+ throws Exception {
+ Cipher c;
+
+ if (mode.compareTo("GCM") == 0) {
+ c = Cipher.getInstance("AES/GCM/PKCS5Padding", "SunJCE");
+ c.init(Cipher.DECRYPT_MODE, key, gcmspec);
+ } else if (mode.compareTo("CBC") == 0) {
+ c = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
+ c.init(Cipher.DECRYPT_MODE, key, iv);
+ } else {
+ return null;
+ }
+
+ return new CipherInputStream(new ByteArrayInputStream(ct, 0, length), c);
+
+ }
+
+ /* Generic method for corrupting a GCM message. Change the last
+ * byte on of the authentication tag
+ */
+ static byte[] corruptGCM(byte[] ct) {
+ ct[ct.length - 1] = (byte) (ct[ct.length - 1] + 1);
+ return ct;
+ }
+
+ public static void main(String[] args) throws Exception {
+ gcm_AEADBadTag();
+ gcm_shortReadAEAD();
+ gcm_suppressUnreadCorrupt();
+ gcm_oneReadByte();
+ gcm_oneReadByteCorrupt();
+ cbc_shortStream();
+ cbc_shortRead400();
+ cbc_shortRead600();
+ cbc_readAllIllegalBlockSize();
+ }
+}
diff --git a/langtools/.hgtags b/langtools/.hgtags
index 6959516..fd4e8f8 100644
--- a/langtools/.hgtags
+++ b/langtools/.hgtags
@@ -414,3 +414,4 @@
417f734de62d74c69e3a8465340bfb3aca60151a jdk8u51-b10
8ac1243890d4f427a32320b81ae1be38f81f0c62 jdk8u51-b11
f65c2fc549b5e9184da67e3a4f81260c27a88010 jdk8u51-b12
+3836d67a94a92befedd97064358270c6f0760e5c jdk8u51-b13
diff --git a/nashorn/.hgtags b/nashorn/.hgtags
index e266495..3a9d234 100644
--- a/nashorn/.hgtags
+++ b/nashorn/.hgtags
@@ -402,3 +402,4 @@
1480e27e4af65e809c1a5cc0310616c2c68392f2 jdk8u51-b10
6e95b9bb2f67d4a77d28e5aa3c07281fc8424823 jdk8u51-b11
bf2fe867628bf323262377f8312c85f440a9b246 jdk8u51-b12
+1ecbb6d582a6ccf9e0f6e359a925155c8c580bac jdk8u51-b13