diff options
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.java | 244 |
1 files changed, 244 insertions, 0 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 new file mode 100644 index 00000000..bb09a76b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java @@ -0,0 +1,244 @@ +package org.bouncycastle.crypto.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.StreamCipher; + +/** + * A CipherInputStream is composed of an InputStream and a BufferedBlockCipher 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. + * <p> + * 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 BufferedBlockCipher bufferedBlockCipher; + private StreamCipher streamCipher; + + private byte[] buf; + private byte[] inBuf; + + 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. + */ + public CipherInputStream( + InputStream is, + BufferedBlockCipher cipher) + { + super(is); + + this.bufferedBlockCipher = cipher; + + buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)]; + inBuf = new byte[INPUT_BUF_SIZE]; + } + + public CipherInputStream( + InputStream is, + StreamCipher cipher) + { + super(is); + + this.streamCipher = cipher; + + buf = new byte[INPUT_BUF_SIZE]; + inBuf = new byte[INPUT_BUF_SIZE]; + } + + /** + * grab the next chunk of input from the underlying input stream + */ + private int nextChunk() + throws IOException + { + int available = super.available(); + + // must always try to read 1 byte! + // some buggy InputStreams return < 0! + if (available <= 0) + { + available = 1; + } + + if (available > inBuf.length) + { + available = super.read(inBuf, 0, inBuf.length); + } + else + { + available = super.read(inBuf, 0, available); + } + + if (available < 0) + { + if (finalized) + { + return -1; + } + + try + { + if (bufferedBlockCipher != null) + { + maxBuf = bufferedBlockCipher.doFinal(buf, 0); + } + else + { + maxBuf = 0; // a stream cipher + } + } + catch (Exception e) + { + throw new IOException("error processing stream: " + e.toString()); + } + + bufOff = 0; + + finalized = true; + + if (bufOff == maxBuf) + { + return -1; + } + } + else + { + bufOff = 0; + + try + { + if (bufferedBlockCipher != null) + { + maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, available, buf, 0); + } + else + { + streamCipher.processBytes(inBuf, 0, available, buf, 0); + maxBuf = available; + } + } + catch (Exception e) + { + throw new IOException("error processing stream: " + e.toString()); + } + + if (maxBuf == 0) // not enough bytes read for first block... + { + return nextChunk(); + } + } + + return maxBuf; + } + + public int read() + throws IOException + { + if (bufOff == maxBuf) + { + if (nextChunk() < 0) + { + return -1; + } + } + + return buf[bufOff++] & 0xff; + } + + public int read( + byte[] b) + throws IOException + { + return read(b, 0, b.length); + } + + public int read( + byte[] b, + int off, + int len) + throws IOException + { + if (bufOff == maxBuf) + { + if (nextChunk() < 0) + { + return -1; + } + } + + int available = maxBuf - bufOff; + + if (len > available) + { + System.arraycopy(buf, bufOff, b, off, available); + bufOff = maxBuf; + + return available; + } + else + { + System.arraycopy(buf, bufOff, b, off, len); + bufOff += len; + + return len; + } + } + + public long skip( + long n) + throws IOException + { + if (n <= 0) + { + return 0; + } + + int available = maxBuf - bufOff; + + if (n > available) + { + bufOff = maxBuf; + + return available; + } + else + { + bufOff += (int)n; + + return (int)n; + } + } + + public int available() + throws IOException + { + return maxBuf - bufOff; + } + + public void close() + throws IOException + { + super.close(); + } + + public boolean markSupported() + { + return false; + } +} |