--- a/jdk/src/share/classes/sun/security/ssl/CipherBox.java Fri Oct 29 12:35:07 2010 +0200
+++ b/jdk/src/share/classes/sun/security/ssl/CipherBox.java Sat Oct 30 18:39:17 2010 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2010, 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
@@ -28,6 +28,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.util.Hashtable;
import java.security.*;
import javax.crypto.*;
@@ -50,6 +51,37 @@
* Individual instances are obtained by calling the static method
* newCipherBox(), which should only be invoked by BulkCipher.newCipher().
*
+ * In RFC 2246, with bock ciphers in CBC mode, the Initialization
+ * Vector (IV) for the first record is generated with the other keys
+ * and secrets when the security parameters are set. The IV for
+ * subsequent records is the last ciphertext block from the previous
+ * record.
+ *
+ * In RFC 4346, the implicit Initialization Vector (IV) is replaced
+ * with an explicit IV to protect against CBC attacks. RFC 4346
+ * recommends two algorithms used to generated the per-record IV.
+ * The implementation uses the algorithm (2)(b), as described at
+ * section 6.2.3.2 of RFC 4346.
+ *
+ * The usage of IV in CBC block cipher can be illustrated in
+ * the following diagrams.
+ *
+ * (random)
+ * R P1 IV C1
+ * | | | |
+ * SIV---+ |-----+ |-... |----- |------
+ * | | | | | | | |
+ * +----+ | +----+ | +----+ | +----+ |
+ * | Ek | | + Ek + | | Dk | | | Dk | |
+ * +----+ | +----+ | +----+ | +----+ |
+ * | | | | | | | |
+ * |----| |----| SIV--+ |----| |-...
+ * | | | |
+ * IV C1 R P1
+ * (discard)
+ *
+ * CBC Encryption CBC Decryption
+ *
* NOTE that any ciphering involved in key exchange (e.g. with RSA) is
* handled separately.
*
@@ -76,6 +108,21 @@
private int blockSize;
/**
+ * secure random
+ */
+ private SecureRandom random;
+
+ /**
+ * Fixed masks of various block size, as the initial decryption IVs
+ * for TLS 1.1 or later.
+ *
+ * For performance, we do not use random IVs. As the initial decryption
+ * IVs will be discarded by TLS decryption processes, so the fixed masks
+ * do not hurt cryptographic strength.
+ */
+ private static Hashtable<Integer, IvParameterSpec> masks;
+
+ /**
* NULL cipherbox. Identity operation, no encryption.
*/
private CipherBox() {
@@ -90,14 +137,37 @@
* implementation could be found.
*/
private CipherBox(ProtocolVersion protocolVersion, BulkCipher bulkCipher,
- SecretKey key, IvParameterSpec iv, boolean encrypt)
- throws NoSuchAlgorithmException {
+ SecretKey key, IvParameterSpec iv, SecureRandom random,
+ boolean encrypt) throws NoSuchAlgorithmException {
try {
this.protocolVersion = protocolVersion;
this.cipher = JsseJce.getCipher(bulkCipher.transformation);
int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
- cipher.init(mode, key, iv);
- // do not call getBlockSize until after init()
+
+ if (random == null) {
+ random = JsseJce.getSecureRandom();
+ }
+ this.random = random;
+
+ /*
+ * RFC 4346 recommends two algorithms used to generated the
+ * per-record IV. The implementation uses the algorithm (2)(b),
+ * as described at section 6.2.3.2 of RFC 4346.
+ *
+ * As we don't care about the initial IV value for TLS 1.1 or
+ * later, so if the "iv" parameter is null, we use the default
+ * value generated by Cipher.init() for encryption, and a fixed
+ * mask for decryption.
+ */
+ if (iv == null && bulkCipher.ivSize != 0 &&
+ mode == Cipher.DECRYPT_MODE &&
+ protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ iv = getFixedMask(bulkCipher.ivSize);
+ }
+
+ 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
@@ -119,19 +189,37 @@
* Factory method to obtain a new CipherBox object.
*/
static CipherBox newCipherBox(ProtocolVersion version, BulkCipher cipher,
- SecretKey key, IvParameterSpec iv, boolean encrypt)
- throws NoSuchAlgorithmException {
+ SecretKey key, IvParameterSpec iv, SecureRandom random,
+ boolean encrypt) throws NoSuchAlgorithmException {
if (cipher.allowed == false) {
throw new NoSuchAlgorithmException("Unsupported cipher " + cipher);
}
+
if (cipher == B_NULL) {
return NULL;
} else {
- return new CipherBox(version, cipher, key, iv, encrypt);
+ return new CipherBox(version, cipher, key, iv, random, encrypt);
}
}
/*
+ * Get a fixed mask, as the initial decryption IVs for TLS 1.1 or later.
+ */
+ private static IvParameterSpec getFixedMask(int ivSize) {
+ if (masks == null) {
+ masks = new Hashtable<Integer, IvParameterSpec>(5);
+ }
+
+ IvParameterSpec iv = masks.get(ivSize);
+ if (iv == null) {
+ iv = new IvParameterSpec(new byte[ivSize]);
+ masks.put(ivSize, iv);
+ }
+
+ return iv;
+ }
+
+ /*
* Encrypts a block of data, returning the size of the
* resulting block.
*/
@@ -139,8 +227,26 @@
if (cipher == null) {
return len;
}
+
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;
+ }
+
len = addPadding(buf, offset, len, blockSize);
}
if (debug != null && Debug.isOn("plaintext")) {
@@ -189,6 +295,34 @@
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()) {
+ buf = bb.array();
+ System.arraycopy(buf, pos,
+ buf, 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);
+
+ // prefix the plaintext
+ bb.put(prefix);
+ bb.position(pos);
+ }
+
// addPadding adjusts pos/limit
len = addPadding(bb, blockSize);
bb.position(pos);
@@ -236,11 +370,25 @@
/*
* Decrypts a block of data, returning the size of the
* resulting block if padding was required.
+ *
+ * For SSLv3 and TLSv1.0, with block ciphers in CBC mode the
+ * Initialization Vector (IV) for the first record is generated by
+ * the handshake protocol, the IV for subsequent records is the
+ * last ciphertext block from the previous record.
+ *
+ * From TLSv1.1, the implicit IV is replaced with an explicit IV to
+ * protect against CBC attacks.
+ *
+ * Differentiating between bad_record_mac and decryption_failed alerts
+ * may permit certain attacks against CBC mode. It is preferable to
+ * uniformly use the bad_record_mac alert to hide the specific type of
+ * the error.
*/
int decrypt(byte[] buf, int offset, int len) throws BadPaddingException {
if (cipher == null) {
return len;
}
+
try {
int newLen = cipher.update(buf, offset, len, buf, offset);
if (newLen != len) {
@@ -263,6 +411,18 @@
if (blockSize != 0) {
newLen = removePadding(buf, offset, newLen,
blockSize, protocolVersion);
+
+ if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
+ 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) {
@@ -277,6 +437,8 @@
* point to the end of the decrypted/depadded data. The initial
* limit and new limit may be different, given we may
* have stripped off some padding bytes.
+ *
+ * @see decrypt(byte[], int, int)
*/
int decrypt(ByteBuffer bb) throws BadPaddingException {
@@ -292,7 +454,6 @@
* Decrypt "in-place".
*/
int pos = bb.position();
-
ByteBuffer dup = bb.duplicate();
int newLen = cipher.update(dup, bb);
if (newLen != len) {
@@ -320,6 +481,33 @@
if (blockSize != 0) {
bb.position(pos);
newLen = removePadding(bb, blockSize, protocolVersion);
+
+ 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()) {
+ buf = bb.array();
+ System.arraycopy(buf, pos + blockSize,
+ buf, 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);
+ }
}
return newLen;
} catch (ShortBufferException e) {