--- a/jdk/src/share/classes/sun/security/ssl/CipherBox.java Tue Mar 12 10:35:44 2013 -0400
+++ b/jdk/src/share/classes/sun/security/ssl/CipherBox.java Tue Mar 12 15:31:49 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, 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,18 +29,15 @@
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;
@@ -105,40 +102,19 @@
private final Cipher cipher;
/**
+ * Cipher blocksize, 0 for stream ciphers
+ */
+ private int blockSize;
+
+ /**
* secure random
*/
private SecureRandom random;
/**
- * fixed IV, the implicit nonce of AEAD cipher suite, only apply to
- * AEAD cipher suites
- */
- private final byte[] fixedIv;
-
- /**
- * the key, reserved only for AEAD cipher initialization
- */
- private final Key key;
-
- /**
- * the operation mode, reserved for AEAD cipher initialization
+ * Is the cipher of CBC mode?
*/
- 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;
+ private final boolean isCBCMode;
/**
* Fixed masks of various block size, as the initial decryption IVs
@@ -156,13 +132,7 @@
private CipherBox() {
this.protocolVersion = ProtocolVersion.DEFAULT;
this.cipher = null;
- 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;
+ this.isCBCMode = false;
}
/**
@@ -177,13 +147,13 @@
try {
this.protocolVersion = protocolVersion;
this.cipher = JsseJce.getCipher(bulkCipher.transformation);
- this.mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+ int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
if (random == null) {
random = JsseJce.getSecureRandom();
}
this.random = random;
- this.cipherType = bulkCipher.cipherType;
+ this.isCBCMode = bulkCipher.isCBCMode;
/*
* RFC 4346 recommends two algorithms used to generated the
@@ -201,40 +171,14 @@
iv = getFixedMask(bulkCipher.ivSize);
}
- 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");
- }
+ cipher.init(mode, key, iv, random);
- // Set the record IV length for AEAD cipher
- recordIvSize = bulkCipher.ivSize - bulkCipher.fixedIvSize;
-
- // 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);
+ // 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;
}
} catch (NoSuchAlgorithmException e) {
throw e;
@@ -291,11 +235,26 @@
}
try {
- int blockSize = cipher.getBlockSize();
- if (cipherType == BLOCK_CIPHER) {
+ 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;
+ }
+
len = addPadding(buf, offset, len, blockSize);
}
-
if (debug != null && Debug.isOn("plaintext")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
@@ -308,28 +267,14 @@
System.out);
} catch (IOException e) { }
}
-
-
- 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;
+ 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;
} catch (ShortBufferException e) {
- // unlikely to happen, we should have enough buffer space here
throw new ArrayIndexOutOfBoundsException(e.toString());
}
}
@@ -343,7 +288,7 @@
* set to last position padded/encrypted. The limit may have changed
* because of the added padding bytes.
*/
- int encrypt(ByteBuffer bb, int outLimit) {
+ int encrypt(ByteBuffer bb) {
int len = bb.remaining();
@@ -352,71 +297,66 @@
return len;
}
- int pos = bb.position();
+ try {
+ int pos = bb.position();
- int blockSize = cipher.getBlockSize();
- if (cipherType == BLOCK_CIPHER) {
- // addPadding adjusts pos/limit
- len = addPadding(bb, blockSize);
- bb.position(pos);
- }
+ 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);
- 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);
-
- } catch (IOException e) { }
- }
+ // 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);
- /*
- * 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);
+ // prefix the plaintext
+ bb.put(prefix);
+ bb.position(pos);
}
- 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);
+
+ // addPadding adjusts pos/limit
+ len = addPadding(bb, blockSize);
+ bb.position(pos);
}
- } 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());
+ if (debug != null && Debug.isOn("plaintext")) {
+ try {
+ HexDumpEncoder hd = new HexDumpEncoder();
+
+ System.out.println(
+ "Padded plaintext before ENCRYPTION: len = "
+ + len);
+ hd.encodeBuffer(bb, System.out);
+
+ } catch (IOException e) { }
+ /*
+ * reset back to beginning
+ */
+ bb.position(pos);
}
+ /*
+ * 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");
}
@@ -427,6 +367,10 @@
"in JCE provider " + cipher.getProvider().getName());
}
return newLen;
+ } catch (ShortBufferException e) {
+ RuntimeException exc = new RuntimeException(e.toString());
+ exc.initCause(e);
+ throw exc;
}
}
@@ -454,23 +398,11 @@
}
try {
- 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());
- }
+ 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 (debug != null && Debug.isOn("plaintext")) {
try {
@@ -484,9 +416,7 @@
System.out);
} catch (IOException e) { }
}
-
- if (cipherType == BLOCK_CIPHER) {
- int blockSize = cipher.getBlockSize();
+ if (blockSize != 0) {
newLen = removePadding(buf, offset, newLen,
blockSize, protocolVersion);
@@ -494,11 +424,16 @@
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());
}
}
@@ -528,29 +463,15 @@
*/
int pos = bb.position();
ByteBuffer dup = bb.duplicate();
- 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());
- }
+ 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());
}
- // 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();
@@ -558,33 +479,50 @@
"Padded plaintext after DECRYPTION: len = "
+ newLen);
- hd.encodeBuffer(
- (ByteBuffer)bb.duplicate().position(pos), System.out);
+ hd.encodeBuffer(bb, System.out);
} catch (IOException e) { }
}
/*
* Remove the block padding.
*/
- if (cipherType == BLOCK_CIPHER) {
- int blockSize = cipher.getBlockSize();
+ if (blockSize != 0) {
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
- bb.position(bb.limit());
+ limit = bb.limit();
+ bb.position(limit);
}
}
return newLen;
} catch (ShortBufferException e) {
- // unlikely to happen, we should have enough buffer space here
- throw new ArrayIndexOutOfBoundsException(e.toString());
+ RuntimeException exc = new RuntimeException(e.toString());
+ exc.initCause(e);
+ throw exc;
}
}
@@ -757,8 +695,8 @@
// ignore return value.
cipher.doFinal();
}
- } catch (Exception e) {
- // swallow all types of exceptions.
+ } catch (GeneralSecurityException e) {
+ // swallow for now.
}
}
@@ -768,234 +706,6 @@
* @return true if the cipher use CBC mode, false otherwise.
*/
boolean 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;
+ return isCBCMode;
}
}