diff options
author | David Hook <dgh@cryptoworkshop.com> | 2014-03-11 00:28:11 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2014-03-11 00:28:11 +0400 |
commit | 6f6de836df25632bc4f8f291d9dc7dfa121f9e1f (patch) | |
tree | a6ba03424e3cefe9d5e0a0933c00a04428b8707d /core/src/main/java/org/bouncycastle | |
parent | 2a53be10f552694fdd48d70152012c7ea899141c (diff) | |
parent | 23955bbc35d9712b1fec023d32f2e1f1d578df3a (diff) |
Merge branch 'feature/update-size-testing' of https://github.com/timw/bc-java into timw-feature/update-size-testing
Diffstat (limited to 'core/src/main/java/org/bouncycastle')
10 files changed, 216 insertions, 86 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java b/core/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java new file mode 100644 index 00000000..beeb60bc --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java @@ -0,0 +1,26 @@ +package org.bouncycastle.crypto.io; + +import java.io.IOException; + +/** + * {@link IOException} wrapper around an exception indicating a problem with the use of a cipher. + */ +public class CipherIOException + extends IOException +{ + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public CipherIOException(String message, Throwable cause) + { + super(message); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +}
\ No newline at end of file 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 diff --git a/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java index 9beb5b95..2fe95bed 100644 --- a/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java +++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java @@ -118,7 +118,7 @@ public class CipherOutputStream int len) throws IOException { - ensureCapacity(len); + ensureCapacity(len, false); if (bufferedBlockCipher != null) { @@ -149,24 +149,35 @@ public class CipherOutputStream /** * Ensure the ciphertext buffer has space sufficient to accept an upcoming output. * - * @param outputSize the size of the pending update. + * @param updateSize the size of the pending update. + * @param <code>true</code> iff this the cipher is to be finalised. */ - private void ensureCapacity(int outputSize) + private void ensureCapacity(int updateSize, boolean finalOutput) { - // This overestimates buffer on updates for AEAD/padded, but keeps it simple. - int bufLen; - if (bufferedBlockCipher != null) + int bufLen = updateSize; + if (finalOutput) { - bufLen = bufferedBlockCipher.getOutputSize(outputSize); - } - else if (aeadBlockCipher != null) - { - bufLen = aeadBlockCipher.getOutputSize(outputSize); + if (bufferedBlockCipher != null) + { + bufLen = bufferedBlockCipher.getOutputSize(updateSize); + } + else if (aeadBlockCipher != null) + { + bufLen = aeadBlockCipher.getOutputSize(updateSize); + } } else { - bufLen = outputSize; + 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]; @@ -213,7 +224,7 @@ public class CipherOutputStream public void close() throws IOException { - ensureCapacity(0); + ensureCapacity(0, true); IOException error = null; try { @@ -242,7 +253,7 @@ public class CipherOutputStream } catch (Exception e) { - error = new IOException("Error closing stream: " + e); + error = new CipherIOException("Error closing stream: ", e); } try diff --git a/core/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java b/core/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java index b601d4c1..46561c67 100644 --- a/core/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java +++ b/core/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java @@ -8,21 +8,12 @@ import java.io.IOException; * expose invalid ciphertext errors. */ public class InvalidCipherTextIOException - extends IOException + extends CipherIOException { private static final long serialVersionUID = 1L; - private final Throwable cause; - public InvalidCipherTextIOException(String message, Throwable cause) { - super(message); - - this.cause = cause; - } - - public Throwable getCause() - { - return cause; + super(message, cause); } }
\ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java index 71b75954..3a2f73a3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java @@ -7,6 +7,15 @@ import org.bouncycastle.crypto.InvalidCipherTextException; /** * A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data. + * <p/> + * Implementations of this interface may operate in a packet mode (where all input data is buffered and + * processed dugin the call to {@link #doFinal(byte[], int)}), or in a streaming mode (where output data is + * incrementally produced with each call to {@link #processByte(byte, byte[], int)} or + * {@link #processBytes(byte[], int, int, byte[], int)}. + * <br/>This is important to consider during decryption: in a streaming mode, unauthenticated plaintext data + * may be output prior to the call to {@link #doFinal(byte[], int)} that results in an authentication + * failure. The higher level protocol utilising this cipher must ensure the plaintext data is handled + * appropriately until the end of data is reached and the entire ciphertext is authenticated. * @see org.bouncycastle.crypto.params.AEADParameters */ public interface AEADBlockCipher @@ -101,7 +110,11 @@ public interface AEADBlockCipher /** * return the size of the output buffer required for a processBytes * an input of len bytes. - * + * <p/> + * The returned size may be dependent on the initialisation of this cipher + * and may not be accurate once subsequent input data is processed - this method + * should be invoked immediately prior to input data being processed. + * * @param len the length of the input. * @return the space required to accommodate a call to processBytes * with len bytes of input. @@ -111,7 +124,12 @@ public interface AEADBlockCipher /** * return the size of the output buffer required for a processBytes plus a * doFinal with an input of len bytes. - * + * <p/> + * The returned size may be dependent on the initialisation of this cipher + * and may not be accurate once subsequent input data is processed - this method + * should be invoked immediately prior to a call to final processing of input data + * and a call to {@link #doFinal(byte[], int)}. + * * @param len the length of the input. * @return the space required to accommodate a call to processBytes and doFinal * with len bytes of input. diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java index fef51fdb..7f870ca2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.macs.CBCBlockCipherMac; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -42,7 +43,7 @@ public class CCMBlockCipher this.cipher = c; this.blockSize = c.getBlockSize(); this.macBlock = new byte[blockSize]; - + if (blockSize != 16) { throw new IllegalArgumentException("cipher required with a block size of 16."); @@ -99,7 +100,7 @@ public class CCMBlockCipher { throw new IllegalArgumentException("nonce must have length from 7 to 13 octets"); } - + reset(); } @@ -130,6 +131,10 @@ public class CCMBlockCipher public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff) throws DataLengthException, IllegalStateException { + if (in.length < (inOff + inLen)) + { + throw new DataLengthException("Input buffer too short"); + } data.write(in, inOff, inLen); return 0; @@ -155,15 +160,15 @@ public class CCMBlockCipher /** * Returns a byte array containing the mac calculated as part of the * last encrypt or decrypt operation. - * + * * @return the last mac calculated. */ public byte[] getMac() { byte[] mac = new byte[macSize]; - + System.arraycopy(macBlock, 0, mac, 0, mac.length); - + return mac; } @@ -267,7 +272,7 @@ public class CCMBlockCipher outputLen = inLen + macSize; if (output.length < (outputLen + outOff)) { - throw new DataLengthException("Output buffer too short."); + throw new OutputLengthException("Output buffer too short."); } calculateMac(in, inOff, inLen, macBlock); @@ -300,7 +305,7 @@ public class CCMBlockCipher outputLen = inLen - macSize; if (output.length < (outputLen + outOff)) { - throw new DataLengthException("Output buffer too short."); + throw new OutputLengthException("Output buffer too short."); } System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize); @@ -350,18 +355,18 @@ public class CCMBlockCipher // build b0 // byte[] b0 = new byte[16]; - + if (hasAssociatedText()) { b0[0] |= 0x40; } - + b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3; b0[0] |= ((15 - nonce.length) - 1) & 0x7; - + System.arraycopy(nonce, 0, b0, 1, nonce.length); - + int q = dataLen; int count = 1; while (q > 0) @@ -370,22 +375,22 @@ public class CCMBlockCipher q >>>= 8; count++; } - + cMac.update(b0, 0, b0.length); - + // // process associated text // if (hasAssociatedText()) { int extra; - + int textLength = getAssociatedTextLength(); if (textLength < ((1 << 16) - (1 << 8))) { cMac.update((byte)(textLength >> 8)); cMac.update((byte)textLength); - + extra = 2; } else // can't go any higher than 2^32 @@ -396,7 +401,7 @@ public class CCMBlockCipher cMac.update((byte)(textLength >> 16)); cMac.update((byte)(textLength >> 8)); cMac.update((byte)textLength); - + extra = 6; } @@ -418,7 +423,7 @@ public class CCMBlockCipher } } } - + // // add the text // diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java index 8f740006..209d5cdb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java @@ -5,22 +5,23 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.macs.CMac; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; /** - * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and + * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and * Efficiency - by M. Bellare, P. Rogaway, D. Wagner. - * + * * http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf - * - * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block - * cipher to encrypt and authenticate data. It's on-line (the length of a + * + * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block + * cipher to encrypt and authenticate data. It's on-line (the length of a * message isn't needed to begin processing it), has good performances, it's * simple and provably secure (provided the underlying block cipher is secure). - * + * * Of course, this implementations is NOT thread-safe. */ public class EAXBlockCipher @@ -43,7 +44,7 @@ public class EAXBlockCipher private byte[] nonceMac; private byte[] associatedTextMac; private byte[] macBlock; - + private int macSize; private byte[] bufBlock; private int bufOff; @@ -61,7 +62,6 @@ public class EAXBlockCipher blockSize = cipher.getBlockSize(); mac = new CMac(cipher); macBlock = new byte[blockSize]; - bufBlock = new byte[blockSize * 2]; associatedTextMac = new byte[mac.getMacSize()]; nonceMac = new byte[mac.getMacSize()]; this.cipher = new SICBlockCipher(cipher); @@ -113,6 +113,8 @@ public class EAXBlockCipher throw new IllegalArgumentException("invalid parameters passed to EAX"); } + bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)]; + byte[] tag = new byte[blockSize]; // Key reuse implemented in CBC mode of underlying CMac @@ -123,9 +125,9 @@ public class EAXBlockCipher mac.update(nonce, 0, nonce.length); mac.doFinal(nonceMac, 0); - // Same BlockCipher underlies this and the mac, so reuse last key on cipher + // Same BlockCipher underlies this and the mac, so reuse last key on cipher cipher.init(true, new ParametersWithIV(null, nonceMac)); - + reset(); } @@ -218,6 +220,11 @@ public class EAXBlockCipher { initCipher(); + if (in.length < (inOff + len)) + { + throw new DataLengthException("Input buffer too short"); + } + int resultLen = 0; for (int i = 0; i != len; i++) @@ -240,12 +247,11 @@ public class EAXBlockCipher if (forEncryption) { - if (out.length < (outOff + extra)) + if (out.length < (outOff + extra + macSize)) { - throw new DataLengthException("Output buffer too short"); + throw new OutputLengthException("Output buffer too short"); } cipher.processBlock(bufBlock, 0, tmp, 0); - cipher.processBlock(bufBlock, blockSize, tmp, blockSize); System.arraycopy(tmp, 0, out, outOff, extra); @@ -261,6 +267,10 @@ public class EAXBlockCipher } else { + if (out.length < (outOff + extra - macSize)) + { + throw new OutputLengthException("Output buffer too short"); + } if (extra < macSize) { throw new InvalidCipherTextException("data too short"); @@ -270,7 +280,6 @@ public class EAXBlockCipher mac.update(bufBlock, 0, extra - macSize); cipher.processBlock(bufBlock, 0, tmp, 0); - cipher.processBlock(bufBlock, blockSize, tmp, blockSize); System.arraycopy(tmp, 0, out, outOff, extra - macSize); } @@ -329,6 +338,10 @@ public class EAXBlockCipher if (bufOff == bufBlock.length) { + if (out.length < (outOff + blockSize)) + { + throw new OutputLengthException("Output buffer is too short"); + } // TODO Could move the processByte(s) calls to here // initCipher(); @@ -347,8 +360,12 @@ public class EAXBlockCipher size = cipher.processBlock(bufBlock, 0, out, outOff); } - bufOff = blockSize; - System.arraycopy(bufBlock, blockSize, bufBlock, 0, blockSize); + bufOff = 0; + if (!forEncryption) + { + System.arraycopy(bufBlock, blockSize, bufBlock, 0, macSize); + bufOff = macSize; + } return size; } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java index 9e617ec9..59c2eb36 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java @@ -4,6 +4,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.modes.gcm.GCMExponentiator; import org.bouncycastle.crypto.modes.gcm.GCMMultiplier; import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator; @@ -23,7 +24,7 @@ public class GCMBlockCipher { private static final int BLOCK_SIZE = 16; - // not final due to a compiler bug + // not final due to a compiler bug private BlockCipher cipher; private GCMMultiplier multiplier; private GCMExponentiator exp; @@ -102,7 +103,7 @@ public class GCMBlockCipher throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits); } - macSize = macSizeBits / 8; + macSize = macSizeBits / 8; keyParam = param.getKey(); } else if (params instanceof ParametersWithIV) @@ -119,7 +120,7 @@ public class GCMBlockCipher throw new IllegalArgumentException("invalid parameters passed to GCM"); } - int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize); + int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize); this.bufBlock = new byte[bufLength]; if (nonce == null || nonce.length < 1) @@ -271,6 +272,10 @@ public class GCMBlockCipher public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException { + if (in.length < (inOff + len)) + { + throw new DataLengthException("Input buffer too short"); + } int resultLen = 0; for (int i = 0; i < len; ++i) @@ -288,6 +293,10 @@ public class GCMBlockCipher private void outputBlock(byte[] output, int offset) { + if (output.length < (offset + BLOCK_SIZE)) + { + throw new OutputLengthException("Output buffer too short"); + } if (totalLength == 0) { initCipher(); @@ -324,6 +333,10 @@ public class GCMBlockCipher if (extra > 0) { + if (out.length < (outOff + extra)) + { + throw new OutputLengthException("Output buffer too short"); + } gCTRPartial(bufBlock, 0, extra, out, outOff); } @@ -390,6 +403,10 @@ public class GCMBlockCipher if (forEncryption) { + if (out.length < (outOff + extra + macSize)) + { + throw new OutputLengthException("Output buffer too short"); + } // Append T to the message System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize); resultLen += macSize; diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java index d0345c4b..66db8beb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -317,6 +318,10 @@ public class OCBBlockCipher public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) throws DataLengthException { + if (input.length < (inOff + len)) + { + throw new DataLengthException("Input buffer too short"); + } int resultLen = 0; for (int i = 0; i < len; ++i) @@ -378,6 +383,10 @@ public class OCBBlockCipher xor(mainBlock, Pad); + if (output.length < (outOff + mainBlockPos)) + { + throw new OutputLengthException("Output buffer too short"); + } System.arraycopy(mainBlock, 0, output, outOff, mainBlockPos); if (!forEncryption) @@ -405,6 +414,10 @@ public class OCBBlockCipher if (forEncryption) { + if (output.length < (outOff + resultLen + macSize)) + { + throw new OutputLengthException("Output buffer too short"); + } // Append tag to the message System.arraycopy(macBlock, 0, output, outOff + resultLen, macSize); resultLen += macSize; @@ -456,6 +469,11 @@ public class OCBBlockCipher protected void processMainBlock(byte[] output, int outOff) { + if (output.length < (outOff + BLOCK_SIZE)) + { + throw new OutputLengthException("Output buffer too short"); + } + /* * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks */ diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index ee3fd60e..d5928f70 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -125,7 +125,7 @@ public class PaddedBufferedBlockCipher if (leftOver == 0) { - return total - buf.length; + return Math.max(0, total - buf.length); } return total - leftOver; |