forked from I2P_Developers/i2p.i2p
(mmMMmm profiling)
2004-10-30 jrandom * Cache the temporary objects used in the AES encryption/decryption process so that AES doesn't require any memory allocation to process data. * Dramatically reduce memory usage within various crypto implementations by avoiding unnecessary (though simplifying) buffers. * If we specify some tags to be sent in an I2CP message explicitly, use only those, not those plus a new set (otherwise we aren't sure on ACK which set was delivered) * Allow configuration for the partial send timeout (how long before resending a message down a different tunnel in a lease). This can be updated with the "router.clientPartialSendTimeout" router config prop. * Logging
This commit is contained in:
@ -109,8 +109,8 @@ class I2CPMessageProducer {
|
||||
// generateNewTags would only generate tags if necessary
|
||||
|
||||
data.setEncryptedData(encr);
|
||||
_log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: "
|
||||
+ data.calculateHash());
|
||||
//_log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: "
|
||||
// + data.calculateHash());
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -114,17 +114,21 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
Set sentTags = null;
|
||||
int oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
|
||||
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
|
||||
if (oldTags < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
//_log.error("** sendBestEffort only had " + oldTags + " adding 50");
|
||||
} else if (availTimeLeft < 30 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 30 seconds, we want more
|
||||
sentTags = createNewTags(50);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
} else {
|
||||
//_log.error("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
|
||||
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
|
||||
if (oldTags < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
//_log.error("** sendBestEffort only had " + oldTags + " adding 50");
|
||||
} else if (availTimeLeft < 30 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 30 seconds, we want more
|
||||
sentTags = createNewTags(50);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
} else {
|
||||
//_log.error("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
|
||||
}
|
||||
}
|
||||
|
||||
SessionKey newKey = null;
|
||||
if (false) // rekey
|
||||
newKey = _context.keyGenerator().generateSessionKey();
|
||||
|
@ -13,13 +13,13 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.MessageStatusMessage;
|
||||
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handle I2CP MessageStatusMessages from the router. This currently only takes
|
||||
* into account status of available, automatically prefetching them as soon as
|
||||
* possible
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
class MessageStatusMessageHandler extends HandlerImpl {
|
||||
public MessageStatusMessageHandler(I2PAppContext context) {
|
||||
@ -28,41 +28,44 @@ class MessageStatusMessageHandler extends HandlerImpl {
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
boolean skipStatus = true;
|
||||
if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions()
|
||||
.getProperty(I2PClient.PROP_RELIABILITY,
|
||||
I2PClient.PROP_RELIABILITY_BEST_EFFORT)))
|
||||
if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions().getProperty(I2PClient.PROP_RELIABILITY,
|
||||
I2PClient.PROP_RELIABILITY_BEST_EFFORT)))
|
||||
skipStatus = false;
|
||||
_log.debug("Handle message " + message);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handle message " + message);
|
||||
MessageStatusMessage msg = (MessageStatusMessage) message;
|
||||
switch (msg.getStatus()) {
|
||||
case MessageStatusMessage.STATUS_AVAILABLE:
|
||||
ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage();
|
||||
m.setMessageId(msg.getMessageId());
|
||||
m.setSessionId(msg.getSessionId());
|
||||
try {
|
||||
session.sendMessage(m);
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error asking for the message", ise);
|
||||
}
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
// noop
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
|
||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
|
||||
_log.info("Message delivery succeeded for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
|
||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
|
||||
_log.info("Message delivery FAILED for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
default:
|
||||
_log.error("Invalid message delivery status received: " + msg.getStatus());
|
||||
case MessageStatusMessage.STATUS_AVAILABLE:
|
||||
ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage();
|
||||
m.setMessageId(msg.getMessageId());
|
||||
m.setSessionId(msg.getSessionId());
|
||||
try {
|
||||
session.sendMessage(m);
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error asking for the message", ise);
|
||||
}
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
// noop
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
|
||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message delivery succeeded for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
|
||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Message delivery FAILED for message " + msg.getMessageId());
|
||||
//if (!skipStatus)
|
||||
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
|
||||
return;
|
||||
default:
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Invalid message delivery status received: " + msg.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
@ -9,8 +9,6 @@ package net.i2p.crypto;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -52,25 +50,26 @@ public class AESEngine {
|
||||
public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) {
|
||||
if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(paddedSize + 64);
|
||||
int size = Hash.HASH_LENGTH
|
||||
+ 4 // sizeof(payload)
|
||||
+ payload.length;
|
||||
int padding = ElGamalAESEngine.getPaddingSize(size, paddedSize);
|
||||
|
||||
byte data[] = new byte[size + padding];
|
||||
Hash h = _context.sha().calculateHash(iv);
|
||||
try {
|
||||
h.writeBytes(baos);
|
||||
DataHelper.writeLong(baos, 4, payload.length);
|
||||
baos.write(payload);
|
||||
byte tv[] = baos.toByteArray();
|
||||
baos.write(ElGamalAESEngine.getPadding(_context, tv.length, paddedSize));
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing data", ioe);
|
||||
return null;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error writing data", dfe);
|
||||
return null;
|
||||
}
|
||||
byte orig[] = baos.toByteArray();
|
||||
byte rv[] = new byte[orig.length];
|
||||
encrypt(orig, 0, rv, 0, sessionKey, iv, rv.length);
|
||||
return rv;
|
||||
|
||||
int cur = 0;
|
||||
System.arraycopy(h.getData(), 0, data, cur, Hash.HASH_LENGTH);
|
||||
cur += Hash.HASH_LENGTH;
|
||||
DataHelper.toLong(data, cur, 4, payload.length);
|
||||
cur += 4;
|
||||
System.arraycopy(payload, 0, data, cur, payload.length);
|
||||
cur += payload.length;
|
||||
byte paddingData[] = ElGamalAESEngine.getPadding(_context, size, paddedSize);
|
||||
System.arraycopy(paddingData, 0, data, cur, paddingData.length);
|
||||
|
||||
encrypt(data, 0, data, 0, sessionKey, iv, data.length);
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] safeDecrypt(byte payload[], SessionKey sessionKey, byte iv[]) {
|
||||
@ -82,31 +81,29 @@ public class AESEngine {
|
||||
_log.error("Error decrypting the data - payload " + payload.length + " decrypted to null");
|
||||
return null;
|
||||
}
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(decr);
|
||||
Hash h = _context.sha().calculateHash(iv);
|
||||
try {
|
||||
Hash rh = new Hash();
|
||||
rh.readBytes(bais);
|
||||
if (!h.equals(rh)) {
|
||||
|
||||
int cur = 0;
|
||||
byte h[] = _context.sha().calculateHash(iv).getData();
|
||||
for (int i = 0; i < Hash.HASH_LENGTH; i++) {
|
||||
if (decr[i] != h[i]) {
|
||||
_log.error("Hash does not match [key=" + sessionKey + " / iv =" + DataHelper.toString(iv, iv.length)
|
||||
+ "]", new Exception("Hash error"));
|
||||
return null;
|
||||
}
|
||||
long len = DataHelper.readLong(bais, 4);
|
||||
byte data[] = new byte[(int) len];
|
||||
int read = bais.read(data);
|
||||
if (read != len) {
|
||||
_log.error("Not enough to read");
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing data", ioe);
|
||||
return null;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error writing data", dfe);
|
||||
}
|
||||
cur += Hash.HASH_LENGTH;
|
||||
|
||||
long len = DataHelper.fromLong(decr, cur, 4);
|
||||
cur += 4;
|
||||
|
||||
if (cur + len > decr.length) {
|
||||
_log.error("Not enough to read");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte data[] = new byte[(int)len];
|
||||
System.arraycopy(decr, cur, data, 0, (int)len);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@ package net.i2p.crypto;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
@ -29,10 +29,12 @@ public class CryptixAESEngine extends AESEngine {
|
||||
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
|
||||
private final static boolean USE_FAKE_CRYPTO = false;
|
||||
private final static byte FAKE_KEY = 0x2A;
|
||||
private CryptixAESKeyCache _cache;
|
||||
|
||||
public CryptixAESEngine(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(CryptixAESEngine.class);
|
||||
_cache = new CryptixAESKeyCache();
|
||||
}
|
||||
|
||||
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
|
||||
@ -65,8 +67,13 @@ public class CryptixAESEngine extends AESEngine {
|
||||
|
||||
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
|
||||
if ((iv== null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
|
||||
|| (iv.length != 16))
|
||||
|| (iv.length != 16) )
|
||||
throw new IllegalArgumentException("bad setup");
|
||||
else if (out == null)
|
||||
throw new IllegalArgumentException("out is null");
|
||||
else if (out.length - outIndex < length)
|
||||
throw new IllegalArgumentException("out is too small (out.length=" + out.length
|
||||
+ " outIndex=" + outIndex + " length=" + length);
|
||||
|
||||
if (USE_FAKE_CRYPTO) {
|
||||
_log.warn("AES Crypto disabled! Using trivial XOR");
|
||||
@ -74,23 +81,26 @@ public class CryptixAESEngine extends AESEngine {
|
||||
return ;
|
||||
}
|
||||
|
||||
int numblock = payload.length / 16;
|
||||
if (payload.length % 16 != 0) numblock++;
|
||||
int numblock = length / 16;
|
||||
if (length % 16 != 0) numblock++;
|
||||
|
||||
decryptBlock(payload, 0, sessionKey, out, 0);
|
||||
DataHelper.xor(out, 0, iv, 0, out, 0, 16);
|
||||
decryptBlock(payload, payloadIndex, sessionKey, out, outIndex);
|
||||
DataHelper.xor(out, outIndex, iv, 0, out, outIndex, 16);
|
||||
for (int x = 1; x < numblock; x++) {
|
||||
decryptBlock(payload, x * 16, sessionKey, out, x * 16);
|
||||
DataHelper.xor(out, x * 16, payload, (x - 1) * 16, out, x * 16, 16);
|
||||
decryptBlock(payload, payloadIndex + (x * 16), sessionKey, out, outIndex + (x * 16));
|
||||
DataHelper.xor(out, outIndex + x * 16, payload, payloadIndex + (x - 1) * 16, out, outIndex + x * 16, 16);
|
||||
}
|
||||
}
|
||||
|
||||
final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
|
||||
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
|
||||
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, key, 16);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.error("Invalid key", ike);
|
||||
} finally {
|
||||
_cache.releaseKey(keyData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,11 +110,20 @@ public class CryptixAESEngine extends AESEngine {
|
||||
* @return unencrypted data
|
||||
*/
|
||||
final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
|
||||
if ( (payload == null) || (rv == null) )
|
||||
throw new IllegalArgumentException("null block args [payload=" + payload + " rv="+rv);
|
||||
if (payload.length - inIndex > rv.length - outIndex)
|
||||
throw new IllegalArgumentException("bad block args [payload.len=" + payload.length
|
||||
+ " inIndex=" + inIndex + " rv.len=" + rv.length
|
||||
+ " outIndex="+outIndex);
|
||||
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
|
||||
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, key, 16);
|
||||
} catch (InvalidKeyException ike) {
|
||||
_log.error("Invalid key", ike);
|
||||
} finally {
|
||||
_cache.releaseKey(keyData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +132,8 @@ public class CryptixAESEngine extends AESEngine {
|
||||
try {
|
||||
testEDBlock(ctx);
|
||||
testED(ctx);
|
||||
testFake(ctx);
|
||||
testNull(ctx);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -134,6 +155,42 @@ public class CryptixAESEngine extends AESEngine {
|
||||
else
|
||||
System.out.println("full D(E(orig)) == orig");
|
||||
}
|
||||
private static void testFake(I2PAppContext ctx) {
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
SessionKey wrongKey = ctx.keyGenerator().generateSessionKey();
|
||||
byte iv[] = new byte[16];
|
||||
byte orig[] = new byte[128];
|
||||
byte encrypted[] = new byte[128];
|
||||
byte decrypted[] = new byte[128];
|
||||
ctx.random().nextBytes(iv);
|
||||
ctx.random().nextBytes(orig);
|
||||
CryptixAESEngine aes = new CryptixAESEngine(ctx);
|
||||
aes.encrypt(orig, 0, encrypted, 0, key, iv, orig.length);
|
||||
aes.decrypt(encrypted, 0, decrypted, 0, wrongKey, iv, encrypted.length);
|
||||
if (DataHelper.eq(decrypted,orig))
|
||||
throw new RuntimeException("full D(E(orig)) == orig when we used the wrong key!");
|
||||
else
|
||||
System.out.println("full D(E(orig)) != orig when we used the wrong key");
|
||||
}
|
||||
private static void testNull(I2PAppContext ctx) {
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
SessionKey wrongKey = ctx.keyGenerator().generateSessionKey();
|
||||
byte iv[] = new byte[16];
|
||||
byte orig[] = new byte[128];
|
||||
byte encrypted[] = new byte[128];
|
||||
byte decrypted[] = new byte[128];
|
||||
ctx.random().nextBytes(iv);
|
||||
ctx.random().nextBytes(orig);
|
||||
CryptixAESEngine aes = new CryptixAESEngine(ctx);
|
||||
aes.encrypt(orig, 0, encrypted, 0, key, iv, orig.length);
|
||||
try {
|
||||
aes.decrypt(null, 0, null, 0, wrongKey, iv, encrypted.length);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException("full D(E(orig)) didn't fail when we used null!");
|
||||
}
|
||||
private static void testEDBlock(I2PAppContext ctx) {
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
byte iv[] = new byte[16];
|
||||
|
70
core/java/src/net/i2p/crypto/CryptixAESKeyCache.java
Normal file
70
core/java/src/net/i2p/crypto/CryptixAESKeyCache.java
Normal file
@ -0,0 +1,70 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cache the objects used in CryptixRijndael_Algorithm.makeKey to reduce
|
||||
* memory churn. The KeyCacheEntry should be held onto as long as the
|
||||
* data referenced in it is needed (which often is only one or two lines
|
||||
* of code)
|
||||
*
|
||||
*/
|
||||
final class CryptixAESKeyCache {
|
||||
private List _availableKeys;
|
||||
|
||||
private static final int KEYSIZE = 32; // 256bit AES
|
||||
private static final int BLOCKSIZE = 16;
|
||||
private static final int ROUNDS = CryptixRijndael_Algorithm.getRounds(KEYSIZE, BLOCKSIZE);
|
||||
private static final int BC = BLOCKSIZE / 4;
|
||||
private static final int KC = KEYSIZE / 4;
|
||||
|
||||
public CryptixAESKeyCache() {
|
||||
_availableKeys = new ArrayList(64);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
_availableKeys.add(createNew());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next available structure, either from the cache or a brand new one
|
||||
*
|
||||
*/
|
||||
public final KeyCacheEntry acquireKey() {
|
||||
synchronized (_availableKeys) {
|
||||
if (_availableKeys.size() > 0)
|
||||
return (KeyCacheEntry)_availableKeys.remove(0);
|
||||
}
|
||||
return createNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put this structure back onto the available cache for reuse
|
||||
*
|
||||
*/
|
||||
public final void releaseKey(KeyCacheEntry key) {
|
||||
synchronized (_availableKeys) {
|
||||
_availableKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static final KeyCacheEntry createNew() {
|
||||
KeyCacheEntry e = new KeyCacheEntry();
|
||||
e.Ke = new int[ROUNDS + 1][BC]; // encryption round keys
|
||||
e.Kd = new int[ROUNDS + 1][BC]; // decryption round keys
|
||||
e.tk = new int[KC];
|
||||
e.key = new Object[] { e.Ke, e.Kd };
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* all the data alloc'ed in a makeKey call
|
||||
*/
|
||||
public static final class KeyCacheEntry {
|
||||
int[][] Ke;
|
||||
int[][] Kd;
|
||||
int[] tk;
|
||||
|
||||
Object[] key;
|
||||
}
|
||||
}
|
@ -453,6 +453,11 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
* @param sessionKey The session key to use for decryption.
|
||||
*/
|
||||
public static final void blockDecrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey) {
|
||||
if (in.length - inOffset > result.length - outOffset)
|
||||
throw new IllegalArgumentException("result too small: in.len=" + in.length + " in.offset=" + inOffset
|
||||
+ " result.len=" + result.length + " result.offset=" + outOffset);
|
||||
if (in.length - inOffset <= 15)
|
||||
throw new IllegalArgumentException("data too small: " + in.length + " inOffset: " + inOffset);
|
||||
if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
|
||||
int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
|
||||
int ROUNDS = Kd.length - 1;
|
||||
@ -534,18 +539,31 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
* @exception InvalidKeyException If the key is invalid.
|
||||
*/
|
||||
public static final/* synchronized */Object makeKey(byte[] k, int blockSize) throws InvalidKeyException {
|
||||
return makeKey(k, blockSize, null);
|
||||
}
|
||||
public static final/* synchronized */Object makeKey(byte[] k, int blockSize, CryptixAESKeyCache.KeyCacheEntry keyData) throws InvalidKeyException {
|
||||
if (_RDEBUG) trace(_IN, "makeKey(" + k + ", " + blockSize + ")");
|
||||
if (k == null) throw new InvalidKeyException("Empty key");
|
||||
if (!(k.length == 16 || k.length == 24 || k.length == 32))
|
||||
throw new InvalidKeyException("Incorrect key length");
|
||||
int ROUNDS = getRounds(k.length, blockSize);
|
||||
int BC = blockSize / 4;
|
||||
int[][] Ke = new int[ROUNDS + 1][BC]; // encryption round keys
|
||||
int[][] Kd = new int[ROUNDS + 1][BC]; // decryption round keys
|
||||
int[][] Ke = null; // new int[ROUNDS + 1][BC]; // encryption round keys
|
||||
int[][] Kd = null; // new int[ROUNDS + 1][BC]; // decryption round keys
|
||||
int ROUND_KEY_COUNT = (ROUNDS + 1) * BC;
|
||||
int KC = k.length / 4;
|
||||
int[] tk = new int[KC];
|
||||
int[] tk = null; // new int[KC];
|
||||
int i, j;
|
||||
|
||||
if (keyData == null) {
|
||||
Ke = new int[ROUNDS + 1][BC];
|
||||
Kd = new int[ROUNDS + 1][BC];
|
||||
tk = new int[KC];
|
||||
} else {
|
||||
Ke = keyData.Ke;
|
||||
Kd = keyData.Kd;
|
||||
tk = keyData.tk;
|
||||
}
|
||||
|
||||
// copy user material bytes into temporary ints
|
||||
for (i = 0, j = 0; i < KC;)
|
||||
@ -604,7 +622,11 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
|
||||
}
|
||||
// assemble the encryption (Ke) and decryption (Kd) round keys into
|
||||
// one sessionKey object
|
||||
Object[] sessionKey = new Object[] { Ke, Kd};
|
||||
Object[] sessionKey = null;
|
||||
if (keyData == null)
|
||||
sessionKey = new Object[] { Ke, Kd};
|
||||
else
|
||||
sessionKey = keyData.key;
|
||||
if (_RDEBUG) trace(_OUT, "makeKey()");
|
||||
return sessionKey;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ package net.i2p.crypto;
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@ -147,25 +146,19 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(data, 0, elgEncr, 514 - data.length, data.length);
|
||||
}
|
||||
byte elgDecr[] = _context.elGamalEngine().decrypt(elgEncr, targetPrivateKey);
|
||||
if (elgDecr == null) return null;
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(elgDecr);
|
||||
byte preIV[] = null;
|
||||
|
||||
try {
|
||||
usedKey.readBytes(bais);
|
||||
preIV = new byte[32];
|
||||
int read = bais.read(preIV);
|
||||
if (read != preIV.length) {
|
||||
// hmm, this can't really happen...
|
||||
throw new DataFormatException("Somehow ElGamal broke and 256 bytes is less than 32 bytes..."); }
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR)) _log.error("Error decrypting the new session", ioe);
|
||||
if (elgDecr == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("decrypt returned null", new Exception("decrypt failed"));
|
||||
return null;
|
||||
}
|
||||
// ignore the next 192 bytes
|
||||
byte aesEncr[] = new byte[data.length - 514];
|
||||
System.arraycopy(data, 514, aesEncr, 0, aesEncr.length);
|
||||
|
||||
byte preIV[] = null;
|
||||
|
||||
byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
|
||||
System.arraycopy(elgDecr, 0, key, 0, SessionKey.KEYSIZE_BYTES);
|
||||
usedKey.setData(key);
|
||||
preIV = new byte[32];
|
||||
System.arraycopy(elgDecr, SessionKey.KEYSIZE_BYTES, preIV, 0, 32);
|
||||
|
||||
//_log.debug("Pre IV for decryptNewSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
@ -173,7 +166,7 @@ public class ElGamalAESEngine {
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
byte aesDecr[] = decryptAESBlock(aesEncr, usedKey, iv, null, foundTags, foundKey);
|
||||
byte aesDecr[] = decryptAESBlock(data, 514, data.length-514, usedKey, iv, null, foundTags, foundKey);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
|
||||
@ -203,8 +196,6 @@ public class ElGamalAESEngine {
|
||||
SessionKey usedKey, SessionKey foundKey) throws DataFormatException {
|
||||
byte preIV[] = new byte[32];
|
||||
System.arraycopy(data, 0, preIV, 0, preIV.length);
|
||||
byte encr[] = new byte[data.length - 32];
|
||||
System.arraycopy(data, 32, encr, 0, encr.length);
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
@ -213,7 +204,7 @@ public class ElGamalAESEngine {
|
||||
|
||||
//_log.debug("Pre IV for decryptExistingSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
byte decrypted[] = decryptAESBlock(encr, key, iv, preIV, foundTags, foundKey);
|
||||
byte decrypted[] = decryptAESBlock(data, 32, data.length-32, key, iv, preIV, foundTags, foundKey);
|
||||
if (decrypted == null) {
|
||||
// it begins with a valid session tag, but thats just a coincidence.
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -244,12 +235,16 @@ public class ElGamalAESEngine {
|
||||
* @param foundTags set which is filled with any sessionTags found during decryption
|
||||
* @param foundKey session key which may be filled with a new sessionKey found during decryption
|
||||
*/
|
||||
byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte sentTag[], Set foundTags,
|
||||
SessionKey foundKey) throws DataFormatException {
|
||||
byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[],
|
||||
byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException {
|
||||
return decryptAESBlock(encrypted, 0, encrypted.length, key, iv, sentTag, foundTags, foundKey);
|
||||
}
|
||||
byte[] decryptAESBlock(byte encrypted[], int offset, int encryptedLen, SessionKey key, byte iv[],
|
||||
byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException {
|
||||
//_log.debug("iv for decryption: " + DataHelper.toString(iv, 16));
|
||||
//_log.debug("decrypting AES block. encr.length = " + (encrypted == null? -1 : encrypted.length) + " sentTag: " + DataHelper.toString(sentTag, 32));
|
||||
byte decrypted[] = new byte[encrypted.length];
|
||||
_context.aes().decrypt(encrypted, 0, decrypted, 0, key, iv, encrypted.length);
|
||||
byte decrypted[] = new byte[encryptedLen];
|
||||
_context.aes().decrypt(encrypted, offset, decrypted, 0, key, iv, encryptedLen);
|
||||
//Hash h = _context.sha().calculateHash(decrypted);
|
||||
//_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
|
||||
try {
|
||||
@ -271,7 +266,7 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
long len = DataHelper.readLong(bais, 4);
|
||||
//_log.debug("len: " + len);
|
||||
if ((len < 0) || (len > encrypted.length)) throw new Exception("Invalid size of payload");
|
||||
if ((len < 0) || (len > encryptedLen)) throw new Exception("Invalid size of payload");
|
||||
byte hashval[] = new byte[32];
|
||||
int read = bais.read(hashval);
|
||||
if (read != hashval.length) throw new Exception("Invalid size of hash");
|
||||
@ -371,54 +366,45 @@ public class ElGamalAESEngine {
|
||||
byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionKey newKey, long paddedSize) {
|
||||
//_log.debug("Encrypting to a NEW session");
|
||||
try {
|
||||
ByteArrayOutputStream elgSrc = new ByteArrayOutputStream(64);
|
||||
key.writeBytes(elgSrc);
|
||||
byte preIV[] = new byte[32];
|
||||
_context.random().nextBytes(preIV);
|
||||
elgSrc.write(preIV);
|
||||
byte rnd[] = new byte[158];
|
||||
_context.random().nextBytes(rnd);
|
||||
elgSrc.write(rnd);
|
||||
elgSrc.flush();
|
||||
byte elgSrcData[] = new byte[SessionKey.KEYSIZE_BYTES+32+158];
|
||||
System.arraycopy(key.getData(), 0, elgSrcData, 0, SessionKey.KEYSIZE_BYTES);
|
||||
byte preIV[] = new byte[32];
|
||||
_context.random().nextBytes(preIV);
|
||||
System.arraycopy(preIV, 0, elgSrcData, SessionKey.KEYSIZE_BYTES, 32);
|
||||
byte rnd[] = new byte[158];
|
||||
_context.random().nextBytes(rnd);
|
||||
System.arraycopy(rnd, 0, elgSrcData, SessionKey.KEYSIZE_BYTES+32, 158);
|
||||
|
||||
//_log.debug("Pre IV for encryptNewSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
long before = _context.clock().now();
|
||||
byte elgEncr[] = _context.elGamalEngine().encrypt(elgSrc.toByteArray(), target);
|
||||
long after = _context.clock().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms");
|
||||
if (elgEncr.length < 514) {
|
||||
byte elg[] = new byte[514];
|
||||
int diff = elg.length - elgEncr.length;
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
|
||||
elgEncr = elg;
|
||||
}
|
||||
//_log.debug("ElGamal encrypted length: " + elgEncr.length + " elGamal source length: " + elgSrc.toByteArray().length);
|
||||
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
|
||||
//_log.debug("AES encrypted length: " + aesEncr.length);
|
||||
|
||||
byte rv[] = new byte[elgEncr.length + aesEncr.length];
|
||||
System.arraycopy(elgEncr, 0, rv, 0, elgEncr.length);
|
||||
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
|
||||
//_log.debug("Return length: " + rv.length);
|
||||
long finish = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error encrypting the new session", ioe);
|
||||
return null;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error writing out the bytes for the new session", dfe);
|
||||
return null;
|
||||
//_log.debug("Pre IV for encryptNewSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
long before = _context.clock().now();
|
||||
byte elgEncr[] = _context.elGamalEngine().encrypt(elgSrcData, target);
|
||||
long after = _context.clock().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms");
|
||||
if (elgEncr.length < 514) {
|
||||
byte elg[] = new byte[514];
|
||||
int diff = elg.length - elgEncr.length;
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
|
||||
elgEncr = elg;
|
||||
}
|
||||
//_log.debug("ElGamal encrypted length: " + elgEncr.length + " elGamal source length: " + elgSrc.toByteArray().length);
|
||||
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
|
||||
//_log.debug("AES encrypted length: " + aesEncr.length);
|
||||
|
||||
byte rv[] = new byte[elgEncr.length + aesEncr.length];
|
||||
System.arraycopy(elgEncr, 0, rv, 0, elgEncr.length);
|
||||
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
|
||||
//_log.debug("Return length: " + rv.length);
|
||||
long finish = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,11 +431,10 @@ public class ElGamalAESEngine {
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
|
||||
byte rv[] = new byte[rawTag.length + aesEncr.length];
|
||||
System.arraycopy(rawTag, 0, rv, 0, rawTag.length);
|
||||
System.arraycopy(aesEncr, 0, rv, rawTag.length, aesEncr.length);
|
||||
return rv;
|
||||
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize, SessionTag.BYTE_LENGTH);
|
||||
// that prepended SessionTag.BYTE_LENGTH bytes at the beginning of the buffer
|
||||
System.arraycopy(rawTag, 0, aesEncr, 0, rawTag.length);
|
||||
return aesEncr;
|
||||
}
|
||||
|
||||
private final static Set EMPTY_SET = new HashSet();
|
||||
@ -469,52 +454,64 @@ public class ElGamalAESEngine {
|
||||
*/
|
||||
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
long paddedSize) {
|
||||
return encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize, 0);
|
||||
}
|
||||
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
long paddedSize, int prefixBytes) {
|
||||
//_log.debug("iv for encryption: " + DataHelper.toString(iv, 16));
|
||||
//_log.debug("Encrypting AES");
|
||||
try {
|
||||
ByteArrayOutputStream aesSrc = new ByteArrayOutputStream((int) paddedSize);
|
||||
if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET;
|
||||
DataHelper.writeLong(aesSrc, 2, tagsForDelivery.size());
|
||||
for (Iterator iter = tagsForDelivery.iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
aesSrc.write(tag.getData());
|
||||
}
|
||||
//_log.debug("# tags created, registered, and written: " + tags.size());
|
||||
DataHelper.writeLong(aesSrc, 4, data.length);
|
||||
//_log.debug("data length: " + data.length);
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
hash.writeBytes(aesSrc);
|
||||
//_log.debug("hash of data: " + DataHelper.toString(hash.getData(), 32));
|
||||
if (newKey == null) {
|
||||
byte flag = 0x00; // don't rekey
|
||||
aesSrc.write(flag);
|
||||
//_log.debug("flag written");
|
||||
} else {
|
||||
byte flag = 0x01; // rekey
|
||||
aesSrc.write(flag);
|
||||
aesSrc.write(newKey.getData());
|
||||
}
|
||||
aesSrc.write(data);
|
||||
int len = aesSrc.toByteArray().length;
|
||||
//_log.debug("raw data written: " + len);
|
||||
byte padding[] = getPadding(_context, len, paddedSize);
|
||||
//_log.debug("padding length: " + padding.length);
|
||||
aesSrc.write(padding);
|
||||
if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET;
|
||||
int size = 2 // sizeof(tags)
|
||||
+ tagsForDelivery.size()
|
||||
+ SessionTag.BYTE_LENGTH*tagsForDelivery.size()
|
||||
+ 4 // payload length
|
||||
+ Hash.HASH_LENGTH
|
||||
+ (newKey == null ? 1 : 1 + SessionKey.KEYSIZE_BYTES)
|
||||
+ data.length;
|
||||
int totalSize = size + getPaddingSize(size, paddedSize);
|
||||
|
||||
byte aesUnencr[] = aesSrc.toByteArray();
|
||||
//Hash h = _context.sha().calculateHash(aesUnencr);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
byte aesEncr[] = new byte[aesUnencr.length];
|
||||
_context.aes().encrypt(aesUnencr, 0, aesEncr, 0, key, iv, aesEncr.length);
|
||||
//_log.debug("Encrypted length: " + aesEncr.length);
|
||||
return aesEncr;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR)) _log.error("Error encrypting AES chunk", ioe);
|
||||
return null;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.ERROR)) _log.error("Error formatting the bytes to write the AES chunk", dfe);
|
||||
return null;
|
||||
byte aesData[] = new byte[totalSize + prefixBytes];
|
||||
|
||||
int cur = prefixBytes;
|
||||
DataHelper.toLong(aesData, cur, 2, tagsForDelivery.size());
|
||||
cur += 2;
|
||||
for (Iterator iter = tagsForDelivery.iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
System.arraycopy(tag.getData(), 0, aesData, cur, SessionTag.BYTE_LENGTH);
|
||||
cur += SessionTag.BYTE_LENGTH;
|
||||
}
|
||||
//_log.debug("# tags created, registered, and written: " + tags.size());
|
||||
DataHelper.toLong(aesData, cur, 4, data.length);
|
||||
cur += 4;
|
||||
//_log.debug("data length: " + data.length);
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
System.arraycopy(hash.getData(), 0, aesData, cur, Hash.HASH_LENGTH);
|
||||
cur += Hash.HASH_LENGTH;
|
||||
|
||||
//_log.debug("hash of data: " + DataHelper.toString(hash.getData(), 32));
|
||||
if (newKey == null) {
|
||||
aesData[cur++] = 0x00; // don't rekey
|
||||
//_log.debug("flag written");
|
||||
} else {
|
||||
aesData[cur++] = 0x01; // rekey
|
||||
System.arraycopy(newKey.getData(), 0, aesData, cur, SessionKey.KEYSIZE_BYTES);
|
||||
cur += SessionKey.KEYSIZE_BYTES;
|
||||
}
|
||||
System.arraycopy(data, 0, aesData, cur, data.length);
|
||||
cur += data.length;
|
||||
|
||||
//_log.debug("raw data written: " + len);
|
||||
byte padding[] = getPadding(_context, size, paddedSize);
|
||||
//_log.debug("padding length: " + padding.length);
|
||||
System.arraycopy(padding, 0, aesData, cur, padding.length);
|
||||
cur += padding.length;
|
||||
|
||||
//Hash h = _context.sha().calculateHash(aesUnencr);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
_context.aes().encrypt(aesData, prefixBytes, aesData, prefixBytes, key, iv, aesData.length - prefixBytes);
|
||||
//_log.debug("Encrypted length: " + aesEncr.length);
|
||||
//return aesEncr;
|
||||
return aesData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -523,6 +520,12 @@ public class ElGamalAESEngine {
|
||||
*
|
||||
*/
|
||||
final static byte[] getPadding(I2PAppContext context, int curSize, long minPaddedSize) {
|
||||
int size = getPaddingSize(curSize, minPaddedSize);
|
||||
byte rv[] = new byte[size];
|
||||
context.random().nextBytes(rv);
|
||||
return rv;
|
||||
}
|
||||
final static int getPaddingSize(int curSize, long minPaddedSize) {
|
||||
int diff = 0;
|
||||
if (curSize < minPaddedSize) {
|
||||
diff = (int) minPaddedSize - curSize;
|
||||
@ -530,9 +533,7 @@ public class ElGamalAESEngine {
|
||||
|
||||
int numPadding = diff;
|
||||
if (((curSize + diff) % 16) != 0) numPadding += (16 - ((curSize + diff) % 16));
|
||||
byte rv[] = new byte[numPadding];
|
||||
context.random().nextBytes(rv);
|
||||
return rv;
|
||||
return numPadding;
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,6 @@ package net.i2p.crypto;
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -98,19 +97,12 @@ public class ElGamalEngine {
|
||||
|
||||
long start = _context.clock().now();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
|
||||
try {
|
||||
baos.write(0xFF);
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
hash.writeBytes(baos);
|
||||
baos.write(data);
|
||||
baos.flush();
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.ERROR)) _log.error("Internal error writing to buffer", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte d2[] = baos.toByteArray();
|
||||
byte d2[] = new byte[1+Hash.HASH_LENGTH+data.length];
|
||||
d2[0] = (byte)0xFF;
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
System.arraycopy(hash.getData(), 0, d2, 1, Hash.HASH_LENGTH);
|
||||
System.arraycopy(data, 0, d2, 1+Hash.HASH_LENGTH, data.length);
|
||||
|
||||
long t0 = _context.clock().now();
|
||||
BigInteger m = new NativeBigInteger(1, d2);
|
||||
long t1 = _context.clock().now();
|
||||
|
@ -81,6 +81,13 @@ public class Payload extends DataStructureImpl {
|
||||
out.write(_encryptedData);
|
||||
_log.debug("wrote payload: " + _encryptedData.length);
|
||||
}
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data");
|
||||
DataHelper.toLong(target, offset, 4, _encryptedData.length);
|
||||
offset += 4;
|
||||
System.arraycopy(_encryptedData, 0, target, offset, _encryptedData.length);
|
||||
return 4 + _encryptedData.length;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ((object == null) || !(object instanceof Payload)) return false;
|
||||
@ -94,6 +101,7 @@ public class Payload extends DataStructureImpl {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (true) return "[Payload]";
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("[Payload: ");
|
||||
if (getUnencryptedData() != null)
|
||||
|
@ -12,11 +12,13 @@ package net.i2p.data.i2cp;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Payload;
|
||||
import net.i2p.data.i2cp.I2CPMessageException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -87,20 +89,31 @@ public class SendMessageMessage extends I2CPMessageImpl {
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if ((_sessionId == null) || (_destination == null) || (_payload == null) || (_nonce <= 0))
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(512);
|
||||
try {
|
||||
_sessionId.writeBytes(os);
|
||||
_destination.writeBytes(os);
|
||||
_payload.writeBytes(os);
|
||||
DataHelper.writeLong(os, 4, _nonce);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
throw new RuntimeException("wtf, dont run me");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the full message to the stream, including the 4 byte size and 1
|
||||
* byte type header. Override the parent so we can be more mem efficient
|
||||
*
|
||||
*/
|
||||
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
|
||||
if ((_sessionId == null) || (_destination == null) || (_payload == null) || (_nonce <= 0))
|
||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||
int len = 2 + _destination.size() + _payload.getSize() + 4 + 4;
|
||||
|
||||
try {
|
||||
DataHelper.writeLong(out, 4, len);
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
_sessionId.writeBytes(out);
|
||||
_destination.writeBytes(out);
|
||||
_payload.writeBytes(out);
|
||||
DataHelper.writeLong(out, 4, _nonce);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2CPMessageException("Error writing the msg", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return MESSAGE_TYPE;
|
||||
}
|
||||
|
Reference in New Issue
Block a user