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 _cumulativePrepared; // how many bytes decrypted and added to _readyBuf
private long _cumulativePaddingStripped; // how many bytes have been stripped private long _cumulativePaddingStripped; // how many bytes have been stripped
private ByteArrayOutputStream _encryptedBuf; // read from the stream but not yet decrypted /** read but not yet decrypted */
private List _readyBuf; // list of Bytes ready to be consumed, where index 0 is the first 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 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); super(source);
_context = context; _context = context;
_log = context.logManager().getLog(AESInputStream.class); _log = context.logManager().getLog(AESInputStream.class);
_key = key; _key = key;
_lastBlock = new byte[BLOCK_SIZE]; _lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_encryptedBuf = new ByteArrayOutputStream(BLOCK_SIZE); _encryptedBuf = new byte[BLOCK_SIZE];
_readyBuf = new ArrayList(1024); _writesSinceDecrypt = 0;
_decryptedBuf = new int[BLOCK_SIZE-1];
_decryptedSize = 0;
_cumulativePaddingStripped = 0; _cumulativePaddingStripped = 0;
_eofFound = false; _eofFound = false;
} }
public int read() throws IOException { public int read() throws IOException {
while ((!_eofFound) && (_readyBuf.size() <= 0)) { while ((!_eofFound) && (_decryptedSize <= 0)) {
refill(READ_SIZE); refill();
} }
Integer nval = getNext(); if (_decryptedSize > 0) {
if (nval != null) { int c = _decryptedBuf[0];
return nval.intValue(); System.arraycopy(_decryptedBuf, 1, _decryptedBuf, 0, _decryptedBuf.length-1);
} _decryptedSize--;
if (_log.shouldLog(Log.DEBUG)) return c;
_log.debug("No byte available. eof? " + _eofFound); } else if (_eofFound) {
if (_eofFound)
return -1; return -1;
} else {
throw new IOException("Not EOF, but none available? " + _readyBuf.size() + "/" + _encryptedBuf.size() throw new IOException("Not EOF, but none available? " + _decryptedSize
+ "/" + _cumulativeRead + "... impossible"); + "/" + _writesSinceDecrypt
+ "/" + _cumulativeRead + "... impossible");
}
} }
public int read(byte dest[]) throws IOException { public int read(byte dest[]) throws IOException {
for (int i = 0; i < dest.length; i++) { return read(dest, 0, dest.length);
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;
} }
public int read(byte dest[], int off, int len) throws IOException { public int read(byte dest[], int off, int len) throws IOException {
byte buf[] = new byte[len]; for (int i = 0; i < len; i++) {
int read = read(buf); int val = read();
if (read == -1) return -1; if (val == -1) {
System.arraycopy(buf, 0, dest, off, read); // no more to read... can they expect more?
return read; 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 { public long skip(long numBytes) throws IOException {
@@ -117,25 +132,15 @@ public class AESInputStream extends FilterInputStream {
} }
public int available() throws IOException { public int available() throws IOException {
return _readyBuf.size(); return _decryptedSize;
} }
public void close() throws IOException { 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(); in.close();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/" _log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/"
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + ready + " ready, " + _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + _decryptedSize + " ready, "
+ encrypted + " still encrypted]"); + _writesSinceDecrypt + " still encrypted]");
} }
public void mark(int readLimit) { // nop public void mark(int readLimit) { // nop
@@ -149,116 +154,60 @@ public class AESInputStream extends FilterInputStream {
return false; 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, * 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 * but not necessarily enough for a new decrypted block. This blocks until at least
* one new byte is read from the stream * one new byte is read from the stream
* *
*/ */
private void refill(int max) throws IOException { private void refill() throws IOException {
if (!_eofFound) { if (!_eofFound) {
byte buf[] = new byte[max]; int read = in.read(_encryptedBuf, _writesSinceDecrypt, _encryptedBuf.length - _writesSinceDecrypt);
int read = in.read(buf);
if (read == -1) { if (read == -1) {
_eofFound = true; _eofFound = true;
} else if (read > 0) { } else if (read > 0) {
//_log.debug("Read from the source stream " + read + " bytes");
_cumulativeRead += read; _cumulativeRead += read;
_encryptedBuf.write(buf, 0, read); _writesSinceDecrypt += read;
} }
} }
if (false) return; // true to keep the data for decrypt/display on close if (_writesSinceDecrypt == BLOCK_SIZE) {
if (_encryptedBuf.size() > 0) { if (_log.shouldLog(Log.DEBUG))
if (_encryptedBuf.size() >= DECRYPT_SIZE) { _log.debug("We have " + _writesSinceDecrypt + " available to decrypt... doing so");
if (_log.shouldLog(Log.DEBUG)) decryptBlock();
_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so"); if ( (_writesSinceDecrypt > 0) && (_log.shouldLog(Log.DEBUG)) )
decrypt(); _log.debug("Bytes left in the encrypted buffer after decrypt: "
if ( (_encryptedBuf.size() > 0) && (_log.shouldLog(Log.DEBUG)) ) + _writesSinceDecrypt);
_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 +"]");
}
}
} }
} }
/** /**
* Take (n*BLOCK_SIZE) bytes off the _encryptedBuf, decrypt them, and place * Decrypt the
* them on _readyBuf
*
*/ */
private void decrypt() throws IOException { private void decryptBlock() throws IOException {
byte encrypted[] = _encryptedBuf.toByteArray(); if (_writesSinceDecrypt != BLOCK_SIZE)
_encryptedBuf.reset();
if ((encrypted == null) || (encrypted.length <= 0))
throw new IOException("Error decrypting - no data to decrypt"); 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; for (int i = 0; i < payloadBytes; i++) {
if ((encrypted.length % BLOCK_SIZE) != 0) { int c = _encryptedBuf[i];
// it was flushed / handled off the BLOCK_SIZE segments, so put the excess if (c <= 0)
// back into the _encryptedBuf for later handling c += 256;
int trailing = encrypted.length % BLOCK_SIZE; _decryptedBuf[i] = c;
_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");
} }
_decryptedSize = payloadBytes;
for (int i = 0; i < numBlocks; i++) { _cumulativePaddingStripped += BLOCK_SIZE - payloadBytes;
_context.aes().decrypt(encrypted, i * BLOCK_SIZE, encrypted, i * BLOCK_SIZE, _key, _lastBlock, BLOCK_SIZE); _cumulativePrepared += payloadBytes;
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);
}
int remaining = encrypted.length % BLOCK_SIZE; System.arraycopy(_encryptedBuf, 0, _lastBlock, 0, BLOCK_SIZE);
if (remaining != 0) {
_encryptedBuf.write(encrypted, encrypted.length - remaining, remaining); _writesSinceDecrypt = 0;
_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");
}
} }
/** /**
@@ -303,11 +252,11 @@ public class AESInputStream extends FilterInputStream {
} }
int remainingBytes() { int remainingBytes() {
return _encryptedBuf.size(); return _writesSinceDecrypt;
} }
int readyBytes() { int readyBytes() {
return _readyBuf.size(); return _decryptedSize;
} }
/** /**
@@ -446,7 +395,9 @@ public class AESInputStream extends FilterInputStream {
if (eq) { if (eq) {
//log.info("Equal hashes. hash: " + origHash); //log.info("Equal hashes. hash: " + origHash);
} else { } 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); boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length); 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 2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart. * Don't kill the establisher threads during a soft restart.

View File

@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
* *
*/ */
public class RouterVersion { 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 String VERSION = "0.4.1.1";
public final static long BUILD = 13; public final static long BUILD = 14;
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION); System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID); System.out.println("Router ID: " + RouterVersion.ID);