| BlowfishInputStream |
package BlowfishJ;
import java.io.*;
import java.util.*;
/**
* An InputStream that reads from a Blowfish encrypted file.
* @author Dale Anson (danson@germane-software.com), February, 2002
*/
public class BlowfishInputStream extends InputStream {
private PushbackInputStream _in;
private String _passphrase;
private BlowfishCBC _cbc;
private long _iv;
private byte[] _in_buffer;
private int _bytes_read = 0;
private int _buffer_index = 0;
private boolean _started = false;
/**
* @param passphrase the passphrase that was used to encrypt the original data.
* @param is the input stream from which bytes will be read
*/
public BlowfishInputStream(String passphrase, InputStream is) {
_passphrase = passphrase;
_in = new PushbackInputStream(new BufferedInputStream(is));
// hash down the password to a 160bit key
SHA1 hasher = new SHA1();
hasher.update(_passphrase);
hasher.finalize();
// setup the encryptor (use a dummy IV)
_cbc = new BlowfishCBC(hasher.getDigest(), 0);
hasher.clear();
// create the input buffer
_in_buffer = new byte[BlowfishCBC.BLOCKSIZE];
}
/**
* Reads the next byte of data from this input stream. The value byte is returned
* as an int in the range 0 to 255. If no byte is available because the end of the
* stream has been reached, the value -1 is returned. This method blocks until
* input data is available, the end of the stream is detected, or an exception is
* thrown.
* @return the next byte of data or -1 if the end of the stream has been reached.
*/
public int read() throws IOException {
if ( !_started ) {
decryptBuffer(); // load the iv
if ( _bytes_read < BlowfishCBC.BLOCKSIZE ) {
return -1;
}
decryptBuffer(); // load the input buffer
if ( _bytes_read == -1 ) {
return -1;
}
_buffer_index = 0;
}
// check that all bytes from input stream have been returned
if ( _bytes_read < _in_buffer.length && _buffer_index == _bytes_read ) {
return -1;
}
// check if all bytes in buffer have been returned, if so,
// need to refill the buffer
if ( _buffer_index == _bytes_read ) {
decryptBuffer();
if ( _bytes_read == -1 ) {
return -1;
}
_buffer_index = 0;
}
// return the next byte from the buffer
int rtn = (int)_in_buffer[_buffer_index] & 0xff;
++_buffer_index;
return rtn;
}
/**
* Reads enough bytes from the underlying input stream to decrypt, and then
* decrypts it.
*/
private void decryptBuffer() throws IOException {
_bytes_read = _in.read(_in_buffer, 0, _in_buffer.length);
if ( _bytes_read == -1 ) {
return;
}
if ( !_started ) {
// did the entire CBC IV get read?
if ( _bytes_read < _in_buffer.length )
return;
// set the CBC IV, it is the first 8 bytes of the input stream
long iv = BinConverter.byteArrayToLong(_in_buffer, 0);
_cbc.setCBCIV(iv);
_started = true;
return;
}
// decrypt the buffer
_cbc.decrypt(_in_buffer);
// check for last block -- if the original data did not fit exactly into
// an 8 byte block, the block was padded with enough bytes to fill the
// block, then encrypted. The last byte is ALWAYS the number of pad bytes,
// that means the last block could be eight 8's, which is all padding.
int end = _in.read();
if ( end == -1 ) {
// all done
int pad_count = _in_buffer[_in_buffer.length - 1];
if ( pad_count > _in_buffer.length || pad_count < 1 ) {
// the last byte wasn't a number, so it must be good data
return;
}
else {
// adjust bytes read to reflect the number of 'good' bytes
_bytes_read = _in_buffer.length - pad_count;
if ( _bytes_read == 0 ) {
_bytes_read = -1;
}
}
}
else {
_in.unread(end);
}
}
/**
* @see java.io.InputStream
*/
public boolean markSupported() {
return _in.markSupported();
}
/**
* @see java.io.InputStream
*/
public void mark(int readlimit) {
_in.mark(readlimit);
}
/**
* @see java.io.InputStream
*/
public int available() throws IOException {
return _in.available();
}
/**
* @see java.io.InputStream
*/
public void close() throws IOException {
_in.close();
_cbc.cleanUp();
return;
}
}
| BlowfishInputStream |