2004-10-08 jrandom

* Revamp the AESInputStream so it doesn't allocate any temporary objects
      during its operation.
This commit is contained in:
jrandom
2004-10-08 22:53:03 +00:00
committed by zzz
parent 730da3aa27
commit 65676f8988
3 changed files with 99 additions and 144 deletions

View File

@ -46,66 +46,81 @@ public class AESInputStream extends FilterInputStream {
private long _cumulativePrepared; // how many bytes decrypted and added to _readyBuf
private long _cumulativePaddingStripped; // how many bytes have been stripped
private ByteArrayOutputStream _encryptedBuf; // read from the stream but not yet decrypted
private List _readyBuf; // list of Bytes ready to be consumed, where index 0 is the first
/** read but not yet decrypted */
private byte _encryptedBuf[];
/** how many bytes have been added to the encryptedBuf since it was decrypted? */
private int _writesSinceDecrypt;
/** decrypted bytes ready for reading (first available == index of 0) */
private int _decryptedBuf[];
/** how many bytes are available for reading without decrypt? */
private int _decryptedSize;
private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE;
private final static int READ_SIZE = BLOCK_SIZE;
private final static int DECRYPT_SIZE = BLOCK_SIZE - 1;
public AESInputStream(I2PAppContext context, InputStream source, SessionKey key, byte iv[]) {
public AESInputStream(I2PAppContext context, InputStream source, SessionKey key, byte[] iv) {
super(source);
_context = context;
_log = context.logManager().getLog(AESInputStream.class);
_key = key;
_lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_encryptedBuf = new ByteArrayOutputStream(BLOCK_SIZE);
_readyBuf = new ArrayList(1024);
_encryptedBuf = new byte[BLOCK_SIZE];
_writesSinceDecrypt = 0;
_decryptedBuf = new int[BLOCK_SIZE-1];
_decryptedSize = 0;
_cumulativePaddingStripped = 0;
_eofFound = false;
}
public int read() throws IOException {
while ((!_eofFound) && (_readyBuf.size() <= 0)) {
refill(READ_SIZE);
while ((!_eofFound) && (_decryptedSize <= 0)) {
refill();
}
Integer nval = getNext();
if (nval != null) {
return nval.intValue();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("No byte available. eof? " + _eofFound);
if (_eofFound)
if (_decryptedSize > 0) {
int c = _decryptedBuf[0];
System.arraycopy(_decryptedBuf, 1, _decryptedBuf, 0, _decryptedBuf.length-1);
_decryptedSize--;
return c;
} else if (_eofFound) {
return -1;
throw new IOException("Not EOF, but none available? " + _readyBuf.size() + "/" + _encryptedBuf.size()
+ "/" + _cumulativeRead + "... impossible");
} else {
throw new IOException("Not EOF, but none available? " + _decryptedSize
+ "/" + _writesSinceDecrypt
+ "/" + _cumulativeRead + "... impossible");
}
}
public int read(byte dest[]) throws IOException {
for (int i = 0; i < dest.length; i++) {
int val = read();
if (val == -1) {
// no more to read... can they expect more?
if (_eofFound && (i == 0)) return -1;
return i;
}
dest[i] = (byte) val;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the full buffer of size " + dest.length);
return dest.length;
return read(dest, 0, dest.length);
}
public int read(byte dest[], int off, int len) throws IOException {
byte buf[] = new byte[len];
int read = read(buf);
if (read == -1) return -1;
System.arraycopy(buf, 0, dest, off, read);
return read;
for (int i = 0; i < len; i++) {
int val = read();
if (val == -1) {
// no more to read... can they expect more?
if (_eofFound && (i == 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.info("EOF? " + _eofFound
+ "\nread=" + i + " decryptedSize=" + _decryptedSize
+ " \nencryptedSize=" + _writesSinceDecrypt
+ " \ntotal=" + _cumulativeRead
+ " \npadding=" + _cumulativePaddingStripped
+ " \nprepared=" + _cumulativePrepared);
return -1;
} else {
if (i != len)
if (_log.shouldLog(Log.DEBUG))
_log.info("non-terminal eof: " + _eofFound + " i=" + i + " len=" + len);
}
return i;
}
dest[off+i] = (byte)val;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the full buffer of size " + len);
return len;
}
public long skip(long numBytes) throws IOException {
@ -117,25 +132,15 @@ public class AESInputStream extends FilterInputStream {
}
public int available() throws IOException {
return _readyBuf.size();
return _decryptedSize;
}
public void close() throws IOException {
//_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
//decrypt();
//byte buf[] = new byte[_readyBuf.size()];
//for (int i = 0; i < buf.length; i++)
// buf[i] = ((Integer)_readyBuf.get(i)).byteValue();
//_log.debug("After decrypt: readyBuf.size: " + _readyBuf.size() + "\n val:\t" + Base64.encode(buf));
int ready = _readyBuf.size();
int encrypted = _readyBuf.size();
_readyBuf.clear();
_encryptedBuf.reset();
in.close();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/"
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + ready + " ready, "
+ encrypted + " still encrypted]");
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + _decryptedSize + " ready, "
+ _writesSinceDecrypt + " still encrypted]");
}
public void mark(int readLimit) { // nop
@ -149,116 +154,60 @@ public class AESInputStream extends FilterInputStream {
return false;
}
/**
* Retrieve the next ready byte, or null if no bytes are ready. this does not refill or block
*
*/
private Integer getNext() {
if (_readyBuf.size() > 0) {
return (Integer) _readyBuf.remove(0);
}
return null;
}
/**
* Read at least one new byte from the underlying stream, and up to max new bytes,
* but not necessarily enough for a new decrypted block. This blocks until at least
* one new byte is read from the stream
*
*/
private void refill(int max) throws IOException {
private void refill() throws IOException {
if (!_eofFound) {
byte buf[] = new byte[max];
int read = in.read(buf);
int read = in.read(_encryptedBuf, _writesSinceDecrypt, _encryptedBuf.length - _writesSinceDecrypt);
if (read == -1) {
_eofFound = true;
} else if (read > 0) {
//_log.debug("Read from the source stream " + read + " bytes");
_cumulativeRead += read;
_encryptedBuf.write(buf, 0, read);
_writesSinceDecrypt += read;
}
}
if (false) return; // true to keep the data for decrypt/display on close
if (_encryptedBuf.size() > 0) {
if (_encryptedBuf.size() >= DECRYPT_SIZE) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
decrypt();
if ( (_encryptedBuf.size() > 0) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Bytes left in the encrypted buffer after decrypt: " + _encryptedBuf.size());
} else {
if (_eofFound) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("EOF and not enough bytes to decrypt [size = " + _encryptedBuf.size()
+ " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]!");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not enough bytes to decrypt [size = " + _encryptedBuf.size()
+ " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]");
}
}
if (_writesSinceDecrypt == BLOCK_SIZE) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have " + _writesSinceDecrypt + " available to decrypt... doing so");
decryptBlock();
if ( (_writesSinceDecrypt > 0) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Bytes left in the encrypted buffer after decrypt: "
+ _writesSinceDecrypt);
}
}
/**
* Take (n*BLOCK_SIZE) bytes off the _encryptedBuf, decrypt them, and place
* them on _readyBuf
*
* Decrypt the
*/
private void decrypt() throws IOException {
byte encrypted[] = _encryptedBuf.toByteArray();
_encryptedBuf.reset();
if ((encrypted == null) || (encrypted.length <= 0))
private void decryptBlock() throws IOException {
if (_writesSinceDecrypt != BLOCK_SIZE)
throw new IOException("Error decrypting - no data to decrypt");
if (_decryptedSize != 0)
throw new IOException("wtf, decrypted size is not 0? " + _decryptedSize);
_context.aes().decrypt(_encryptedBuf, 0, _encryptedBuf, 0, _key, _lastBlock, BLOCK_SIZE);
DataHelper.xor(_encryptedBuf, 0, _lastBlock, 0, _encryptedBuf, 0, BLOCK_SIZE);
int payloadBytes = countBlockPayload(_encryptedBuf, 0);
int numBlocks = encrypted.length / BLOCK_SIZE;
if ((encrypted.length % BLOCK_SIZE) != 0) {
// it was flushed / handled off the BLOCK_SIZE segments, so put the excess
// back into the _encryptedBuf for later handling
int trailing = encrypted.length % BLOCK_SIZE;
_encryptedBuf.write(encrypted, encrypted.length - trailing, trailing);
byte nencrypted[] = new byte[encrypted.length - trailing];
System.arraycopy(encrypted, 0, nencrypted, 0, nencrypted.length);
encrypted = nencrypted;
if (_log.shouldLog(Log.WARN))
_log.warn("Decrypt got odd segment - " + trailing
+ " bytes pushed back for later decryption - corrupted or slow data stream perhaps?");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally");
for (int i = 0; i < payloadBytes; i++) {
int c = _encryptedBuf[i];
if (c <= 0)
c += 256;
_decryptedBuf[i] = c;
}
_decryptedSize = payloadBytes;
for (int i = 0; i < numBlocks; i++) {
_context.aes().decrypt(encrypted, i * BLOCK_SIZE, encrypted, i * BLOCK_SIZE, _key, _lastBlock, BLOCK_SIZE);
DataHelper.xor(encrypted, i * BLOCK_SIZE, _lastBlock, 0, encrypted, i * BLOCK_SIZE, BLOCK_SIZE);
int payloadBytes = countBlockPayload(encrypted, i * BLOCK_SIZE);
for (int j = 0; j < payloadBytes; j++) {
int c = encrypted[j + i * BLOCK_SIZE];
if (c <= 0)
c += 256;
_readyBuf.add(new Integer(c));
}
_cumulativePaddingStripped += BLOCK_SIZE - payloadBytes;
_cumulativePrepared += payloadBytes;
System.arraycopy(encrypted, i * BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
}
_cumulativePaddingStripped += BLOCK_SIZE - payloadBytes;
_cumulativePrepared += payloadBytes;
int remaining = encrypted.length % BLOCK_SIZE;
if (remaining != 0) {
_encryptedBuf.write(encrypted, encrypted.length - remaining, remaining);
_log.debug("After pushing " + remaining
+ " bytes back onto the buffer, lets delay 1s our action so we don't fast busy until the net transfers data");
try {
Thread.sleep(1000);
} catch (InterruptedException ie) { // nop
}
} else {
//_log.debug("No remaining encrypted bytes beyond the block size");
}
System.arraycopy(_encryptedBuf, 0, _lastBlock, 0, BLOCK_SIZE);
_writesSinceDecrypt = 0;
}
/**
@ -303,11 +252,11 @@ public class AESInputStream extends FilterInputStream {
}
int remainingBytes() {
return _encryptedBuf.size();
return _writesSinceDecrypt;
}
int readyBytes() {
return _readyBuf.size();
return _decryptedSize;
}
/**
@ -446,7 +395,9 @@ public class AESInputStream extends FilterInputStream {
if (eq) {
//log.info("Equal hashes. hash: " + origHash);
} else {
throw new RuntimeException("NOT EQUAL! len=" + orig.length + "\norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
throw new RuntimeException("NOT EQUAL! len=" + orig.length + " read=" + read
+ "\norig: \t" + Base64.encode(orig) + "\nnew : \t"
+ Base64.encode(fin));
}
boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);

View File

@ -1,4 +1,8 @@
$Id: history.txt,v 1.39 2004/10/07 21:08:11 jrandom Exp $
$Id: history.txt,v 1.40 2004/10/08 13:38:49 jrandom Exp $
2004-10-08 jrandom
* Revamp the AESInputStream so it doesn't allocate any temporary objects
during its operation.
2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart.

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.47 $ $Date: 2004/10/07 21:08:11 $";
public final static String ID = "$Revision: 1.48 $ $Date: 2004/10/08 13:38:48 $";
public final static String VERSION = "0.4.1.1";
public final static long BUILD = 13;
public final static long BUILD = 14;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID);