Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Whittington <bc@whittington.net.nz>2014-03-07 00:15:11 +0400
committerTim Whittington <bc@whittington.net.nz>2014-03-10 12:27:39 +0400
commitf5212359caf7e5931ae0f48f00e328cc3b017317 (patch)
tree59c9fdb29509651205f28019b1d9c187de515955 /core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
parent8eca220a9b2c68938b19ee6b88e0534d0a07c618 (diff)
Fix buffer underflows in cipher light weight API input/output streams and beef up testing.
Buffer underflows could occur when: - decrypting data > internal buffer size in output stream (input stream was fixed in prior commit) - packet mode AE cipher (e.g. CCM) is used with a data size > internal buffer size (since all output is buffered) Buffer is now sized appropriately to every cipher operation immediately prior to it (using getUpdateOutputSize/getOutputSize as appropriate) in both streams. Tests now run over boundaries of various block/buffer sizes to try to expose issues (0, 64 bit block, 128 bit block, 1K, 2K, 4K).
Diffstat (limited to 'core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java')
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java71
1 files changed, 49 insertions, 22 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
index 66772df5..f80add1a 100644
--- a/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
+++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
@@ -12,28 +12,28 @@ import org.bouncycastle.crypto.modes.AEADBlockCipher;
/**
* A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
* that are read in from the underlying InputStream but have been additionally processed by the
- * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
+ * cipher. The cipher must be fully initialized before being used by a CipherInputStream.
* <p/>
- * For example, if the Cipher is initialized for decryption, the
+ * For example, if the cipher is initialized for decryption, the
* CipherInputStream will attempt to read in data and decrypt them,
* before returning the decrypted data.
*/
public class CipherInputStream
extends FilterInputStream
{
+ private static final int INPUT_BUF_SIZE = 2048;
+
private BufferedBlockCipher bufferedBlockCipher;
private StreamCipher streamCipher;
private AEADBlockCipher aeadBlockCipher;
- private final byte[] buf;
- private final byte[] inBuf;
+ private byte[] buf;
+ private final byte[] inBuf = new byte[INPUT_BUF_SIZE];
private int bufOff;
private int maxBuf;
private boolean finalized;
- private static final int INPUT_BUF_SIZE = 2048;
-
/**
* Constructs a CipherInputStream from an InputStream and a
* BufferedBlockCipher.
@@ -45,11 +45,6 @@ public class CipherInputStream
super(is);
this.bufferedBlockCipher = cipher;
-
- int outSize = cipher.getOutputSize(INPUT_BUF_SIZE);
-
- buf = new byte[(outSize > INPUT_BUF_SIZE) ? outSize : INPUT_BUF_SIZE];
- inBuf = new byte[INPUT_BUF_SIZE];
}
public CipherInputStream(
@@ -59,9 +54,6 @@ public class CipherInputStream
super(is);
this.streamCipher = cipher;
-
- buf = new byte[INPUT_BUF_SIZE];
- inBuf = new byte[INPUT_BUF_SIZE];
}
/**
@@ -72,11 +64,6 @@ public class CipherInputStream
super(is);
this.aeadBlockCipher = cipher;
-
- int outSize = cipher.getOutputSize(INPUT_BUF_SIZE);
-
- buf = new byte[(outSize > INPUT_BUF_SIZE) ? outSize : INPUT_BUF_SIZE];
- inBuf = new byte[INPUT_BUF_SIZE];
}
/**
@@ -112,6 +99,7 @@ public class CipherInputStream
try
{
+ ensureCapacity(read, false);
if (bufferedBlockCipher != null)
{
maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0);
@@ -128,7 +116,7 @@ public class CipherInputStream
}
catch (Exception e)
{
- throw new IOException("Error processing stream " + e);
+ throw new CipherIOException("Error processing stream ", e);
}
}
return maxBuf;
@@ -140,6 +128,7 @@ public class CipherInputStream
try
{
finalized = true;
+ ensureCapacity(0, true);
if (bufferedBlockCipher != null)
{
maxBuf = bufferedBlockCipher.doFinal(buf, 0);
@@ -159,7 +148,7 @@ public class CipherInputStream
}
catch (Exception e)
{
- throw new IOException("Error finalising cipher " + e);
+ throw new CipherIOException("Error finalising cipher ", e);
}
}
@@ -263,11 +252,49 @@ public class CipherInputStream
}
/**
+ * Ensure the ciphertext buffer has space sufficient to accept an upcoming output.
+ *
+ * @param updateSize the size of the pending update.
+ * @param <code>true</code> iff this the cipher is to be finalised.
+ */
+ private void ensureCapacity(int updateSize, boolean finalOutput)
+ {
+ int bufLen = updateSize;
+ if (finalOutput)
+ {
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getOutputSize(updateSize);
+ }
+ }
+ else
+ {
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getUpdateOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getUpdateOutputSize(updateSize);
+ }
+ }
+
+ if ((buf == null) || (buf.length < bufLen))
+ {
+ buf = new byte[bufLen];
+ }
+ }
+
+ /**
* Closes the underlying input stream and finalises the processing of the data by the cipher.
*
* @throws IOException if there was an error closing the input stream.
* @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
- * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
*/
public void close()
throws IOException