new TlsKeymaterialSpec(clientMacKey, serverMacKey,
* null, null, null, null)
.
*
- * @param clientMacKey the client MAC key
- * @param serverMacKey the server MAC key
- * @throws NullPointerException if clientMacKey or serverMacKey is null
+ * @param clientMacKey the client MAC key (or null)
+ * @param serverMacKey the server MAC key (or null)
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey) {
this(clientMacKey, serverMacKey, null, null, null, null);
@@ -73,11 +72,10 @@ public class TlsKeyMaterialSpec implements KeySpec, SecretKey {
* new TlsKeymaterialSpec(clientMacKey, serverMacKey,
* clientCipherKey, serverCipherKey, null, null)
.
*
- * @param clientMacKey the client MAC key
- * @param serverMacKey the server MAC key
+ * @param clientMacKey the client MAC key (or null)
+ * @param serverMacKey the server MAC key (or null)
* @param clientCipherKey the client cipher key (or null)
* @param serverCipherKey the server cipher key (or null)
- * @throws NullPointerException if clientMacKey or serverMacKey is null
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey,
SecretKey clientCipherKey, SecretKey serverCipherKey) {
@@ -90,21 +88,17 @@ public class TlsKeyMaterialSpec implements KeySpec, SecretKey {
* keys, client and server cipher keys, and client and server
* initialization vectors.
*
- * @param clientMacKey the client MAC key
- * @param serverMacKey the server MAC key
+ * @param clientMacKey the client MAC key (or null)
+ * @param serverMacKey the server MAC key (or null)
* @param clientCipherKey the client cipher key (or null)
* @param clientIv the client initialization vector (or null)
* @param serverCipherKey the server cipher key (or null)
* @param serverIv the server initialization vector (or null)
- *
- * @throws NullPointerException if clientMacKey or serverMacKey is null
*/
public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey,
SecretKey clientCipherKey, IvParameterSpec clientIv,
SecretKey serverCipherKey, IvParameterSpec serverIv) {
- if ((clientMacKey == null) || (serverMacKey == null)) {
- throw new NullPointerException("MAC keys must not be null");
- }
+
this.clientMacKey = clientMacKey;
this.serverMacKey = serverMacKey;
this.clientCipherKey = clientCipherKey;
@@ -143,7 +137,7 @@ public class TlsKeyMaterialSpec implements KeySpec, SecretKey {
/**
* Returns the client MAC key.
*
- * @return the client MAC key.
+ * @return the client MAC key (or null).
*/
public SecretKey getClientMacKey() {
return clientMacKey;
@@ -152,7 +146,7 @@ public class TlsKeyMaterialSpec implements KeySpec, SecretKey {
/**
* Return the server MAC key.
*
- * @return the server MAC key.
+ * @return the server MAC key (or null).
*/
public SecretKey getServerMacKey() {
return serverMacKey;
diff --git a/src/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java b/src/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java
index 94e731b01f920af2856446e2257b6cbcb89a4460..14d1708537a1cb64922b65b81514f12f32275007 100644
--- a/src/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java
+++ b/src/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2012, 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
@@ -168,10 +168,22 @@ public final class P11TlsKeyMaterialGenerator extends KeyGeneratorSpi {
// Note that the MAC keys do not inherit all attributes from the
// template, but they do inherit the sensitive/extractable/token
// flags, which is all P11Key cares about.
- SecretKey clientMacKey = P11Key.secretKey
+ SecretKey clientMacKey, serverMacKey;
+
+ // The MAC size may be zero for GCM mode.
+ //
+ // PKCS11 does not support GCM mode as the author made the comment,
+ // so the macBits is unlikely to be zero. It's only a place holder.
+ if (macBits != 0) {
+ clientMacKey = P11Key.secretKey
(session, out.hClientMacSecret, "MAC", macBits, attributes);
- SecretKey serverMacKey = P11Key.secretKey
+ serverMacKey = P11Key.secretKey
(session, out.hServerMacSecret, "MAC", macBits, attributes);
+ } else {
+ clientMacKey = null;
+ serverMacKey = null;
+ }
+
SecretKey clientCipherKey, serverCipherKey;
if (keyBits != 0) {
clientCipherKey = P11Key.secretKey(session, out.hClientKey,
diff --git a/src/share/classes/sun/security/provider/certpath/OCSP.java b/src/share/classes/sun/security/provider/certpath/OCSP.java
index f57c832bfa5474b893e9587361e044be2d931a88..03c910b8897fca6e56da7b0088927dd81f9e4284 100644
--- a/src/share/classes/sun/security/provider/certpath/OCSP.java
+++ b/src/share/classes/sun/security/provider/certpath/OCSP.java
@@ -89,7 +89,7 @@ public final class OCSP {
new GetIntegerAction("com.sun.security.ocsp.timeout",
DEFAULT_CONNECT_TIMEOUT));
if (tmp < 0) {
- tmp = DEFAULT_CONNECT_TIMEOUT;
+ return DEFAULT_CONNECT_TIMEOUT;
}
// Convert to milliseconds, as the system property will be
// specified in seconds
diff --git a/src/share/classes/sun/security/ssl/Authenticator.java b/src/share/classes/sun/security/ssl/Authenticator.java
new file mode 100644
index 0000000000000000000000000000000000000000..a97ce7d5f0caf33e1bc7c96ce7466284afd496aa
--- /dev/null
+++ b/src/share/classes/sun/security/ssl/Authenticator.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2012, 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.ssl;
+
+import java.util.Arrays;
+
+/**
+ * This class represents an SSL/TLS message authentication token,
+ * which encapsulates a sequence number and ensures that attempts to
+ * delete or reorder messages can be detected.
+ *
+ * Each SSL/TLS connection state contains a sequence number, which
+ * is maintained separately for read and write states. The sequence
+ * number MUST be set to zero whenever a connection state is made the
+ * active state. Sequence numbers are of type uint64 and may not
+ * exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS
+ * implementation would need to wrap a sequence number, it must
+ * renegotiate instead. A sequence number is incremented after each
+ * record: specifically, the first record transmitted under a
+ * particular connection state MUST use sequence number 0.
+ */
+class Authenticator {
+
+ // byte array containing the additional authentication information for
+ // each record
+ private final byte[] block;
+
+ // the block size of SSL v3.0:
+ // sequence number + record type + + record length
+ private static final int BLOCK_SIZE_SSL = 8 + 1 + 2;
+
+ // the block size of TLS v1.0 and later:
+ // sequence number + record type + protocol version + record length
+ private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
+
+ /**
+ * Default construct, no message authentication token is initialized.
+ *
+ * Note that this construct can only be called for null MAC
+ */
+ Authenticator() {
+ block = new byte[0];
+ }
+
+ /**
+ * Constructs the message authentication token for the specified
+ * SSL/TLS protocol.
+ */
+ Authenticator(ProtocolVersion protocolVersion) {
+ if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
+ block = new byte[BLOCK_SIZE_TLS];
+ block[9] = protocolVersion.major;
+ block[10] = protocolVersion.minor;
+ } else {
+ block = new byte[BLOCK_SIZE_SSL];
+ }
+ }
+
+ /**
+ * Checks whether the sequence number is close to wrap.
+ *
+ * Sequence numbers are of type uint64 and may not exceed 2^64-1.
+ * Sequence numbers do not wrap. When the sequence number is near
+ * to wrap, we need to close the connection immediately.
+ *
+ * @return true if the sequence number is close to wrap
+ */
+ final boolean seqNumOverflow() {
+ /*
+ * Conservatively, we don't allow more records to be generated
+ * when there are only 2^8 sequence numbers left.
+ */
+ return (block.length != 0 &&
+ block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+ block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+ block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+ block[6] == (byte)0xFF);
+ }
+
+ /**
+ * Checks whether the sequence number close to renew.
+ *
+ * Sequence numbers are of type uint64 and may not exceed 2^64-1.
+ * Sequence numbers do not wrap. If a TLS
+ * implementation would need to wrap a sequence number, it must
+ * renegotiate instead.
+ *
+ * @return true if the sequence number is huge enough to renew
+ */
+ final boolean seqNumIsHuge() {
+ /*
+ * Conservatively, we should ask for renegotiation when there are
+ * only 2^48 sequence numbers left.
+ */
+ return (block.length != 0 &&
+ block[0] == (byte)0xFF && block[1] == (byte)0xFF);
+ }
+
+ /**
+ * Gets the current sequence number.
+ *
+ * @return the byte array of the current sequence number
+ */
+ final byte[] sequenceNumber() {
+ return Arrays.copyOf(block, 8);
+ }
+
+ /**
+ * Acquires the current message authentication information with the
+ * specified record type and fragment length, and then increases the
+ * sequence number.
+ *
+ * @param type the record type
+ * @param length the fragment of the record
+ * @return the byte array of the current message authentication information
+ */
+ final byte[] acquireAuthenticationBytes(byte type, int length) {
+ byte[] copy = block.clone();
+
+ if (block.length != 0) {
+ copy[8] = type;
+ copy[copy.length - 2] = (byte)(length >> 8);
+ copy[copy.length - 1] = (byte)(length);
+
+ /*
+ * Increase the sequence number in the block array
+ * it is a 64-bit number stored in big-endian format
+ */
+ int k = 7;
+ while ((k >= 0) && (++block[k] == 0)) {
+ k--;
+ }
+ }
+
+ return copy;
+ }
+
+}
diff --git a/src/share/classes/sun/security/ssl/CipherBox.java b/src/share/classes/sun/security/ssl/CipherBox.java
index 362f9b5d904c56e0bc3a81a920abcd9950184b4c..bce44dabda3f4b50162cb2528a6f425d1e188edc 100644
--- a/src/share/classes/sun/security/ssl/CipherBox.java
+++ b/src/share/classes/sun/security/ssl/CipherBox.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, 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
@@ -29,15 +29,18 @@ package sun.security.ssl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Hashtable;
+import java.util.Arrays;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.GCMParameterSpec;
import java.nio.*;
import sun.security.ssl.CipherSuite.*;
import static sun.security.ssl.CipherSuite.*;
+import static sun.security.ssl.CipherSuite.CipherType.*;
import sun.misc.HexDumpEncoder;
@@ -102,19 +105,40 @@ final class CipherBox {
private final Cipher cipher;
/**
- * Cipher blocksize, 0 for stream ciphers
+ * secure random
*/
- private int blockSize;
+ private SecureRandom random;
/**
- * secure random
+ * fixed IV, the implicit nonce of AEAD cipher suite, only apply to
+ * AEAD cipher suites
*/
- private SecureRandom random;
+ private final byte[] fixedIv;
/**
- * Is the cipher of CBC mode?
+ * the key, reserved only for AEAD cipher initialization
*/
- private final boolean isCBCMode;
+ private final Key key;
+
+ /**
+ * the operation mode, reserved for AEAD cipher initialization
+ */
+ private final int mode;
+
+ /**
+ * the authentication tag size, only apply to AEAD cipher suites
+ */
+ private final int tagSize;
+
+ /**
+ * the record IV length, only apply to AEAD cipher suites
+ */
+ private final int recordIvSize;
+
+ /**
+ * cipher type
+ */
+ private final CipherType cipherType;
/**
* Fixed masks of various block size, as the initial decryption IVs
@@ -132,7 +156,13 @@ final class CipherBox {
private CipherBox() {
this.protocolVersion = ProtocolVersion.DEFAULT;
this.cipher = null;
- this.isCBCMode = false;
+ this.cipherType = STREAM_CIPHER;
+ this.fixedIv = new byte[0];
+ this.key = null;
+ this.mode = Cipher.ENCRYPT_MODE; // choose at random
+ this.random = null;
+ this.tagSize = 0;
+ this.recordIvSize = 0;
}
/**
@@ -147,13 +177,13 @@ final class CipherBox {
try {
this.protocolVersion = protocolVersion;
this.cipher = JsseJce.getCipher(bulkCipher.transformation);
- int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+ this.mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
if (random == null) {
random = JsseJce.getSecureRandom();
}
this.random = random;
- this.isCBCMode = bulkCipher.isCBCMode;
+ this.cipherType = bulkCipher.cipherType;
/*
* RFC 4346 recommends two algorithms used to generated the
@@ -171,14 +201,40 @@ final class CipherBox {
iv = getFixedMask(bulkCipher.ivSize);
}
- cipher.init(mode, key, iv, random);
+ if (cipherType == AEAD_CIPHER) {
+ // AEAD must completely initialize the cipher for each packet,
+ // and so we save initialization parameters for packet
+ // processing time.
+
+ // Set the tag size for AEAD cipher
+ tagSize = bulkCipher.tagSize;
+
+ // Reserve the key for AEAD cipher initialization
+ this.key = key;
+
+ fixedIv = iv.getIV();
+ if (fixedIv == null ||
+ fixedIv.length != bulkCipher.fixedIvSize) {
+ throw new RuntimeException("Improper fixed IV for AEAD");
+ }
+
+ // Set the record IV length for AEAD cipher
+ recordIvSize = bulkCipher.ivSize - bulkCipher.fixedIvSize;
- // Do not call getBlockSize until after init()
- // otherwise we would disrupt JCE delayed provider selection
- blockSize = cipher.getBlockSize();
- // some providers implement getBlockSize() incorrectly
- if (blockSize == 1) {
- blockSize = 0;
+ // DON'T initialize the cipher for AEAD!
+ } else {
+ // CBC only requires one initialization during its lifetime
+ // (future packets/IVs set the proper CBC state), so we can
+ // initialize now.
+
+ // Zeroize the variables that only apply to AEAD cipher
+ this.tagSize = 0;
+ this.fixedIv = new byte[0];
+ this.recordIvSize = 0;
+ this.key = null;
+
+ // Initialize the cipher
+ cipher.init(mode, key, iv, random);
}
} catch (NoSuchAlgorithmException e) {
throw e;
@@ -235,26 +291,11 @@ final class CipherBox {
}
try {
- if (blockSize != 0) {
- // TLSv1.1 needs a IV block
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
- // generate a random number
- byte[] prefix = new byte[blockSize];
- random.nextBytes(prefix);
-
- // move forward the plaintext
- System.arraycopy(buf, offset,
- buf, offset + prefix.length, len);
-
- // prefix the plaintext
- System.arraycopy(prefix, 0,
- buf, offset, prefix.length);
-
- len += prefix.length;
- }
-
+ int blockSize = cipher.getBlockSize();
+ if (cipherType == BLOCK_CIPHER) {
len = addPadding(buf, offset, len, blockSize);
}
+
if (debug != null && Debug.isOn("plaintext")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
@@ -267,14 +308,28 @@ final class CipherBox {
System.out);
} catch (IOException e) { }
}
- int newLen = cipher.update(buf, offset, len, buf, offset);
- if (newLen != len) {
- // catch BouncyCastle buffering error
- throw new RuntimeException("Cipher buffering error " +
- "in JCE provider " + cipher.getProvider().getName());
+
+
+ if (cipherType == AEAD_CIPHER) {
+ try {
+ return cipher.doFinal(buf, offset, len, buf, offset);
+ } catch (IllegalBlockSizeException | BadPaddingException ibe) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "Cipher error in AEAD mode in JCE provider " +
+ cipher.getProvider().getName(), ibe);
+ }
+ } else {
+ int newLen = cipher.update(buf, offset, len, buf, offset);
+ if (newLen != len) {
+ // catch BouncyCastle buffering error
+ throw new RuntimeException("Cipher buffering error " +
+ "in JCE provider " + cipher.getProvider().getName());
+ }
+ return newLen;
}
- return newLen;
} catch (ShortBufferException e) {
+ // unlikely to happen, we should have enough buffer space here
throw new ArrayIndexOutOfBoundsException(e.toString());
}
}
@@ -288,7 +343,7 @@ final class CipherBox {
* set to last position padded/encrypted. The limit may have changed
* because of the added padding bytes.
*/
- int encrypt(ByteBuffer bb) {
+ int encrypt(ByteBuffer bb, int outLimit) {
int len = bb.remaining();
@@ -297,65 +352,70 @@ final class CipherBox {
return len;
}
- try {
- int pos = bb.position();
+ int pos = bb.position();
- if (blockSize != 0) {
- // TLSv1.1 needs a IV block
- if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
- // generate a random number
- byte[] prefix = new byte[blockSize];
- random.nextBytes(prefix);
-
- // move forward the plaintext
- byte[] buf = null;
- int limit = bb.limit();
- if (bb.hasArray()) {
- int arrayOffset = bb.arrayOffset();
- buf = bb.array();
- System.arraycopy(buf, arrayOffset + pos,
- buf, arrayOffset + pos + prefix.length,
- limit - pos);
- bb.limit(limit + prefix.length);
- } else {
- buf = new byte[limit - pos];
- bb.get(buf, 0, limit - pos);
- bb.position(pos + prefix.length);
- bb.limit(limit + prefix.length);
- bb.put(buf);
- }
- bb.position(pos);
+ int blockSize = cipher.getBlockSize();
+ if (cipherType == BLOCK_CIPHER) {
+ // addPadding adjusts pos/limit
+ len = addPadding(bb, blockSize);
+ bb.position(pos);
+ }
- // prefix the plaintext
- bb.put(prefix);
- bb.position(pos);
- }
+ if (debug != null && Debug.isOn("plaintext")) {
+ try {
+ HexDumpEncoder hd = new HexDumpEncoder();
- // addPadding adjusts pos/limit
- len = addPadding(bb, blockSize);
- bb.position(pos);
- }
- if (debug != null && Debug.isOn("plaintext")) {
- try {
- HexDumpEncoder hd = new HexDumpEncoder();
+ System.out.println(
+ "Padded plaintext before ENCRYPTION: len = "
+ + len);
+ hd.encodeBuffer(bb.duplicate(), System.out);
- System.out.println(
- "Padded plaintext before ENCRYPTION: len = "
- + len);
- hd.encodeBuffer(bb, System.out);
+ } catch (IOException e) { }
+ }
- } catch (IOException e) { }
- /*
- * reset back to beginning
- */
- bb.position(pos);
+ /*
+ * Encrypt "in-place". This does not add its own padding.
+ */
+ ByteBuffer dup = bb.duplicate();
+ if (cipherType == AEAD_CIPHER) {
+ try {
+ int outputSize = cipher.getOutputSize(dup.remaining());
+ if (outputSize > bb.remaining()) {
+ // need to expand the limit of the output buffer for
+ // the authentication tag.
+ //
+ // DON'T worry about the buffer's capacity, we have
+ // reserved space for the authentication tag.
+ if (outLimit < pos + outputSize) {
+ // unlikely to happen
+ throw new ShortBufferException(
+ "need more space in output buffer");
+ }
+ bb.limit(pos + outputSize);
+ }
+ int newLen = cipher.doFinal(dup, bb);
+ if (newLen != outputSize) {
+ throw new RuntimeException(
+ "Cipher buffering error in JCE provider " +
+ cipher.getProvider().getName());
+ }
+ return newLen;
+ } catch (IllegalBlockSizeException |
+ BadPaddingException | ShortBufferException ibse) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "Cipher error in AEAD mode in JCE provider " +
+ cipher.getProvider().getName(), ibse);
+ }
+ } else {
+ int newLen;
+ try {
+ newLen = cipher.update(dup, bb);
+ } catch (ShortBufferException sbe) {
+ // unlikely to happen
+ throw new RuntimeException("Cipher buffering error " +
+ "in JCE provider " + cipher.getProvider().getName());
}
-
- /*
- * Encrypt "in-place". This does not add its own padding.
- */
- ByteBuffer dup = bb.duplicate();
- int newLen = cipher.update(dup, bb);
if (bb.position() != dup.position()) {
throw new RuntimeException("bytebuffer padding error");
@@ -367,10 +427,6 @@ final class CipherBox {
"in JCE provider " + cipher.getProvider().getName());
}
return newLen;
- } catch (ShortBufferException e) {
- RuntimeException exc = new RuntimeException(e.toString());
- exc.initCause(e);
- throw exc;
}
}
@@ -398,11 +454,23 @@ final class CipherBox {
}
try {
- int newLen = cipher.update(buf, offset, len, buf, offset);
- if (newLen != len) {
- // catch BouncyCastle buffering error
- throw new RuntimeException("Cipher buffering error " +
- "in JCE provider " + cipher.getProvider().getName());
+ int newLen;
+ if (cipherType == AEAD_CIPHER) {
+ try {
+ newLen = cipher.doFinal(buf, offset, len, buf, offset);
+ } catch (IllegalBlockSizeException ibse) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "Cipher error in AEAD mode in JCE provider " +
+ cipher.getProvider().getName(), ibse);
+ }
+ } else {
+ newLen = cipher.update(buf, offset, len, buf, offset);
+ if (newLen != len) {
+ // catch BouncyCastle buffering error
+ throw new RuntimeException("Cipher buffering error " +
+ "in JCE provider " + cipher.getProvider().getName());
+ }
}
if (debug != null && Debug.isOn("plaintext")) {
try {
@@ -416,7 +484,9 @@ final class CipherBox {
System.out);
} catch (IOException e) { }
}
- if (blockSize != 0) {
+
+ if (cipherType == BLOCK_CIPHER) {
+ int blockSize = cipher.getBlockSize();
newLen = removePadding(buf, offset, newLen,
blockSize, protocolVersion);
@@ -424,16 +494,11 @@ final class CipherBox {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
-
- // discards the first cipher block, the IV component.
- System.arraycopy(buf, offset + blockSize,
- buf, offset, newLen - blockSize);
-
- newLen -= blockSize;
}
}
return newLen;
} catch (ShortBufferException e) {
+ // unlikely to happen, we should have enough buffer space here
throw new ArrayIndexOutOfBoundsException(e.toString());
}
}
@@ -463,15 +528,29 @@ final class CipherBox {
*/
int pos = bb.position();
ByteBuffer dup = bb.duplicate();
- int newLen = cipher.update(dup, bb);
- if (newLen != len) {
- // catch BouncyCastle buffering error
- throw new RuntimeException("Cipher buffering error " +
- "in JCE provider " + cipher.getProvider().getName());
+ int newLen;
+ if (cipherType == AEAD_CIPHER) {
+ try {
+ newLen = cipher.doFinal(dup, bb);
+ } catch (IllegalBlockSizeException ibse) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "Cipher error in AEAD mode \"" + ibse.getMessage() +
+ " \"in JCE provider " + cipher.getProvider().getName());
+ }
+ } else {
+ newLen = cipher.update(dup, bb);
+ if (newLen != len) {
+ // catch BouncyCastle buffering error
+ throw new RuntimeException("Cipher buffering error " +
+ "in JCE provider " + cipher.getProvider().getName());
+ }
}
+ // reset the limit to the end of the decryted data
+ bb.limit(pos + newLen);
+
if (debug != null && Debug.isOn("plaintext")) {
- bb.position(pos);
try {
HexDumpEncoder hd = new HexDumpEncoder();
@@ -479,50 +558,33 @@ final class CipherBox {
"Padded plaintext after DECRYPTION: len = "
+ newLen);
- hd.encodeBuffer(bb, System.out);
+ hd.encodeBuffer(
+ (ByteBuffer)bb.duplicate().position(pos), System.out);
} catch (IOException e) { }
}
/*
* Remove the block padding.
*/
- if (blockSize != 0) {
+ if (cipherType == BLOCK_CIPHER) {
+ int blockSize = cipher.getBlockSize();
bb.position(pos);
newLen = removePadding(bb, blockSize, protocolVersion);
+ // check the explicit IV of TLS v1.1 or later
if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV");
}
- // discards the first cipher block, the IV component.
- byte[] buf = null;
- int limit = bb.limit();
- if (bb.hasArray()) {
- int arrayOffset = bb.arrayOffset();
- buf = bb.array();
- System.arraycopy(buf, arrayOffset + pos + blockSize,
- buf, arrayOffset + pos, limit - pos - blockSize);
- bb.limit(limit - blockSize);
- } else {
- buf = new byte[limit - pos - blockSize];
- bb.position(pos + blockSize);
- bb.get(buf);
- bb.position(pos);
- bb.put(buf);
- bb.limit(limit - blockSize);
- }
-
// reset the position to the end of the decrypted data
- limit = bb.limit();
- bb.position(limit);
+ bb.position(bb.limit());
}
}
return newLen;
} catch (ShortBufferException e) {
- RuntimeException exc = new RuntimeException(e.toString());
- exc.initCause(e);
- throw exc;
+ // unlikely to happen, we should have enough buffer space here
+ throw new ArrayIndexOutOfBoundsException(e.toString());
}
}
@@ -695,8 +757,8 @@ final class CipherBox {
// ignore return value.
cipher.doFinal();
}
- } catch (GeneralSecurityException e) {
- // swallow for now.
+ } catch (Exception e) {
+ // swallow all types of exceptions.
}
}
@@ -706,6 +768,234 @@ final class CipherBox {
* @return true if the cipher use CBC mode, false otherwise.
*/
boolean isCBCMode() {
- return isCBCMode;
+ return cipherType == BLOCK_CIPHER;
+ }
+
+ /*
+ * Does the cipher use AEAD mode?
+ *
+ * @return true if the cipher use AEAD mode, false otherwise.
+ */
+ boolean isAEADMode() {
+ return cipherType == AEAD_CIPHER;
+ }
+
+ /*
+ * Is the cipher null?
+ *
+ * @return true if the cipher is null, false otherwise.
+ */
+ boolean isNullCipher() {
+ return cipher == null;
+ }
+
+ /*
+ * Gets the explicit nonce/IV size of the cipher.
+ *
+ * The returned value is the SecurityParameters.record_iv_length in
+ * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
+ * size of explicit nonce for AEAD mode.
+ *
+ * @return the explicit nonce size of the cipher.
+ */
+ int getExplicitNonceSize() {
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ // For block ciphers, the explicit IV length is of length
+ // SecurityParameters.record_iv_length, which is equal to
+ // the SecurityParameters.block_size.
+ if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ return cipher.getBlockSize();
+ }
+ break;
+ case AEAD_CIPHER:
+ return recordIvSize;
+ // It is also the length of sequence number, which is
+ // used as the nonce_explicit for AEAD cipher suites.
+ }
+
+ return 0;
+ }
+
+ /*
+ * Applies the explicit nonce/IV to this cipher. This method is used to
+ * decrypt an SSL/TLS input record.
+ *
+ * The returned value is the SecurityParameters.record_iv_length in
+ * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
+ * size of explicit nonce for AEAD mode.
+ *
+ * @param authenticator the authenticator to get the additional
+ * authentication data
+ * @param contentType the content type of the input record
+ * @param bb the byte buffer to get the explicit nonce from
+ *
+ * @return the explicit nonce size of the cipher.
+ */
+ int applyExplicitNonce(Authenticator authenticator, byte contentType,
+ ByteBuffer bb) throws BadPaddingException {
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ // For block ciphers, the explicit IV length is of length
+ // SecurityParameters.record_iv_length, which is equal to
+ // the SecurityParameters.block_size.
+ if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ return cipher.getBlockSize();
+ }
+ break;
+ case AEAD_CIPHER:
+ if (bb.remaining() < (recordIvSize + tagSize)) {
+ throw new BadPaddingException(
+ "invalid AEAD cipher fragment");
+ }
+
+ // initialize the AEAD cipher for the unique IV
+ byte[] iv = Arrays.copyOf(fixedIv,
+ fixedIv.length + recordIvSize);
+ bb.get(iv, fixedIv.length, recordIvSize);
+ bb.position(bb.position() - recordIvSize);
+ GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
+ try {
+ cipher.init(mode, key, spec, random);
+ } catch (InvalidKeyException |
+ InvalidAlgorithmParameterException ikae) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "invalid key or spec in GCM mode", ikae);
+ }
+
+ // update the additional authentication data
+ byte[] aad = authenticator.acquireAuthenticationBytes(
+ contentType, bb.remaining() - recordIvSize - tagSize);
+ cipher.updateAAD(aad);
+
+ return recordIvSize;
+ // It is also the length of sequence number, which is
+ // used as the nonce_explicit for AEAD cipher suites.
+ }
+
+ return 0;
+ }
+
+ /*
+ * Applies the explicit nonce/IV to this cipher. This method is used to
+ * decrypt an SSL/TLS input record.
+ *
+ * The returned value is the SecurityParameters.record_iv_length in
+ * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
+ * size of explicit nonce for AEAD mode.
+ *
+ * @param authenticator the authenticator to get the additional
+ * authentication data
+ * @param contentType the content type of the input record
+ * @param buf the byte array to get the explicit nonce from
+ * @param offset the offset of the byte buffer
+ * @param cipheredLength the ciphered fragment length of the output
+ * record, it is the TLSCiphertext.length in RFC 4346/5246.
+ *
+ * @return the explicit nonce size of the cipher.
+ */
+ int applyExplicitNonce(Authenticator authenticator,
+ byte contentType, byte[] buf, int offset,
+ int cipheredLength) throws BadPaddingException {
+
+ ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength);
+
+ return applyExplicitNonce(authenticator, contentType, bb);
+ }
+
+ /*
+ * Creates the explicit nonce/IV to this cipher. This method is used to
+ * encrypt an SSL/TLS output record.
+ *
+ * The size of the returned array is the SecurityParameters.record_iv_length
+ * in RFC 4346/5246. It is the size of explicit IV for CBC mode, and the
+ * size of explicit nonce for AEAD mode.
+ *
+ * @param authenticator the authenticator to get the additional
+ * authentication data
+ * @param contentType the content type of the input record
+ * @param fragmentLength the fragment length of the output record, it is
+ * the TLSCompressed.length in RFC 4346/5246.
+ *
+ * @return the explicit nonce of the cipher.
+ */
+ byte[] createExplicitNonce(Authenticator authenticator,
+ byte contentType, int fragmentLength) {
+
+ byte[] nonce = new byte[0];
+ switch (cipherType) {
+ case BLOCK_CIPHER:
+ if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ // For block ciphers, the explicit IV length is of length
+ // SecurityParameters.record_iv_length, which is equal to
+ // the SecurityParameters.block_size.
+ //
+ // Generate a random number as the explicit IV parameter.
+ nonce = new byte[cipher.getBlockSize()];
+ random.nextBytes(nonce);
+ }
+ break;
+ case AEAD_CIPHER:
+ // To be unique and aware of overflow-wrap, sequence number
+ // is used as the nonce_explicit of AEAD cipher suites.
+ nonce = authenticator.sequenceNumber();
+
+ // initialize the AEAD cipher for the unique IV
+ byte[] iv = Arrays.copyOf(fixedIv,
+ fixedIv.length + nonce.length);
+ System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
+ GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
+ try {
+ cipher.init(mode, key, spec, random);
+ } catch (InvalidKeyException |
+ InvalidAlgorithmParameterException ikae) {
+ // unlikely to happen
+ throw new RuntimeException(
+ "invalid key or spec in GCM mode", ikae);
+ }
+
+ // update the additional authentication data
+ byte[] aad = authenticator.acquireAuthenticationBytes(
+ contentType, fragmentLength);
+ cipher.updateAAD(aad);
+ break;
+ }
+
+ return nonce;
+ }
+
+ /*
+ * Is this cipher available?
+ *
+ * This method can only be called by CipherSuite.BulkCipher.isAvailable()
+ * to test the availability of a cipher suites. Please DON'T use it in
+ * other places, otherwise, the behavior may be unexpected because we may
+ * initialize AEAD cipher improperly in the method.
+ */
+ Boolean isAvailable() {
+ // We won't know whether a cipher for a particular key size is
+ // available until the cipher is successfully initialized.
+ //
+ // We do not initialize AEAD cipher in the constructor. Need to
+ // initialize the cipher to ensure that the AEAD mode for a
+ // particular key size is supported.
+ if (cipherType == AEAD_CIPHER) {
+ try {
+ Authenticator authenticator =
+ new Authenticator(protocolVersion);
+ byte[] nonce = authenticator.sequenceNumber();
+ byte[] iv = Arrays.copyOf(fixedIv,
+ fixedIv.length + nonce.length);
+ System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
+ GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
+
+ cipher.init(mode, key, spec, random);
+ } catch (Exception e) {
+ return Boolean.FALSE;
+ }
+ } // Otherwise, we have initialized the cipher in the constructor.
+
+ return Boolean.TRUE;
}
}
diff --git a/src/share/classes/sun/security/ssl/CipherSuite.java b/src/share/classes/sun/security/ssl/CipherSuite.java
index 555304880b7a5d4cd108bf6827fe0978ff447c9b..435693751c7a0523212de319128f5a7bf14bed1c 100644
--- a/src/share/classes/sun/security/ssl/CipherSuite.java
+++ b/src/share/classes/sun/security/ssl/CipherSuite.java
@@ -33,12 +33,14 @@ import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.security.KeyManagementException;
+import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
import static sun.security.ssl.CipherSuite.PRF.*;
+import static sun.security.ssl.CipherSuite.CipherType.*;
import static sun.security.ssl.JsseJce.*;
/**
@@ -135,7 +137,9 @@ final class CipherSuite implements Comparable- * We assume that the higher levels have assured us enough - * room, otherwise we'll indirectly throw a - * BufferOverFlowException runtime exception. - * - * position should equal limit, and points to the next - * free spot. - */ - private void addMAC(MAC signer, ByteBuffer bb) - throws IOException { - - if (signer.MAClen() != 0) { - byte[] hash = signer.compute(contentType(), bb); - - /* - * position was advanced to limit in compute above. - * - * Mark next area as writable (above layers should have - * established that we have plenty of room), then write - * out the hash. - */ - bb.limit(bb.limit() + hash.length); - bb.put(hash); - } - } - - /* - * Encrypt a ByteBuffer. - * - * We assume that the higher levels have assured us enough - * room for the encryption (plus padding), otherwise we'll - * indirectly throw a BufferOverFlowException runtime exception. - * - * position and limit will be the same, and points to the - * next free spot. - */ - void encrypt(CipherBox box, ByteBuffer bb) { - box.encrypt(bb); - } - /* * Override the actual write below. We do things this way to be * consistent with InputRecord. InputRecord may try to write out @@ -160,7 +114,8 @@ final class EngineOutputRecord extends OutputRecord { * Copy data out of buffer, it's ready to go. */ ByteBuffer netBB = (ByteBuffer) - ByteBuffer.allocate(len).put(buf, 0, len).flip(); + ByteBuffer.allocate(len).put(buf, off, len).flip(); + writer.putOutboundData(netBB); } @@ -168,17 +123,19 @@ final class EngineOutputRecord extends OutputRecord { * Main method for writing non-application data. * We MAC/encrypt, then send down for processing. */ - void write(MAC writeMAC, CipherBox writeCipher) throws IOException { + void write(Authenticator authenticator, CipherBox writeCipher) + throws IOException { + /* * Sanity check. */ switch (contentType()) { - case ct_change_cipher_spec: - case ct_alert: - case ct_handshake: - break; - default: - throw new RuntimeException("unexpected byte buffers"); + case ct_change_cipher_spec: + case ct_alert: + case ct_handshake: + break; + default: + throw new RuntimeException("unexpected byte buffers"); } /* @@ -193,10 +150,10 @@ final class EngineOutputRecord extends OutputRecord { */ if (!isEmpty()) { // compress(); // eventually - addMAC(writeMAC); - encrypt(writeCipher); - write((OutputStream)null, false, // send down for processing - (ByteArrayOutputStream)null); + encrypt(authenticator, writeCipher); + + // send down for processing + write((OutputStream)null, false, (ByteArrayOutputStream)null); } return; } @@ -204,8 +161,8 @@ final class EngineOutputRecord extends OutputRecord { /** * Main wrap/write driver. */ - void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher) - throws IOException { + void write(EngineArgs ea, Authenticator authenticator, + CipherBox writeCipher) throws IOException { /* * sanity check to make sure someone didn't inadvertantly * send us an impossible combination we don't know how @@ -217,7 +174,7 @@ final class EngineOutputRecord extends OutputRecord { * Have we set the MAC's yet? If not, we're not ready * to process application data yet. */ - if (writeMAC == MAC.NULL) { + if (authenticator == MAC.NULL) { return; } @@ -255,7 +212,7 @@ final class EngineOutputRecord extends OutputRecord { */ int length; if (engine.needToSplitPayload(writeCipher, protocolVersion)) { - write(ea, writeMAC, writeCipher, 0x01); + write(ea, authenticator, writeCipher, 0x01); ea.resetLim(); // reset application data buffer limit length = Math.min(ea.getAppRemaining(), maxDataSizeMinusOneByteRecord); @@ -265,14 +222,14 @@ final class EngineOutputRecord extends OutputRecord { // Don't bother to really write empty records. if (length > 0) { - write(ea, writeMAC, writeCipher, length); + write(ea, authenticator, writeCipher, length); } return; } - void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher, - int length) throws IOException { + void write(EngineArgs ea, Authenticator authenticator, + CipherBox writeCipher, int length) throws IOException { /* * Copy out existing buffer values. */ @@ -286,39 +243,76 @@ final class EngineOutputRecord extends OutputRecord { * Don't need to worry about SSLv2 rewrites, if we're here, * that's long since done. */ - int dstData = dstPos + headerSize; + int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize(); dstBB.position(dstData); - ea.gather(length); - /* - * "flip" but skip over header again, add MAC & encrypt - * addMAC will expand the limit to reflect the new - * data. + * transfer application data into the network data buffer */ + ea.gather(length); dstBB.limit(dstBB.position()); dstBB.position(dstData); - addMAC(writeMAC, dstBB); /* - * Encrypt may pad, so again the limit may have changed. + * "flip" but skip over header again, add MAC & encrypt */ - dstBB.limit(dstBB.position()); - dstBB.position(dstData); - encrypt(writeCipher, dstBB); + if (authenticator instanceof MAC) { + MAC signer = (MAC)authenticator; + if (signer.MAClen() != 0) { + byte[] hash = signer.compute(contentType(), dstBB); + + /* + * position was advanced to limit in compute above. + * + * Mark next area as writable (above layers should have + * established that we have plenty of room), then write + * out the hash. + */ + dstBB.limit(dstBB.limit() + hash.length); + dstBB.put(hash); + + // reset the position and limit + dstBB.limit(dstBB.position()); + dstBB.position(dstData); + } + } + + if (!writeCipher.isNullCipher()) { + /* + * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1 + * or later. + */ + if (protocolVersion.v >= ProtocolVersion.TLS11.v && + (writeCipher.isCBCMode() || writeCipher.isAEADMode())) { + byte[] nonce = writeCipher.createExplicitNonce( + authenticator, contentType(), dstBB.remaining()); + dstBB.position(dstPos + headerSize); + dstBB.put(nonce); + if (!writeCipher.isAEADMode()) { + // The explicit IV in TLS 1.1 and later can be encrypted. + dstBB.position(dstPos + headerSize); + } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode + } - if (debug != null - && (Debug.isOn("record") || Debug.isOn("handshake"))) { - if ((debug != null && Debug.isOn("record")) - || contentType() == ct_change_cipher_spec) + /* + * Encrypt may pad, so again the limit may have changed. + */ + writeCipher.encrypt(dstBB, dstLim); + + if ((debug != null) && (Debug.isOn("record") || + (Debug.isOn("handshake") && + (contentType() == ct_change_cipher_spec)))) { System.out.println(Thread.currentThread().getName() // v3.0/v3.1 ... + ", WRITE: " + protocolVersion + " " + InputRecord.contentName(contentType()) + ", length = " + length); + } + } else { + dstBB.position(dstBB.limit()); } - int packetLength = dstBB.limit() - dstData; + int packetLength = dstBB.limit() - dstPos - headerSize; /* * Finish out the record header. @@ -333,7 +327,5 @@ final class EngineOutputRecord extends OutputRecord { * Position was already set by encrypt() above. */ dstBB.limit(dstLim); - - return; } } diff --git a/src/share/classes/sun/security/ssl/EngineWriter.java b/src/share/classes/sun/security/ssl/EngineWriter.java index e08dbb06069a1a3ab40489bc75bfd2ada1d590fe..c6a265626068b4e725e6d5f40c3ddb3846aba806 100644 --- a/src/share/classes/sun/security/ssl/EngineWriter.java +++ b/src/share/classes/sun/security/ssl/EngineWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -99,7 +99,8 @@ final class EngineWriter { * other writeRecord. */ synchronized void writeRecord(EngineOutputRecord outputRecord, - MAC writeMAC, CipherBox writeCipher) throws IOException { + Authenticator authenticator, + CipherBox writeCipher) throws IOException { /* * Only output if we're still open. @@ -108,7 +109,7 @@ final class EngineWriter { throw new IOException("writer side was already closed."); } - outputRecord.write(writeMAC, writeCipher); + outputRecord.write(authenticator, writeCipher); /* * Did our handshakers notify that we just sent the @@ -151,7 +152,8 @@ final class EngineWriter { * Return any determined status. */ synchronized HandshakeStatus writeRecord( - EngineOutputRecord outputRecord, EngineArgs ea, MAC writeMAC, + EngineOutputRecord outputRecord, EngineArgs ea, + Authenticator authenticator, CipherBox writeCipher) throws IOException { /* @@ -181,7 +183,7 @@ final class EngineWriter { throw new IOException("The write side was already closed"); } - outputRecord.write(ea, writeMAC, writeCipher); + outputRecord.write(ea, authenticator, writeCipher); if (debug != null && Debug.isOn("packet")) { dumpPacket(ea, false); diff --git a/src/share/classes/sun/security/ssl/Handshaker.java b/src/share/classes/sun/security/ssl/Handshaker.java index 828b60bcab6e77dfb8d25da4a8171dfca656ea53..d03b8f7e5ac908946a130b2477031dd463f4098a 100644 --- a/src/share/classes/sun/security/ssl/Handshaker.java +++ b/src/share/classes/sun/security/ssl/Handshaker.java @@ -49,6 +49,7 @@ import sun.security.ssl.HandshakeMessage.*; import sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.PRF.*; +import static sun.security.ssl.CipherSuite.CipherType.*; /** * Handshaker ... processes handshake records from an SSL V3.0 @@ -714,33 +715,47 @@ abstract class Handshaker { /** * Create a new read MAC and return it to caller. */ - MAC newReadMAC() throws NoSuchAlgorithmException, InvalidKeyException { - MacAlg macAlg = cipherSuite.macAlg; - MAC mac; - if (isClient) { - mac = macAlg.newMac(protocolVersion, svrMacSecret); - svrMacSecret = null; + Authenticator newReadAuthenticator() + throws NoSuchAlgorithmException, InvalidKeyException { + + Authenticator authenticator = null; + if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { + authenticator = new Authenticator(protocolVersion); } else { - mac = macAlg.newMac(protocolVersion, clntMacSecret); - clntMacSecret = null; + MacAlg macAlg = cipherSuite.macAlg; + if (isClient) { + authenticator = macAlg.newMac(protocolVersion, svrMacSecret); + svrMacSecret = null; + } else { + authenticator = macAlg.newMac(protocolVersion, clntMacSecret); + clntMacSecret = null; + } } - return mac; + + return authenticator; } /** * Create a new write MAC and return it to caller. */ - MAC newWriteMAC() throws NoSuchAlgorithmException, InvalidKeyException { - MacAlg macAlg = cipherSuite.macAlg; - MAC mac; - if (isClient) { - mac = macAlg.newMac(protocolVersion, clntMacSecret); - clntMacSecret = null; + Authenticator newWriteAuthenticator() + throws NoSuchAlgorithmException, InvalidKeyException { + + Authenticator authenticator = null; + if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { + authenticator = new Authenticator(protocolVersion); } else { - mac = macAlg.newMac(protocolVersion, svrMacSecret); - svrMacSecret = null; + MacAlg macAlg = cipherSuite.macAlg; + if (isClient) { + authenticator = macAlg.newMac(protocolVersion, clntMacSecret); + clntMacSecret = null; + } else { + authenticator = macAlg.newMac(protocolVersion, svrMacSecret); + svrMacSecret = null; + } } - return mac; + + return authenticator; } /* @@ -1189,11 +1204,23 @@ abstract class Handshaker { int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); + // TLS v1.1 or later uses an explicit IV in CBC cipher suites to + // protect against the CBC attacks. AEAD/GCM cipher suites in TLS + // v1.2 or later use a fixed IV as the implicit part of the partially + // implicit nonce technique described in RFC 5116. + int ivSize = cipher.ivSize; + if (cipher.cipherType == AEAD_CIPHER) { + ivSize = cipher.fixedIvSize; + } else if (protocolVersion.v >= ProtocolVersion.TLS11.v && + cipher.cipherType == BLOCK_CIPHER) { + ivSize = 0; + } + TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( masterKey, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, cipher.algorithm, cipher.keySize, expandedKeySize, - cipher.ivSize, hashSize, + ivSize, hashSize, prfHashAlg, prfHashLength, prfBlockSize); try { @@ -1201,14 +1228,15 @@ abstract class Handshaker { kg.init(spec); TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec)kg.generateKey(); + // Return null if cipher keys are not supposed to be generated. clntWriteKey = keySpec.getClientCipherKey(); svrWriteKey = keySpec.getServerCipherKey(); // Return null if IVs are not supposed to be generated. - // e.g. TLS 1.1+. clntWriteIV = keySpec.getClientIv(); svrWriteIV = keySpec.getServerIv(); + // Return null if MAC keys are not supposed to be generated. clntMacSecret = keySpec.getClientMacKey(); svrMacSecret = keySpec.getServerMacKey(); } catch (GeneralSecurityException e) { @@ -1233,10 +1261,14 @@ abstract class Handshaker { printHex(dump, masterKey.getEncoded()); // Outputs: - System.out.println("Client MAC write Secret:"); - printHex(dump, clntMacSecret.getEncoded()); - System.out.println("Server MAC write Secret:"); - printHex(dump, svrMacSecret.getEncoded()); + if (clntMacSecret != null) { + System.out.println("Client MAC write Secret:"); + printHex(dump, clntMacSecret.getEncoded()); + System.out.println("Server MAC write Secret:"); + printHex(dump, svrMacSecret.getEncoded()); + } else { + System.out.println("... no MAC keys used for this cipher"); + } if (clntWriteKey != null) { System.out.println("Client write key:"); diff --git a/src/share/classes/sun/security/ssl/InputRecord.java b/src/share/classes/sun/security/ssl/InputRecord.java index 78e866a07ca9478e89edab7efc7ca6706ebd1e79..74d88f9b81b54434d8d299ac40547c66ceb029a0 100644 --- a/src/share/classes/sun/security/ssl/InputRecord.java +++ b/src/share/classes/sun/security/ssl/InputRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2012, 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 @@ -77,6 +77,17 @@ class InputRecord extends ByteArrayInputStream implements Record { /* * Construct the record to hold the maximum sized input record. * Data will be filled in separately. + * + * The structure of the byte buffer looks like: + * + * |--------+---------+---------------------------------| + * | header | IV | content, MAC/TAG, padding, etc. | + * | headerPlusIVSize | + * + * header: the header of an SSL records + * IV: the optional IV/nonce field, it is only required for block + * (TLS 1.1 or later) and AEAD cipher suites. + * */ InputRecord() { super(new byte[maxRecordSize]); @@ -133,44 +144,83 @@ class InputRecord extends ByteArrayInputStream implements Record { return handshakeHash; } - /* - * Verify and remove the MAC ... used for all records. - */ - boolean checkMAC(MAC signer) { - int len = signer.MAClen(); - if (len == 0) { // no mac - return true; - } + void decrypt(Authenticator authenticator, + CipherBox box) throws BadPaddingException { - int offset = count - len; - - if (offset < headerSize) { - // data length would be negative, something is wrong - return false; + BadPaddingException bpe = null; + if (!box.isNullCipher()) { + try { + int cipheredLength = count - headerSize; + + // apply explicit nonce for AEAD/CBC cipher suites if needed + int nonceSize = box.applyExplicitNonce(authenticator, + contentType(), buf, headerSize, cipheredLength); + pos = headerSize + nonceSize; + lastHashed = pos; // don't digest the explicit nonce + + // decrypt the content + int offset = headerSize; + if (box.isAEADMode()) { + // DON'T encrypt the nonce_explicit for AEAD mode + offset += nonceSize; + } // The explicit IV for CBC mode can be decrypted. + + count = offset + box.decrypt(buf, offset, count - offset); + + // Note that we don't remove the nonce from the buffer. + } catch (BadPaddingException e) { + // RFC 2246 states that decryption_failed should be used + // for this purpose. However, that allows certain attacks, + // so we just send bad record MAC. We also need to make + // sure to always check the MAC to avoid a timing attack + // for the same issue. See paper by Vaudenay et al and the + // update in RFC 4346/5246. + // + // Failover to message authenticatoin code checking. + bpe = new BadPaddingException("invalid padding"); + } } - byte[] mac = signer.compute(contentType(), buf, - headerSize, offset - headerSize); + // Requires message authentication code for null, stream and block + // cipher suites. + if (authenticator instanceof MAC) { + MAC signer = (MAC)authenticator; + int macLen = signer.MAClen(); + if (macLen != 0) { + int macOffset = count - macLen; + int contentLen = macOffset - pos; + if (contentLen < 0) { + // negative data length, something is wrong + throw new BadPaddingException("bad record"); + } - if (len != mac.length) { - throw new RuntimeException("Internal MAC error"); - } + count -= macLen; // Set the count before any MAC checking + // exception occurs, so that the following + // process can read the actual decrypted + // content (minus the MAC) in the fragment + // if necessary. + byte[] hash = signer.compute(contentType(), + buf, pos, contentLen); + if (hash == null || macLen != hash.length) { + // something is wrong with MAC implementation + throw new RuntimeException("Internal MAC error"); + } - for (int i = 0; i < len; i++) { - if (buf[offset + i] != mac[i]) { - return false; + int offset = macOffset; + for (byte b : hash) { + if (buf[offset++] != b) { + throw new BadPaddingException("bad record MAC"); + } + } } } - count -= len; - return true; - } - void decrypt(CipherBox box) throws BadPaddingException { - int len = count - headerSize; - count = headerSize + box.decrypt(buf, headerSize, len); + // Is it a failover? + if (bpe != null) { + throw bpe; + } } - /* * Well ... hello_request messages are _never_ hashed since we can't * know when they'd appear in the sequence. diff --git a/src/share/classes/sun/security/ssl/JsseJce.java b/src/share/classes/sun/security/ssl/JsseJce.java index e22caece0e239e51e1dfdf4a8f47f4bd8099db65..49e89c6028e8e466d4e229c03200546902347950 100644 --- a/src/share/classes/sun/security/ssl/JsseJce.java +++ b/src/share/classes/sun/security/ssl/JsseJce.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -154,6 +154,11 @@ final class JsseJce { * without padding. */ final static String CIPHER_AES = "AES/CBC/NoPadding"; + /** + * JCE transformation string for AES in GCM mode + * without padding. + */ + final static String CIPHER_AES_GCM = "AES/GCM/NoPadding"; /** * JCA identifier string for DSA, i.e. a DSA with SHA-1. */ diff --git a/src/share/classes/sun/security/ssl/MAC.java b/src/share/classes/sun/security/ssl/MAC.java index 753a8c57ac4e896a12a8dd2d554b6d103fc3fcfd..30bf2e2e50fc4bb005f4d727aa55e9dfa7a9ae5b 100644 --- a/src/share/classes/sun/security/ssl/MAC.java +++ b/src/share/classes/sun/security/ssl/MAC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2012, 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,19 +39,15 @@ import static sun.security.ssl.CipherSuite.*; /** * This class computes the "Message Authentication Code" (MAC) for each - * SSL message. This is essentially a shared-secret signature, used to - * provide integrity protection for SSL messages. The MAC is actually - * one of several keyed hashes, as associated with the cipher suite and - * protocol version. (SSL v3.0 uses one construct, TLS uses another.) - * - *
NOTE: MAC computation is the only place in the SSL protocol that the - * sequence number is used. It's also reset to zero with each change of - * a cipher spec, so this is the only place this state is needed. + * SSL stream and block cipher message. This is essentially a shared-secret + * signature, used to provide integrity protection for SSL messages. The + * MAC is actually one of several keyed hashes, as associated with the cipher + * suite and protocol version. (SSL v3.0 uses one construct, TLS uses another.) * * @author David Brownell * @author Andreas Sterbenz */ -final class MAC { +final class MAC extends Authenticator { final static MAC NULL = new MAC(); @@ -64,26 +60,9 @@ final class MAC { // JCE Mac object private final Mac mac; - // byte array containing the additional information we MAC in each record - // (see below) - private final byte[] block; - - // sequence number + record type + + record length - private static final int BLOCK_SIZE_SSL = 8 + 1 + 2; - - // sequence number + record type + protocol version + record length - private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2; - - // offset of record type in block - private static final int BLOCK_OFFSET_TYPE = 8; - - // offset of protocol version number in block (TLS only) - private static final int BLOCK_OFFSET_VERSION = 8 + 1; - private MAC() { macSize = 0; mac = null; - block = null; } /** @@ -91,6 +70,8 @@ final class MAC { */ MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key) throws NoSuchAlgorithmException, InvalidKeyException { + super(protocolVersion); + this.macSize = macAlg.size; String algorithm; @@ -110,14 +91,6 @@ final class MAC { mac = JsseJce.getMac(algorithm); mac.init(key); - - if (tls) { - block = new byte[BLOCK_SIZE_TLS]; - block[BLOCK_OFFSET_VERSION] = protocolVersion.major; - block[BLOCK_OFFSET_VERSION+1] = protocolVersion.minor; - } else { - block = new byte[BLOCK_SIZE_SSL]; - } } /** @@ -136,7 +109,15 @@ final class MAC { * @param len the size of the compressed record */ final byte[] compute(byte type, byte buf[], int offset, int len) { - return compute(type, null, buf, offset, len); + if (macSize == 0) { + return nullMAC; + } + + byte[] additional = acquireAuthenticationBytes(type, len); + mac.update(additional); + mac.update(buf, offset, len); + + return mac.doFinal(); } /** @@ -151,78 +132,13 @@ final class MAC { * demarcate the data to be MAC'd. */ final byte[] compute(byte type, ByteBuffer bb) { - return compute(type, bb, null, 0, bb.remaining()); - } - - /** - * Check whether the sequence number is close to wrap - * - * Sequence numbers are of type uint64 and may not exceed 2^64-1. - * Sequence numbers do not wrap. When the sequence number is near - * to wrap, we need to close the connection immediately. - */ - final boolean seqNumOverflow() { - /* - * Conservatively, we don't allow more records to be generated - * when there are only 2^8 sequence numbers left. - */ - return (block != null && mac != null && - block[0] == (byte)0xFF && block[1] == (byte)0xFF && - block[2] == (byte)0xFF && block[3] == (byte)0xFF && - block[4] == (byte)0xFF && block[5] == (byte)0xFF && - block[6] == (byte)0xFF); - } - - /* - * Check whether to renew the sequence number - * - * Sequence numbers are of type uint64 and may not exceed 2^64-1. - * Sequence numbers do not wrap. If a TLS - * implementation would need to wrap a sequence number, it must - * renegotiate instead. - */ - final boolean seqNumIsHuge() { - /* - * Conservatively, we should ask for renegotiation when there are - * only 2^48 sequence numbers left. - */ - return (block != null && mac != null && - block[0] == (byte)0xFF && block[1] == (byte)0xFF); - } - - // increment the sequence number in the block array - // it is a 64-bit number stored in big-endian format - private void incrementSequenceNumber() { - int k = 7; - while ((k >= 0) && (++block[k] == 0)) { - k--; - } - } - - /* - * Compute based on either buffer type, either bb.position/limit - * or buf/offset/len. - */ - private byte[] compute(byte type, ByteBuffer bb, byte[] buf, - int offset, int len) { - if (macSize == 0) { return nullMAC; } - block[BLOCK_OFFSET_TYPE] = type; - block[block.length - 2] = (byte)(len >> 8); - block[block.length - 1] = (byte)(len ); - - mac.update(block); - incrementSequenceNumber(); - - // content - if (bb != null) { - mac.update(bb); - } else { - mac.update(buf, offset, len); - } + byte[] additional = acquireAuthenticationBytes(type, bb.remaining()); + mac.update(additional); + mac.update(bb); return mac.doFinal(); } diff --git a/src/share/classes/sun/security/ssl/OutputRecord.java b/src/share/classes/sun/security/ssl/OutputRecord.java index 900003dfd0ff5cae05cd0059770e64017086bee5..d07c779f75e195664992aae82002355ae7fe783d 100644 --- a/src/share/classes/sun/security/ssl/OutputRecord.java +++ b/src/share/classes/sun/security/ssl/OutputRecord.java @@ -54,6 +54,7 @@ class OutputRecord extends ByteArrayOutputStream implements Record { private int lastHashed; private boolean firstMessage; final private byte contentType; + private int headerOffset; // current protocol version, sent as record version ProtocolVersion protocolVersion; @@ -70,6 +71,23 @@ class OutputRecord extends ByteArrayOutputStream implements Record { * Default constructor makes a record supporting the maximum * SSL record size. It allocates the header bytes directly. * + * The structure of the byte buffer looks like: + * + * |---------+--------+-------+---------------------------------| + * | unused | header | IV | content, MAC/TAG, padding, etc. | + * | headerPlusMaxIVSize | + * + * unused: unused part of the buffer of size + * + * headerPlusMaxIVSize - header size - IV size + * + * When this object is created, we don't know the protocol + * version number, IV length, etc., so reserve space in front + * to avoid extra data movement (copies). + * header: the header of an SSL record + * IV: the optional IV/nonce field, it is only required for block + * (TLS 1.1 or later) and AEAD cipher suites. + * * @param type the content type for the record */ OutputRecord(byte type, int size) { @@ -77,9 +95,10 @@ class OutputRecord extends ByteArrayOutputStream implements Record { this.protocolVersion = ProtocolVersion.DEFAULT; this.helloVersion = ProtocolVersion.DEFAULT_HELLO; firstMessage = true; - count = headerSize; + count = headerPlusMaxIVSize; contentType = type; lastHashed = count; + headerOffset = headerPlusMaxIVSize - headerSize; } OutputRecord(byte type) { @@ -119,8 +138,9 @@ class OutputRecord extends ByteArrayOutputStream implements Record { @Override public synchronized void reset() { super.reset(); - count = headerSize; + count = headerPlusMaxIVSize; lastHashed = count; + headerOffset = headerPlusMaxIVSize - headerSize; } /* @@ -173,58 +193,84 @@ class OutputRecord extends ByteArrayOutputStream implements Record { * of sending empty records over the network. */ boolean isEmpty() { - return count == headerSize; + return count == headerPlusMaxIVSize; } /* - * Return true if the record is of a given alert. + * Return true if the record is of an alert of the given description. + * + * Per SSL/TLS specifications, alert messages convey the severity of the + * message (warning or fatal) and a description of the alert. An alert + * is defined with a two bytes struct, {byte level, byte description}, + * following after the header bytes. */ boolean isAlert(byte description) { - // An alert is defined with a two bytes struct, - // {byte level, byte description}, following after the header bytes. - if (count > (headerSize + 1) && contentType == ct_alert) { - return buf[headerSize + 1] == description; + if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) { + return buf[headerPlusMaxIVSize + 1] == description; } return false; } /* - * Compute the MAC and append it to this record. In case we - * are automatically flushing a handshake stream, make sure we - * have hashed the message first. + * Encrypt ... length may grow due to block cipher padding, or + * message authentication code or tag. */ - void addMAC(MAC signer) throws IOException { + void encrypt(Authenticator authenticator, CipherBox box) + throws IOException { + + // In case we are automatically flushing a handshake stream, make + // sure we have hashed the message first. // // when we support compression, hashing can't go here // since it'll need to be done on the uncompressed data, // and the MAC applies to the compressed data. - // if (contentType == ct_handshake) { doHashes(); } - if (signer.MAClen() != 0) { - byte[] hash = signer.compute(contentType, buf, - headerSize, count - headerSize); - write(hash); + + // Requires message authentication code for stream and block + // cipher suites. + if (authenticator instanceof MAC) { + MAC signer = (MAC)authenticator; + if (signer.MAClen() != 0) { + byte[] hash = signer.compute(contentType, buf, + headerPlusMaxIVSize, count - headerPlusMaxIVSize); + write(hash); + } } - } - /* - * Encrypt ... length may grow due to block cipher padding - */ - void encrypt(CipherBox box) { - int len = count - headerSize; - count = headerSize + box.encrypt(buf, headerSize, len); - } + if (!box.isNullCipher()) { + // Requires explicit IV/nonce for CBC/AEAD cipher suites for + // TLS 1.1 or later. + if ((protocolVersion.v >= ProtocolVersion.TLS11.v) && + (box.isCBCMode() || box.isAEADMode())) { + byte[] nonce = box.createExplicitNonce(authenticator, + contentType, count - headerPlusMaxIVSize); + int offset = headerPlusMaxIVSize - nonce.length; + System.arraycopy(nonce, 0, buf, offset, nonce.length); + headerOffset = offset - headerSize; + } else { + headerOffset = headerPlusMaxIVSize - headerSize; + } + // encrypt the content + int offset = headerPlusMaxIVSize; + if (!box.isAEADMode()) { + // The explicit IV can be encrypted. + offset = headerOffset + headerSize; + } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode + + count = offset + box.encrypt(buf, offset, count - offset); + } + } /* * Tell how full the buffer is ... for filling it with application or * handshake data. */ final int availableDataBytes() { - int dataSize = count - headerSize; + int dataSize = count - headerPlusMaxIVSize; return maxDataSize - dataSize; } @@ -270,11 +316,11 @@ class OutputRecord extends ByteArrayOutputStream implements Record { * Don't emit content-free records. (Even change cipher spec * messages have a byte of data!) */ - if (count == headerSize) { + if (count == headerPlusMaxIVSize) { return; } - int length = count - headerSize; + int length = count - headerOffset - headerSize; // "should" really never write more than about 14 Kb... if (length < 0) { throw new SSLException("output record size too small: " @@ -299,7 +345,9 @@ class OutputRecord extends ByteArrayOutputStream implements Record { */ if (firstMessage && useV2Hello()) { byte[] v3Msg = new byte[length - 4]; - System.arraycopy(buf, headerSize + 4, v3Msg, 0, v3Msg.length); + System.arraycopy(buf, headerPlusMaxIVSize + 4, + v3Msg, 0, v3Msg.length); + headerOffset = 0; // reset the header offset V3toV2ClientHello(v3Msg); handshakeHash.reset(); lastHashed = 2; @@ -314,11 +362,11 @@ class OutputRecord extends ByteArrayOutputStream implements Record { /* * Fill out the header, write it and the message. */ - buf[0] = contentType; - buf[1] = protocolVersion.major; - buf[2] = protocolVersion.minor; - buf[3] = (byte)(length >> 8); - buf[4] = (byte)(length); + buf[headerOffset + 0] = contentType; + buf[headerOffset + 1] = protocolVersion.major; + buf[headerOffset + 2] = protocolVersion.minor; + buf[headerOffset + 3] = (byte)(length >> 8); + buf[headerOffset + 4] = (byte)(length); } firstMessage = false; @@ -338,7 +386,8 @@ class OutputRecord extends ByteArrayOutputStream implements Record { * when holdRecord is true, the implementation in this class * will be used. */ - writeBuffer(heldRecordBuffer, buf, 0, count, debugOffset); + writeBuffer(heldRecordBuffer, + buf, headerOffset, count - headerOffset, debugOffset); } else { // It's time to send, do we have buffered data? // May or may not have a heldRecordBuffer. @@ -346,15 +395,18 @@ class OutputRecord extends ByteArrayOutputStream implements Record { int heldLen = heldRecordBuffer.size(); // Ensure the capacity of this buffer. - ensureCapacity(count + heldLen); + int newCount = count + heldLen - headerOffset; + ensureCapacity(newCount); // Slide everything in the buffer to the right. - System.arraycopy(buf, 0, buf, heldLen, count); + System.arraycopy(buf, headerOffset, + buf, heldLen, count - headerOffset); // Prepend the held record to the buffer. System.arraycopy( heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen); - count += heldLen; + count = newCount; + headerOffset = 0; // Clear the held buffer. heldRecordBuffer.reset(); @@ -362,7 +414,8 @@ class OutputRecord extends ByteArrayOutputStream implements Record { // The held buffer has been dumped, set the debug dump offset. debugOffset = heldLen; } - writeBuffer(s, buf, 0, count, debugOffset); + writeBuffer(s, buf, headerOffset, + count - headerOffset, debugOffset); } reset(); @@ -382,12 +435,11 @@ class OutputRecord extends ByteArrayOutputStream implements Record { if (debug != null && Debug.isOn("packet")) { try { HexDumpEncoder hd = new HexDumpEncoder(); - ByteBuffer bb = ByteBuffer.wrap( - buf, off + debugOffset, len - debugOffset); System.out.println("[Raw write]: length = " + - bb.remaining()); - hd.encodeBuffer(bb, System.out); + (len - debugOffset)); + hd.encodeBuffer(new ByteArrayInputStream(buf, + off + debugOffset, len - debugOffset), System.out); } catch (IOException e) { } } } @@ -400,8 +452,13 @@ class OutputRecord extends ByteArrayOutputStream implements Record { return firstMessage && (helloVersion == ProtocolVersion.SSL20Hello) && (contentType == ct_handshake) - && (buf[5] == HandshakeMessage.ht_client_hello) - && (buf[headerSize + 4+2+32] == 0); // V3 session ID is empty + && (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello) + // 5: recode header size + && (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0); + // V3 session ID is empty + // 4: handshake header size + // 2: client_version in ClientHello + // 32: random in ClientHello } /* diff --git a/src/share/classes/sun/security/ssl/Record.java b/src/share/classes/sun/security/ssl/Record.java index 0494d9a4583f034d3ea9d3d5dd36b0036b9a8fd2..ca85a067ae3d9bfa85408df1e6af7df5fda129e7 100644 --- a/src/share/classes/sun/security/ssl/Record.java +++ b/src/share/classes/sun/security/ssl/Record.java @@ -52,20 +52,29 @@ interface Record { static final int trailerSize = 20; // SHA1 hash size static final int maxDataSize = 16384; // 2^14 bytes of data static final int maxPadding = 256; // block cipher padding - static final int maxIVLength = 256; // block length + static final int maxIVLength = 256; // IV length + + /* + * The size of the header plus the max IV length + */ + static final int headerPlusMaxIVSize = + headerSize // header + + maxIVLength; // iv /* * SSL has a maximum record size. It's header, (compressed) data, - * padding, and a trailer for the MAC. + * padding, and a trailer for the message authentication information (MAC + * for block and stream ciphers, and message authentication tag for AEAD + * ciphers). + * * Some compression algorithms have rare cases where they expand the data. * As we don't support compression at this time, leave that out. */ static final int maxRecordSize = - headerSize // header - + maxIVLength // iv - + maxDataSize // data - + maxPadding // padding - + trailerSize; // MAC + headerPlusMaxIVSize // header + iv + + maxDataSize // data + + maxPadding // padding + + trailerSize; // MAC or AEAD tag static final boolean enableCBCProtection = Debug.getBooleanProperty("jsse.enableCBCProtection", true); @@ -77,8 +86,7 @@ interface Record { static final int maxDataSizeMinusOneByteRecord = maxDataSize // max data size - ( // max one byte record size - headerSize // header - + maxIVLength // iv + headerPlusMaxIVSize // header + iv + 1 // one byte data + maxPadding // padding + trailerSize // MAC @@ -104,11 +112,10 @@ interface Record { * Allocate a smaller array. */ static final int maxAlertRecordSize = - headerSize // header - + maxIVLength // iv - + 2 // alert - + maxPadding // padding - + trailerSize; // MAC + headerPlusMaxIVSize // header + iv + + 2 // alert + + maxPadding // padding + + trailerSize; // MAC /* * The overflow values of integers of 8, 16 and 24 bits. diff --git a/src/share/classes/sun/security/ssl/SSLEngineImpl.java b/src/share/classes/sun/security/ssl/SSLEngineImpl.java index 41415337ca1d71469c127e0ff575448d56fa1d36..2da58577c94b0738546efba334f1c8bf682548fc 100644 --- a/src/share/classes/sun/security/ssl/SSLEngineImpl.java +++ b/src/share/classes/sun/security/ssl/SSLEngineImpl.java @@ -280,7 +280,7 @@ final public class SSLEngineImpl extends SSLEngine { /* * Crypto state that's reinitialized when the session changes. */ - private MAC readMAC, writeMAC; + private Authenticator readAuthenticator, writeAuthenticator; private CipherBox readCipher, writeCipher; // NOTE: compression state would be saved here @@ -377,9 +377,9 @@ final public class SSLEngineImpl extends SSLEngine { * Note: compression support would go here too */ readCipher = CipherBox.NULL; - readMAC = MAC.NULL; + readAuthenticator = MAC.NULL; writeCipher = CipherBox.NULL; - writeMAC = MAC.NULL; + writeAuthenticator = MAC.NULL; // default security parameters for secure renegotiation secureRenegotiation = false; @@ -586,7 +586,7 @@ final public class SSLEngineImpl extends SSLEngine { try { readCipher = handshaker.newReadCipher(); - readMAC = handshaker.newReadMAC(); + readAuthenticator = handshaker.newReadAuthenticator(); } catch (GeneralSecurityException e) { // "can't happen" throw new SSLException("Algorithm missing: ", e); @@ -622,7 +622,7 @@ final public class SSLEngineImpl extends SSLEngine { try { writeCipher = handshaker.newWriteCipher(); - writeMAC = handshaker.newWriteMAC(); + writeAuthenticator = handshaker.newWriteAuthenticator(); } catch (GeneralSecurityException e) { // "can't happen" throw new SSLException("Algorithm missing: ", e); @@ -958,34 +958,15 @@ final public class SSLEngineImpl extends SSLEngine { * throw a fatal alert if the integrity check fails. */ try { - decryptedBB = inputRecord.decrypt(readCipher, readBB); + decryptedBB = inputRecord.decrypt( + readAuthenticator, readCipher, readBB); } catch (BadPaddingException e) { - // RFC 2246 states that decryption_failed should be used - // for this purpose. However, that allows certain attacks, - // so we just send bad record MAC. We also need to make - // sure to always check the MAC to avoid a timing attack - // for the same issue. See paper by Vaudenay et al. - // - // rewind the BB if necessary. - readBB.rewind(); - - inputRecord.checkMAC(readMAC, readBB); - // use the same alert types as for MAC failure below byte alertType = (inputRecord.contentType() == Record.ct_handshake) ? Alerts.alert_handshake_failure : Alerts.alert_bad_record_mac; - fatal(alertType, "Invalid padding", e); - } - - if (!inputRecord.checkMAC(readMAC, decryptedBB)) { - if (inputRecord.contentType() == Record.ct_handshake) { - fatal(Alerts.alert_handshake_failure, - "bad handshake record MAC"); - } else { - fatal(Alerts.alert_bad_record_mac, "bad record MAC"); - } + fatal(alertType, e.getMessage(), e); } // if (!inputRecord.decompress(c)) @@ -1137,7 +1118,7 @@ final public class SSLEngineImpl extends SSLEngine { hsStatus = getHSStatus(hsStatus); if (connectionState < cs_ERROR && !isInboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { - if (checkSequenceNumber(readMAC, + if (checkSequenceNumber(readAuthenticator, inputRecord.contentType())) { hsStatus = getHSStatus(null); } @@ -1290,7 +1271,7 @@ final public class SSLEngineImpl extends SSLEngine { // eventually compress as well. HandshakeStatus hsStatus = - writer.writeRecord(eor, ea, writeMAC, writeCipher); + writer.writeRecord(eor, ea, writeAuthenticator, writeCipher); /* * We only need to check the sequence number state for @@ -1307,7 +1288,7 @@ final public class SSLEngineImpl extends SSLEngine { hsStatus = getHSStatus(hsStatus); if (connectionState < cs_ERROR && !isOutboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { - if (checkSequenceNumber(writeMAC, eor.contentType())) { + if (checkSequenceNumber(writeAuthenticator, eor.contentType())) { hsStatus = getHSStatus(null); } } @@ -1346,7 +1327,7 @@ final public class SSLEngineImpl extends SSLEngine { */ void writeRecord(EngineOutputRecord eor) throws IOException { // eventually compress as well. - writer.writeRecord(eor, writeMAC, writeCipher); + writer.writeRecord(eor, writeAuthenticator, writeCipher); /* * Check the sequence number state @@ -1360,7 +1341,7 @@ final public class SSLEngineImpl extends SSLEngine { * of the last record cannot be wrapped. */ if ((connectionState < cs_ERROR) && !isOutboundDone()) { - checkSequenceNumber(writeMAC, eor.contentType()); + checkSequenceNumber(writeAuthenticator, eor.contentType()); } } @@ -1378,14 +1359,14 @@ final public class SSLEngineImpl extends SSLEngine { * * Return true if the handshake status may be changed. */ - private boolean checkSequenceNumber(MAC mac, byte type) + private boolean checkSequenceNumber(Authenticator authenticator, byte type) throws IOException { /* * Don't bother to check the sequence number for error or * closed connections, or NULL MAC */ - if (connectionState >= cs_ERROR || mac == MAC.NULL) { + if (connectionState >= cs_ERROR || authenticator == MAC.NULL) { return false; } @@ -1393,7 +1374,7 @@ final public class SSLEngineImpl extends SSLEngine { * Conservatively, close the connection immediately when the * sequence number is close to overflow */ - if (mac.seqNumOverflow()) { + if (authenticator.seqNumOverflow()) { /* * TLS protocols do not define a error alert for sequence * number overflow. We use handshake_failure error alert @@ -1416,7 +1397,7 @@ final public class SSLEngineImpl extends SSLEngine { * Don't bother to kickstart the renegotiation when the local is * asking for it. */ - if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) { + if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) { if (debug != null && Debug.isOn("ssl")) { System.out.println(Thread.currentThread().getName() + ", request renegotiation " + diff --git a/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/share/classes/sun/security/ssl/SSLSocketImpl.java index f807c4c58ce842e4df35506394f9a31597a7bdf4..9c8b43f17ad226f1482035beaac48fa37194437f 100644 --- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -292,7 +292,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { /* * Crypto state that's reinitialized when the session changes. */ - private MAC readMAC, writeMAC; + private Authenticator readAuthenticator, writeAuthenticator; private CipherBox readCipher, writeCipher; // NOTE: compression state would be saved here @@ -586,9 +586,9 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * Note: compression support would go here too */ readCipher = CipherBox.NULL; - readMAC = MAC.NULL; + readAuthenticator = MAC.NULL; writeCipher = CipherBox.NULL; - writeMAC = MAC.NULL; + writeAuthenticator = MAC.NULL; // initial security parameters for secure renegotiation secureRenegotiation = false; @@ -829,8 +829,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { boolean holdRecord) throws IOException { // r.compress(c); - r.addMAC(writeMAC); - r.encrypt(writeCipher); + r.encrypt(writeAuthenticator, writeCipher); if (holdRecord) { // If we were requested to delay the record due to possibility @@ -861,7 +860,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * of the last record cannot be wrapped. */ if (connectionState < cs_ERROR) { - checkSequenceNumber(writeMAC, r.contentType()); + checkSequenceNumber(writeAuthenticator, r.contentType()); } // turn off the flag of the first application record @@ -986,29 +985,14 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * throw a fatal alert if the integrity check fails. */ try { - r.decrypt(readCipher); + r.decrypt(readAuthenticator, readCipher); } catch (BadPaddingException e) { - // RFC 2246 states that decryption_failed should be used - // for this purpose. However, that allows certain attacks, - // so we just send bad record MAC. We also need to make - // sure to always check the MAC to avoid a timing attack - // for the same issue. See paper by Vaudenay et al. - r.checkMAC(readMAC); // use the same alert types as for MAC failure below byte alertType = (r.contentType() == Record.ct_handshake) ? Alerts.alert_handshake_failure : Alerts.alert_bad_record_mac; - fatal(alertType, "Invalid padding", e); + fatal(alertType, e.getMessage(), e); } - if (!r.checkMAC(readMAC)) { - if (r.contentType() == Record.ct_handshake) { - fatal(Alerts.alert_handshake_failure, - "bad handshake record MAC"); - } else { - fatal(Alerts.alert_bad_record_mac, "bad record MAC"); - } - } - // if (!r.decompress(c)) // fatal(Alerts.alert_decompression_failure, @@ -1159,7 +1143,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * of the last record cannot be wrapped. */ if (connectionState < cs_ERROR) { - checkSequenceNumber(readMAC, r.contentType()); + checkSequenceNumber(readAuthenticator, r.contentType()); } return; @@ -1182,14 +1166,14 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * implementation would need to wrap a sequence number, it must * renegotiate instead." */ - private void checkSequenceNumber(MAC mac, byte type) + private void checkSequenceNumber(Authenticator authenticator, byte type) throws IOException { /* * Don't bother to check the sequence number for error or * closed connections, or NULL MAC. */ - if (connectionState >= cs_ERROR || mac == MAC.NULL) { + if (connectionState >= cs_ERROR || authenticator == MAC.NULL) { return; } @@ -1197,7 +1181,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * Conservatively, close the connection immediately when the * sequence number is close to overflow */ - if (mac.seqNumOverflow()) { + if (authenticator.seqNumOverflow()) { /* * TLS protocols do not define a error alert for sequence * number overflow. We use handshake_failure error alert @@ -1219,7 +1203,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { * Don't bother to kickstart the renegotiation when the local is * asking for it. */ - if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) { + if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) { if (debug != null && Debug.isOn("ssl")) { System.out.println(Thread.currentThread().getName() + ", request renegotiation " + @@ -2081,7 +2065,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { try { readCipher = handshaker.newReadCipher(); - readMAC = handshaker.newReadMAC(); + readAuthenticator = handshaker.newReadAuthenticator(); } catch (GeneralSecurityException e) { // "can't happen" throw new SSLException("Algorithm missing: ", e); @@ -2112,7 +2096,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { try { writeCipher = handshaker.newWriteCipher(); - writeMAC = handshaker.newWriteMAC(); + writeAuthenticator = handshaker.newWriteAuthenticator(); } catch (GeneralSecurityException e) { // "can't happen" throw new SSLException("Algorithm missing: ", e); diff --git a/test/sun/security/ec/TestEC.java b/test/sun/security/ec/TestEC.java index 41d0f0361dad0adda5f4290b6126e8c0c6208bf3..77217cae467a163a9243df6e7440f4ca027ff643 100644 --- a/test/sun/security/ec/TestEC.java +++ b/test/sun/security/ec/TestEC.java @@ -21,6 +21,11 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /** * @test * @bug 6840752 @@ -30,7 +35,7 @@ * @library ../pkcs11/sslecc * @library ../../../java/security/testlibrary * @compile -XDignore.symbol.file TestEC.java - * @run main TestEC + * @run main/othervm TestEC */ import java.security.NoSuchProviderException; diff --git a/test/sun/security/pkcs11/fips/CipherTest.java b/test/sun/security/pkcs11/fips/CipherTest.java index 32bc6df4e160f16b1bf9ee4857291997e473cc56..2fc81c49c543357ea693d2e941f23af8d89e2327 100644 --- a/test/sun/security/pkcs11/fips/CipherTest.java +++ b/test/sun/security/pkcs11/fips/CipherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, 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 @@ -147,6 +147,25 @@ public class CipherTest { CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), + CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + + CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + + CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + // cipher suites obsoleted since TLS 1.2 CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), diff --git a/test/sun/security/pkcs11/sslecc/CipherTest.java b/test/sun/security/pkcs11/sslecc/CipherTest.java index 64153f58d20653e68a7c84cda37e7304e1976f77..26ecec4df0f03c1fa1b8a5d100e76197dcfa9083 100644 --- a/test/sun/security/pkcs11/sslecc/CipherTest.java +++ b/test/sun/security/pkcs11/sslecc/CipherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, 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 @@ -147,6 +147,25 @@ public class CipherTest { CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), + CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + + CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + + CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + // cipher suites obsoleted since TLS 1.2 CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), diff --git a/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java index aeaf5792cb90f6c9cb4c3f51419474a188a3710f..564342b8ee6b9461d4eaaddc45431ab4c1e93549 100644 --- a/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java +++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java @@ -21,14 +21,16 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 7031830 * @summary bad_record_mac failure on TLSv1.2 enabled connection with SSLEngine * @run main/othervm SSLEngineBadBufferArrayAccess - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. */ /** diff --git a/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKeyGCM.java b/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKeyGCM.java new file mode 100644 index 0000000000000000000000000000000000000000..ac78134f6d4089f749e699f808013b8ac5f8a956 --- /dev/null +++ b/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKeyGCM.java @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2012, 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. + */ + +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + +/* + * @test + * @bug 7030966 + * @summary Support AEAD CipherSuites + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_RSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_DH_anon_WITH_AES_128_GCM_SHA256 + */ + +/* + * Need additional key materials to run the following cases. + * + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * + * Need unlimited JCE Unlimited Strength Jurisdiction Policy to run the + * following cases. + * + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_RSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * @run main/othervm ShortRSAKeyGCM PKIX TLS_DH_anon_WITH_AES_256_GCM_SHA384 + */ + +import java.net.*; +import java.util.*; +import java.io.*; +import javax.net.ssl.*; +import java.security.Security; +import java.security.KeyStore; +import java.security.KeyFactory; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.*; +import java.security.interfaces.*; +import sun.misc.BASE64Decoder; + + +public class ShortRSAKeyGCM { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + // Certificates and key used in the test. + static String trustedCertStr = + "-----BEGIN CERTIFICATE-----\n" + + "MIICkjCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTEwODE5MDE1MjE5WhcNMzIwNzI5MDE1MjE5WjA7MQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwgZ8wDQYJ\n" + + "KoZIhvcNAQEBBQADgY0AMIGJAoGBAM8orG08DtF98TMSscjGsidd1ZoN4jiDpi8U\n" + + "ICz+9dMm1qM1d7O2T+KH3/mxyox7Rc2ZVSCaUD0a3CkhPMnlAx8V4u0H+E9sqso6\n" + + "iDW3JpOyzMExvZiRgRG/3nvp55RMIUV4vEHOZ1QbhuqG4ebN0Vz2DkRft7+flthf\n" + + "vDld6f5JAgMBAAGjgaUwgaIwHQYDVR0OBBYEFLl81dnfp0wDrv0OJ1sxlWzH83Xh\n" + + "MGMGA1UdIwRcMFqAFLl81dnfp0wDrv0OJ1sxlWzH83XhoT+kPTA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2WCAQAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEE\n" + + "BQADgYEALlgaH1gWtoBZ84EW8Hu6YtGLQ/L9zIFmHonUPZwn3Pr//icR9Sqhc3/l\n" + + "pVTxOINuFHLRz4BBtEylzRIOPzK3tg8XwuLb1zd0db90x3KBCiAL6E6cklGEPwLe\n" + + "XYMHDn9eDsaq861Tzn6ZwzMgw04zotPMoZN0mVd/3Qca8UJFucE=\n" + + "-----END CERTIFICATE-----"; + + static String targetCertStr = + "-----BEGIN CERTIFICATE-----\n" + + "MIICNDCCAZ2gAwIBAgIBDDANBgkqhkiG9w0BAQQFADA7MQswCQYDVQQGEwJVUzEN\n" + + "MAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UwHhcN\n" + + "MTExMTA3MTM1NTUyWhcNMzEwNzI1MTM1NTUyWjBPMQswCQYDVQQGEwJVUzENMAsG\n" + + "A1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2UxEjAQBgNV\n" + + "BAMTCWxvY2FsaG9zdDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC3Pb49OSPfOD2G\n" + + "HSXFCFx1GJEZfqG9ZUf7xuIi/ra5dLjPGAaoY5QF2QOa8VnOriQCXDfyXHxsuRnE\n" + + "OomxL7EVAgMBAAGjeDB2MAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQUXNCJK3/dtCIc\n" + + "xb+zlA/JINlvs/MwHwYDVR0jBBgwFoAUuXzV2d+nTAOu/Q4nWzGVbMfzdeEwJwYD\n" + + "VR0lBCAwHgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAzANBgkqhkiG9w0B\n" + + "AQQFAAOBgQB2qIDUxA2caMPpGtUACZAPRUtrGssCINIfItETXJZCx/cRuZ5sP4D9\n" + + "N1acoNDn0hCULe3lhXAeTC9NZ97680yJzregQMV5wATjo1FGsKY30Ma+sc/nfzQW\n" + + "+h/7RhYtoG0OTsiaDCvyhI6swkNJzSzrAccPY4+ZgU8HiDLzZTmM3Q==\n" + + "-----END CERTIFICATE-----"; + + // Private key in the format of PKCS#8, key size is 512 bits. + static String targetPrivateKey = + "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAtz2+PTkj3zg9hh0l\n" + + "xQhcdRiRGX6hvWVH+8biIv62uXS4zxgGqGOUBdkDmvFZzq4kAlw38lx8bLkZxDqJ\n" + + "sS+xFQIDAQABAkByx/5Oo2hQ/w2q4L8z+NTRlJ3vdl8iIDtC/4XPnfYfnGptnpG6\n" + + "ZThQRvbMZiai0xHQPQMszvAHjZVme1eDl3EBAiEA3aKJHynPVCEJhpfCLWuMwX5J\n" + + "1LntwJO7NTOyU5m8rPECIQDTpzn5X44r2rzWBDna/Sx7HW9IWCxNgUD2Eyi2nA7W\n" + + "ZQIgJerEorw4aCAuzQPxiGu57PB6GRamAihEAtoRTBQlH0ECIQDN08FgTtnesgCU\n" + + "DFYLLcw1CiHvc7fZw4neBDHCrC8NtQIgA8TOUkGnpCZlQ0KaI8KfKWI+vxFcgFnH\n" + + "3fnqsTgaUs4="; + + static char passphrase[] = "passphrase".toCharArray(); + + /* + * Is the server ready to serve? + */ + volatile static boolean serverReady = false; + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLContext context = generateSSLContext(null, targetCertStr, + targetPrivateKey); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + SSLServerSocket sslServerSocket = + (SSLServerSocket)sslssf.createServerSocket(serverPort); + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept(); + sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslIS.read(); + sslOS.write('A'); + sslOS.flush(); + + sslSocket.close(); + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLContext context = generateSSLContext(trustedCertStr, null, null); + SSLSocketFactory sslsf = context.getSocketFactory(); + + SSLSocket sslSocket = + (SSLSocket)sslsf.createSocket("localhost", serverPort); + + // enable TLSv1.2 only + sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"}); + + // enable a block cipher + sslSocket.setEnabledCipherSuites(new String[] {cipherSuite}); + + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslOS.write('B'); + sslOS.flush(); + sslIS.read(); + + sslSocket.close(); + } + + /* + * ============================================================= + * The remainder is just support stuff + */ + private static String tmAlgorithm; // trust manager + private static String cipherSuite; // cipher suite + + private static void parseArguments(String[] args) { + tmAlgorithm = args[0]; + cipherSuite = args[1]; + } + + private static SSLContext generateSSLContext(String trustedCertStr, + String keyCertStr, String keySpecStr) throws Exception { + + // generate certificate from cert string + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // create a key store + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + + // import the trused cert + Certificate trusedCert = null; + ByteArrayInputStream is = null; + if (trustedCertStr != null) { + is = new ByteArrayInputStream(trustedCertStr.getBytes()); + trusedCert = cf.generateCertificate(is); + is.close(); + + ks.setCertificateEntry("RSA Export Signer", trusedCert); + } + + if (keyCertStr != null) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + new BASE64Decoder().decodeBuffer(keySpecStr)); + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPrivateKey priKey = + (RSAPrivateKey)kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream(keyCertStr.getBytes()); + Certificate keyCert = cf.generateCertificate(is); + is.close(); + + Certificate[] chain = null; + if (trusedCert != null) { + chain = new Certificate[2]; + chain[0] = keyCert; + chain[1] = trusedCert; + } else { + chain = new Certificate[1]; + chain[0] = keyCert; + } + + // import the key entry. + ks.setKeyEntry("Whatever", priKey, passphrase, chain); + } + + // create SSL context + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); + tmf.init(ks); + + SSLContext ctx = SSLContext.getInstance("TLS"); + if (keyCertStr != null && !keyCertStr.isEmpty()) { + KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); + kmf.init(ks, passphrase); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + ks = null; + } else { + ctx.init(null, tmf.getTrustManagers(), null); + } + + return ctx; + } + + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + // reset the security property to make sure that the algorithms + // and keys used in this test are not disabled. + Security.setProperty("jdk.certpath.disabledAlgorithms", "MD2"); + + if (debug) { + System.setProperty("javax.net.debug", "all"); + } + + /* + * Get the customized arguments. + */ + parseArguments(args); + + /* + * Start the tests. + */ + new ShortRSAKeyGCM(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + ShortRSAKeyGCM() throws Exception { + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + // swallow for now. Show later + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + String whichRemote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + whichRemote = "server"; + } else { + remote = clientException; + local = serverException; + whichRemote = "client"; + } + + /* + * If both failed, return the curthread's exception, but also + * print the remote side Exception + */ + if ((local != null) && (remote != null)) { + System.out.println(whichRemote + " also threw:"); + remote.printStackTrace(); + System.out.println(); + throw local; + } + + if (remote != null) { + throw remote; + } + + if (local != null) { + throw local; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..." + e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + serverException = e; + } finally { + serverReady = true; + } + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..." + e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + clientException = e; + } + } + } +} diff --git a/test/sun/security/ssl/sanity/ciphersuites/CipherSuitesInOrder.java b/test/sun/security/ssl/sanity/ciphersuites/CipherSuitesInOrder.java index 3d081c1bd401ce87365bde3e10265d1e396ad629..9dc1a923dc941ca5f0aa7c0393afe0e7ba68d58e 100644 --- a/test/sun/security/ssl/sanity/ciphersuites/CipherSuitesInOrder.java +++ b/test/sun/security/ssl/sanity/ciphersuites/CipherSuitesInOrder.java @@ -21,13 +21,15 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 7174244 * @summary NPE in Krb5ProxyImpl.getServerKeys() - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. * @run main/othervm CipherSuitesInOrder */ @@ -72,6 +74,22 @@ public class CipherSuitesInOrder { "SSL_RSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_RSA_WITH_RC4_128_SHA", + + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", @@ -83,6 +101,9 @@ public class CipherSuitesInOrder { "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + "TLS_DH_anon_WITH_AES_256_GCM_SHA384", + "TLS_DH_anon_WITH_AES_128_GCM_SHA256", + "TLS_DH_anon_WITH_AES_256_CBC_SHA256", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_DH_anon_WITH_AES_256_CBC_SHA", diff --git a/test/sun/security/ssl/sanity/interop/CipherTest.java b/test/sun/security/ssl/sanity/interop/CipherTest.java index 9c5af7eda4afb029a9fa4e1bcb044429dd93051e..106d4a75e53740b2c9962e29ec0943e08ecfcedc 100644 --- a/test/sun/security/ssl/sanity/interop/CipherTest.java +++ b/test/sun/security/ssl/sanity/interop/CipherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, 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 @@ -148,6 +148,25 @@ public class CipherTest { CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), + CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + + CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + + CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), + CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), + // cipher suites obsoleted since TLS 1.2 CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), diff --git a/test/sun/security/ssl/templates/SSLSocketSSLEngineTemplate.java b/test/sun/security/ssl/templates/SSLSocketSSLEngineTemplate.java index e31409a0f7495590e27963f3d2b0c8b4db32dea1..6bd63ae7104414690d5b95f2f332583b3bf394da 100644 --- a/test/sun/security/ssl/templates/SSLSocketSSLEngineTemplate.java +++ b/test/sun/security/ssl/templates/SSLSocketSSLEngineTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -21,14 +21,15 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 7105780 * @summary Add SSLSocket client/SSLEngine server to templates directory. - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. - * * @run main/othervm SSLSocketSSLEngineTemplate */