Move some data structures away from ByteArray; offsets were always zero
  - New BuildRequestRecord constructors
  - BuildRequestRecord field becomes final byte[222]
  - IV becomes byte[16]
  - Build record becomes EncryptedBuildRecord
Remove extra copy in BuildRequestRecord.encryptRecord()
Remove unused BuildRequestRecord.readOurIdentityMatches()
This commit is contained in:
zzz
2014-11-15 17:48:11 +00:00
parent 23f24c7d39
commit 5044f3e58f
12 changed files with 200 additions and 122 deletions

View File

@@ -2,6 +2,7 @@ package net.i2p.data.i2np;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray; import net.i2p.data.ByteArray;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
@@ -9,7 +10,8 @@ import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
/** /**
* Hold the tunnel request record, managing its ElGamal encryption and decryption. * Holds the unencrypted 222-byte tunnel request record,
* with a constructor for ElGamal decryption and a method for ElGamal encryption.
* Iterative AES encryption/decryption is done elsewhere. * Iterative AES encryption/decryption is done elsewhere.
* *
* Cleartext: * Cleartext:
@@ -36,7 +38,7 @@ import net.i2p.data.SessionKey;
* *
*/ */
public class BuildRequestRecord { public class BuildRequestRecord {
private ByteArray _data; private final byte[] _data;
/** /**
* If set in the flag byte, any peer may send a message into this tunnel, but if * If set in the flag byte, any peer may send a message into this tunnel, but if
@@ -55,11 +57,10 @@ public class BuildRequestRecord {
/** we show 16 bytes of the peer hash outside the elGamal block */ /** we show 16 bytes of the peer hash outside the elGamal block */
public static final int PEER_SIZE = 16; public static final int PEER_SIZE = 16;
public BuildRequestRecord(ByteArray data) { _data = data; } /**
public BuildRequestRecord() { } * @return 222 bytes, non-null
*/
public ByteArray getData() { return _data; } public byte[] getData() { return _data; }
public void setData(ByteArray data) { _data = data; }
private static final int OFF_RECV_TUNNEL = 0; private static final int OFF_RECV_TUNNEL = 0;
private static final int OFF_OUR_IDENT = OFF_RECV_TUNNEL + 4; private static final int OFF_OUR_IDENT = OFF_RECV_TUNNEL + 4;
@@ -72,91 +73,101 @@ public class BuildRequestRecord {
private static final int OFF_FLAG = OFF_REPLY_IV + IV_SIZE; private static final int OFF_FLAG = OFF_REPLY_IV + IV_SIZE;
private static final int OFF_REQ_TIME = OFF_FLAG + 1; private static final int OFF_REQ_TIME = OFF_FLAG + 1;
private static final int OFF_SEND_MSG_ID = OFF_REQ_TIME + 4; private static final int OFF_SEND_MSG_ID = OFF_REQ_TIME + 4;
private static final int PADDING_SIZE = 29;
// 222
private static final int LENGTH = OFF_SEND_MSG_ID + 4 + PADDING_SIZE;
/** what tunnel ID should this receive messages on */ /** what tunnel ID should this receive messages on */
public long readReceiveTunnelId() { public long readReceiveTunnelId() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_RECV_TUNNEL, 4); return DataHelper.fromLong(_data, OFF_RECV_TUNNEL, 4);
}
/** true if the identity they expect us to be is who we are */
public boolean readOurIdentityMatches(Hash ourIdentity) {
return DataHelper.eq(ourIdentity.getData(), 0, _data.getData(), _data.getOffset() + OFF_OUR_IDENT, Hash.HASH_LENGTH);
} }
/** /**
* What tunnel ID the next hop receives messages on. If this is the outbound tunnel endpoint, * What tunnel ID the next hop receives messages on. If this is the outbound tunnel endpoint,
* this specifies the tunnel ID to which the reply should be sent. * this specifies the tunnel ID to which the reply should be sent.
*/ */
public long readNextTunnelId() { public long readNextTunnelId() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_SEND_TUNNEL, 4); return DataHelper.fromLong(_data, OFF_SEND_TUNNEL, 4);
} }
/** /**
* Read the next hop from the record. If this is the outbound tunnel endpoint, this specifies * Read the next hop from the record. If this is the outbound tunnel endpoint, this specifies
* the gateway to which the reply should be sent. * the gateway to which the reply should be sent.
*/ */
public Hash readNextIdentity() { public Hash readNextIdentity() {
//byte rv[] = new byte[Hash.HASH_LENGTH]; //byte rv[] = new byte[Hash.HASH_LENGTH];
//System.arraycopy(_data.getData(), _data.getOffset() + OFF_SEND_IDENT, rv, 0, Hash.HASH_LENGTH); //System.arraycopy(_data, OFF_SEND_IDENT, rv, 0, Hash.HASH_LENGTH);
//return new Hash(rv); //return new Hash(rv);
return Hash.create(_data.getData(), _data.getOffset() + OFF_SEND_IDENT); return Hash.create(_data, OFF_SEND_IDENT);
} }
/** /**
* Tunnel layer encryption key that the current hop should use * Tunnel layer encryption key that the current hop should use
*/ */
public SessionKey readLayerKey() { public SessionKey readLayerKey() {
byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(_data.getData(), _data.getOffset() + OFF_LAYER_KEY, key, 0, SessionKey.KEYSIZE_BYTES); System.arraycopy(_data, OFF_LAYER_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
return new SessionKey(key); return new SessionKey(key);
} }
/** /**
* Tunnel IV encryption key that the current hop should use * Tunnel IV encryption key that the current hop should use
*/ */
public SessionKey readIVKey() { public SessionKey readIVKey() {
byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(_data.getData(), _data.getOffset() + OFF_IV_KEY, key, 0, SessionKey.KEYSIZE_BYTES); System.arraycopy(_data, OFF_IV_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
return new SessionKey(key); return new SessionKey(key);
} }
/** /**
* Session key that should be used to encrypt the reply * Session key that should be used to encrypt the reply
*/ */
public SessionKey readReplyKey() { public SessionKey readReplyKey() {
byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(_data.getData(), _data.getOffset() + OFF_REPLY_KEY, key, 0, SessionKey.KEYSIZE_BYTES); System.arraycopy(_data, OFF_REPLY_KEY, key, 0, SessionKey.KEYSIZE_BYTES);
return new SessionKey(key); return new SessionKey(key);
} }
/** /**
* IV that should be used to encrypt the reply * IV that should be used to encrypt the reply
*/ */
public byte[] readReplyIV() { public byte[] readReplyIV() {
byte iv[] = new byte[IV_SIZE]; byte iv[] = new byte[IV_SIZE];
System.arraycopy(_data.getData(), _data.getOffset() + OFF_REPLY_IV, iv, 0, IV_SIZE); System.arraycopy(_data, OFF_REPLY_IV, iv, 0, IV_SIZE);
return iv; return iv;
} }
/** /**
* The current hop is the inbound gateway. If this is true, it means anyone can send messages to * The current hop is the inbound gateway. If this is true, it means anyone can send messages to
* this tunnel, but if it is false, only the current predecessor can. * this tunnel, but if it is false, only the current predecessor can.
* *
*/ */
public boolean readIsInboundGateway() { public boolean readIsInboundGateway() {
return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_UNRESTRICTED_PREV) != 0; return (_data[OFF_FLAG] & FLAG_UNRESTRICTED_PREV) != 0;
} }
/** /**
* The current hop is the outbound endpoint. If this is true, the next identity and next tunnel * The current hop is the outbound endpoint. If this is true, the next identity and next tunnel
* fields refer to where the reply should be sent. * fields refer to where the reply should be sent.
*/ */
public boolean readIsOutboundEndpoint() { public boolean readIsOutboundEndpoint() {
return (_data.getData()[_data.getOffset() + OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0; return (_data[OFF_FLAG] & FLAG_OUTBOUND_ENDPOINT) != 0;
} }
/** /**
* Time that the request was sent (ms), truncated to the nearest hour * Time that the request was sent (ms), truncated to the nearest hour
*/ */
public long readRequestTime() { public long readRequestTime() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * (60 * 60 * 1000L); return DataHelper.fromLong(_data, OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
} }
/** /**
* What message ID should we send the request to the next hop with. If this is the outbound tunnel endpoint, * What message ID should we send the request to the next hop with. If this is the outbound tunnel endpoint,
* this specifies the message ID with which the reply should be sent. * this specifies the message ID with which the reply should be sent.
*/ */
public long readReplyMessageId() { public long readReplyMessageId() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_SEND_MSG_ID, 4); return DataHelper.fromLong(_data, OFF_SEND_MSG_ID, 4);
} }
/** /**
@@ -164,42 +175,43 @@ public class BuildRequestRecord {
* bytes 0-15: truncated SHA-256 of the current hop's identity (the toPeer parameter) * bytes 0-15: truncated SHA-256 of the current hop's identity (the toPeer parameter)
* bytes 15-527: ElGamal-2048 encrypted block * bytes 15-527: ElGamal-2048 encrypted block
* </pre> * </pre>
*
* @return non-null
*/ */
public void encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer, byte out[], int outOffset) { public EncryptedBuildRecord encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer) {
System.arraycopy(toPeer.getData(), 0, out, outOffset, PEER_SIZE); byte[] out = new byte[EncryptedBuildRecord.LENGTH];
byte preEncr[] = new byte[OFF_SEND_MSG_ID + 4 + PADDING_SIZE]; System.arraycopy(toPeer.getData(), 0, out, 0, PEER_SIZE);
System.arraycopy(_data.getData(), _data.getOffset(), preEncr, 0, preEncr.length); byte encrypted[] = ctx.elGamalEngine().encrypt(_data, toKey);
byte encrypted[] = ctx.elGamalEngine().encrypt(preEncr, toKey);
// the elg engine formats it kind of weird, giving 257 bytes for each part rather than 256, so // the elg engine formats it kind of weird, giving 257 bytes for each part rather than 256, so
// we want to strip out that excess byte and store it in the record // we want to strip out that excess byte and store it in the record
System.arraycopy(encrypted, 1, out, outOffset + PEER_SIZE, 256); System.arraycopy(encrypted, 1, out, PEER_SIZE, 256);
System.arraycopy(encrypted, 258, out, outOffset + 256 + PEER_SIZE, 256); System.arraycopy(encrypted, 258, out, 256 + PEER_SIZE, 256);
return new EncryptedBuildRecord(out);
} }
/** /**
* Decrypt the data from the specified record, writing the decrypted record into this instance's * Decrypt the data from the specified record, writing the decrypted record into this instance's
* buffer (but not overwriting the array contained within the old buffer) * data buffer
*
* Caller MUST check that first 16 bytes of our hash matches first 16 bytes of encryptedRecord
* before calling this. Not checked here.
*
* @throws DataFormatException on decrypt fail
* @since 0.9.18, was decryptRecord()
*/ */
public boolean decryptRecord(I2PAppContext ctx, PrivateKey ourKey, Hash ourIdent, ByteArray encryptedRecord) { public BuildRequestRecord(I2PAppContext ctx, PrivateKey ourKey,
if (DataHelper.eq(ourIdent.getData(), 0, encryptedRecord.getData(), encryptedRecord.getOffset(), PEER_SIZE)) { EncryptedBuildRecord encryptedRecord) throws DataFormatException {
byte preDecrypt[] = new byte[514]; byte preDecrypt[] = new byte[514];
System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE, preDecrypt, 1, 256); System.arraycopy(encryptedRecord.getData(), PEER_SIZE, preDecrypt, 1, 256);
System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE + 256, preDecrypt, 258, 256); System.arraycopy(encryptedRecord.getData(), PEER_SIZE + 256, preDecrypt, 258, 256);
byte decrypted[] = ctx.elGamalEngine().decrypt(preDecrypt, ourKey); byte decrypted[] = ctx.elGamalEngine().decrypt(preDecrypt, ourKey);
if (decrypted != null) { if (decrypted != null) {
_data = new ByteArray(decrypted); _data = decrypted;
_data.setOffset(0);
return true;
} else { } else {
return false; throw new DataFormatException("decrypt fail");
} }
} else {
return false;
}
} }
private static final int PADDING_SIZE = 29;
/** /**
* Populate this instance with data. A new buffer is created to contain the data, with the * Populate this instance with data. A new buffer is created to contain the data, with the
* necessary randomized padding. * necessary randomized padding.
@@ -215,14 +227,13 @@ public class BuildRequestRecord {
* @param iv iv to be used when encrypting the reply to this build request * @param iv iv to be used when encrypting the reply to this build request
* @param isInGateway are we the gateway of an inbound tunnel? * @param isInGateway are we the gateway of an inbound tunnel?
* @param isOutEndpoint are we the endpoint of an outbound tunnel? * @param isOutEndpoint are we the endpoint of an outbound tunnel?
* @since 0.9.18, was createRecord()
*/ */
public void createRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop, long nextMsgId, public BuildRequestRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop, long nextMsgId,
SessionKey layerKey, SessionKey ivKey, SessionKey replyKey, byte iv[], boolean isInGateway, SessionKey layerKey, SessionKey ivKey, SessionKey replyKey, byte iv[], boolean isInGateway,
boolean isOutEndpoint) { boolean isOutEndpoint) {
if ( (_data == null) || (_data.getData() != null) ) byte buf[] = new byte[LENGTH];
_data = new ByteArray(); _data = buf;
byte buf[] = new byte[OFF_SEND_MSG_ID+4+PADDING_SIZE];
_data.setData(buf);
/* bytes 0-3: tunnel ID to receive messages as /* bytes 0-3: tunnel ID to receive messages as
* bytes 4-35: local router identity hash * bytes 4-35: local router identity hash

View File

@@ -7,12 +7,17 @@ import net.i2p.data.SessionKey;
//import net.i2p.util.Log; //import net.i2p.util.Log;
/** /**
* Read and write the reply to a tunnel build message record. * Class that creates an encrypted tunnel build message record.
* *
* The reply record is the same size as the request record (528 bytes). * The reply record is the same size as the request record (528 bytes).
*
* When decrypted:
*
*<pre>
* Bytes 0-31 contain the hash of bytes 32-527 * Bytes 0-31 contain the hash of bytes 32-527
* Bytes 32-526 contain random data. * Bytes 32-526 contain random data.
* Byte 527 contains the reply. * Byte 527 contains the reply.
*</pre>
*/ */
public class BuildResponseRecord { public class BuildResponseRecord {
@@ -20,10 +25,12 @@ public class BuildResponseRecord {
* Create a new encrypted response * Create a new encrypted response
* *
* @param status the response 0-255 * @param status the response 0-255
* @param replyIV 16 bytes
* @param responseMessageId unused except for debugging * @param responseMessageId unused except for debugging
* @return a 528-byte response record * @return a 528-byte response record
*/ */
public static byte[] create(I2PAppContext ctx, int status, SessionKey replyKey, byte replyIV[], long responseMessageId) { public static EncryptedBuildRecord create(I2PAppContext ctx, int status, SessionKey replyKey,
byte replyIV[], long responseMessageId) {
//Log log = ctx.logManager().getLog(BuildResponseRecord.class); //Log log = ctx.logManager().getLog(BuildResponseRecord.class);
byte rv[] = new byte[TunnelBuildReplyMessage.RECORD_SIZE]; byte rv[] = new byte[TunnelBuildReplyMessage.RECORD_SIZE];
ctx.random().nextBytes(rv, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE - Hash.HASH_LENGTH - 1); ctx.random().nextBytes(rv, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE - Hash.HASH_LENGTH - 1);
@@ -35,6 +42,6 @@ public class BuildResponseRecord {
ctx.aes().encrypt(rv, 0, rv, 0, replyKey, replyIV, rv.length); ctx.aes().encrypt(rv, 0, rv, 0, replyKey, replyIV, rv.length);
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))
// log.debug(responseMessageId + ": after encrypt: " + Base64.encode(rv, 0, 128)); // log.debug(responseMessageId + ": after encrypt: " + Base64.encode(rv, 0, 128));
return rv; return new EncryptedBuildRecord(rv);
} }
} }

View File

@@ -0,0 +1,32 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* No warranty of any kind, either expressed or implied.
*/
import net.i2p.data.SimpleDataStructure;
/**
* ElGamal-encrypted request or response.
* 528 bytes. Previously stored in a ByteArray.
* May or may not be AES layer-encrypted.
*
* Note that these are layer-encrypted and layer-decrypted in-place.
* Do not cache.
*
* @since 0.9.18
*/
public class EncryptedBuildRecord extends SimpleDataStructure {
public final static int LENGTH = TunnelBuildMessageBase.RECORD_SIZE;
/** @throws IllegalArgumentException if data is not correct length (null is ok) */
public EncryptedBuildRecord(byte data[]) {
super(data);
}
public int length() {
return LENGTH;
}
}

View File

@@ -1,7 +1,6 @@
package net.i2p.data.i2np; package net.i2p.data.i2np;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
/** /**
* Base for TBM, TBRM, VTBM, VTBRM * Base for TBM, TBRM, VTBM, VTBRM
@@ -18,7 +17,7 @@ import net.i2p.data.ByteArray;
* @since 0.8.8 * @since 0.8.8
*/ */
public abstract class TunnelBuildMessageBase extends I2NPMessageImpl { public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
protected ByteArray _records[]; protected EncryptedBuildRecord _records[];
protected int RECORD_COUNT; protected int RECORD_COUNT;
public static final int MAX_RECORD_COUNT = 8; public static final int MAX_RECORD_COUNT = 8;
@@ -31,14 +30,14 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
super(context); super(context);
if (records > 0) { if (records > 0) {
RECORD_COUNT = records; RECORD_COUNT = records;
_records = new ByteArray[records]; _records = new EncryptedBuildRecord[records];
} }
// else will be initialized by readMessage() // else will be initialized by readMessage()
} }
public void setRecord(int index, ByteArray record) { _records[index] = record; } public void setRecord(int index, EncryptedBuildRecord record) { _records[index] = record; }
public ByteArray getRecord(int index) { return _records[index]; } public EncryptedBuildRecord getRecord(int index) { return _records[index]; }
/** @since 0.7.12 */ /** @since 0.7.12 */
public int getRecordCount() { return RECORD_COUNT; } public int getRecordCount() { return RECORD_COUNT; }
@@ -57,7 +56,7 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
int off = offset + (i * RECORD_SIZE); int off = offset + (i * RECORD_SIZE);
byte rec[] = new byte[RECORD_SIZE]; byte rec[] = new byte[RECORD_SIZE];
System.arraycopy(data, off, rec, 0, RECORD_SIZE); System.arraycopy(data, off, rec, 0, RECORD_SIZE);
setRecord(i, new ByteArray(rec)); setRecord(i, new EncryptedBuildRecord(rec));
} }
} }
@@ -66,7 +65,7 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
if (remaining < 0) if (remaining < 0)
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")"); throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
for (int i = 0; i < RECORD_COUNT; i++) { for (int i = 0; i < RECORD_COUNT; i++) {
System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE); System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
curIndex += RECORD_SIZE; curIndex += RECORD_SIZE;
} }
return curIndex; return curIndex;

View File

@@ -1,7 +1,6 @@
package net.i2p.data.i2np; package net.i2p.data.i2np;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
/** /**
@@ -36,7 +35,7 @@ public class VariableTunnelBuildMessage extends TunnelBuildMessage {
RECORD_COUNT = r; RECORD_COUNT = r;
if (dataSize != calculateWrittenLength()) if (dataSize != calculateWrittenLength())
throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")"); throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")");
_records = new ByteArray[RECORD_COUNT]; _records = new EncryptedBuildRecord[RECORD_COUNT];
super.readMessage(data, offset + 1, dataSize, type); super.readMessage(data, offset + 1, dataSize, type);
} }
@@ -51,7 +50,7 @@ public class VariableTunnelBuildMessage extends TunnelBuildMessage {
// can't call super, written length check will fail // can't call super, written length check will fail
//return super.writeMessageBody(out, curIndex + 1); //return super.writeMessageBody(out, curIndex + 1);
for (int i = 0; i < RECORD_COUNT; i++) { for (int i = 0; i < RECORD_COUNT; i++) {
System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE); System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
curIndex += RECORD_SIZE; curIndex += RECORD_SIZE;
} }
return curIndex; return curIndex;

View File

@@ -1,7 +1,6 @@
package net.i2p.data.i2np; package net.i2p.data.i2np;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
/** /**
@@ -38,7 +37,7 @@ public class VariableTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
RECORD_COUNT = r; RECORD_COUNT = r;
if (dataSize != calculateWrittenLength()) if (dataSize != calculateWrittenLength())
throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")"); throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")");
_records = new ByteArray[RECORD_COUNT]; _records = new EncryptedBuildRecord[RECORD_COUNT];
super.readMessage(data, offset + 1, dataSize, type); super.readMessage(data, offset + 1, dataSize, type);
} }
@@ -53,7 +52,7 @@ public class VariableTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
// can't call super, written length check will fail // can't call super, written length check will fail
//return super.writeMessageBody(out, curIndex + 1); //return super.writeMessageBody(out, curIndex + 1);
for (int i = 0; i < RECORD_COUNT; i++) { for (int i = 0; i < RECORD_COUNT; i++) {
System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE); System.arraycopy(_records[i].getData(), 0, out, curIndex, RECORD_SIZE);
curIndex += RECORD_SIZE; curIndex += RECORD_SIZE;
} }
return curIndex; return curIndex;

View File

@@ -3,11 +3,11 @@ package net.i2p.router.tunnel;
import java.util.List; import java.util.List;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.i2np.BuildRequestRecord; import net.i2p.data.i2np.BuildRequestRecord;
import net.i2p.data.i2np.EncryptedBuildRecord;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
@@ -54,33 +54,41 @@ public abstract class BuildMessageGenerator {
* containing the hop's configuration (as well as the reply info, if it is an outbound endpoint) * containing the hop's configuration (as well as the reply info, if it is an outbound endpoint)
* *
* @param msg out parameter * @param msg out parameter
* @throws IllegalArgumentException if hop bigger than config
*/ */
public static void createRecord(int recordNum, int hop, TunnelBuildMessage msg, public static void createRecord(int recordNum, int hop, TunnelBuildMessage msg,
TunnelCreatorConfig cfg, Hash replyRouter, TunnelCreatorConfig cfg, Hash replyRouter,
long replyTunnel, I2PAppContext ctx, PublicKey peerKey) { long replyTunnel, I2PAppContext ctx, PublicKey peerKey) {
byte encrypted[] = new byte[TunnelBuildMessage.RECORD_SIZE];
//Log log = ctx.logManager().getLog(BuildMessageGenerator.class); //Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
EncryptedBuildRecord erec;
if (peerKey != null) { if (peerKey != null) {
BuildRequestRecord req = null; BuildRequestRecord req = null;
if ( (!cfg.isInbound()) && (hop + 1 == cfg.getLength()) ) //outbound endpoint if ( (!cfg.isInbound()) && (hop + 1 == cfg.getLength()) ) //outbound endpoint
req = createUnencryptedRecord(ctx, cfg, hop, replyRouter, replyTunnel); req = createUnencryptedRecord(ctx, cfg, hop, replyRouter, replyTunnel);
else else
req = createUnencryptedRecord(ctx, cfg, hop, null, -1); req = createUnencryptedRecord(ctx, cfg, hop, null, -1);
if (req == null)
throw new IllegalArgumentException("hop bigger than config");
Hash peer = cfg.getPeer(hop); Hash peer = cfg.getPeer(hop);
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))
// log.debug("Record " + recordNum + "/" + hop + "/" + peer.toBase64() // log.debug("Record " + recordNum + "/" + hop + "/" + peer.toBase64()
// + ": unencrypted = " + Base64.encode(req.getData().getData())); // + ": unencrypted = " + Base64.encode(req.getData().getData()));
req.encryptRecord(ctx, peerKey, peer, encrypted, 0); erec = req.encryptRecord(ctx, peerKey, peer);
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))
// log.debug("Record " + recordNum + "/" + hop + ": encrypted = " + Base64.encode(encrypted)); // log.debug("Record " + recordNum + "/" + hop + ": encrypted = " + Base64.encode(encrypted));
} else { } else {
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))
// log.debug("Record " + recordNum + "/" + hop + "/ is blank/random"); // log.debug("Record " + recordNum + "/" + hop + "/ is blank/random");
byte encrypted[] = new byte[TunnelBuildMessage.RECORD_SIZE];
ctx.random().nextBytes(encrypted); ctx.random().nextBytes(encrypted);
erec = new EncryptedBuildRecord(encrypted);
} }
msg.setRecord(recordNum, new ByteArray(encrypted)); msg.setRecord(recordNum, erec);
} }
/**
* Returns null if hop >= cfg.length
*/
private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop, private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop,
Hash replyRouter, long replyTunnel) { Hash replyRouter, long replyTunnel) {
//Log log = ctx.logManager().getLog(BuildMessageGenerator.class); //Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
@@ -111,11 +119,11 @@ public abstract class BuildMessageGenerator {
SessionKey layerKey = hopConfig.getLayerKey(); SessionKey layerKey = hopConfig.getLayerKey();
SessionKey ivKey = hopConfig.getIVKey(); SessionKey ivKey = hopConfig.getIVKey();
SessionKey replyKey = hopConfig.getReplyKey(); SessionKey replyKey = hopConfig.getReplyKey();
byte iv[] = hopConfig.getReplyIV().getData(); byte iv[] = hopConfig.getReplyIV();
if ( (iv == null) || (iv.length != BuildRequestRecord.IV_SIZE) ) { if (iv == null) {
iv = new byte[BuildRequestRecord.IV_SIZE]; iv = new byte[BuildRequestRecord.IV_SIZE];
ctx.random().nextBytes(iv); ctx.random().nextBytes(iv);
hopConfig.getReplyIV().setData(iv); hopConfig.setReplyIV(iv);
} }
boolean isInGW = (cfg.isInbound() && (hop == 0)); boolean isInGW = (cfg.isInbound() && (hop == 0));
boolean isOutEnd = (!cfg.isInbound() && (hop + 1 >= cfg.getLength())); boolean isOutEnd = (!cfg.isInbound() && (hop + 1 >= cfg.getLength()));
@@ -132,9 +140,9 @@ public abstract class BuildMessageGenerator {
// log.debug("Hop " + hop + " has the next message ID of " + nextMsgId + " for " + cfg // log.debug("Hop " + hop + " has the next message ID of " + nextMsgId + " for " + cfg
// + " with replyKey " + replyKey.toBase64() + " and replyIV " + Base64.encode(iv)); // + " with replyKey " + replyKey.toBase64() + " and replyIV " + Base64.encode(iv));
BuildRequestRecord rec= new BuildRequestRecord(); BuildRequestRecord rec= new BuildRequestRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer,
rec.createRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer, nextMsgId, layerKey, ivKey, replyKey, nextMsgId, layerKey, ivKey, replyKey,
iv, isInGW, isOutEnd); iv, isInGW, isOutEnd);
return rec; return rec;
} else { } else {
@@ -143,7 +151,11 @@ public abstract class BuildMessageGenerator {
} }
/** /**
* Encrypt the records so their hop ident is visible at the appropriate times * Encrypt the records so their hop ident is visible at the appropriate times.
*
* Note that this layer-encrypts the build records for the message in-place.
* Only call this onece for a given message.
*
* @param order list of hop #s as Integers. For instance, if (order.get(1) is 4), it is peer cfg.getPeer(4) * @param order list of hop #s as Integers. For instance, if (order.get(1) is 4), it is peer cfg.getPeer(4)
*/ */
public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg, public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg,
@@ -151,7 +163,7 @@ public abstract class BuildMessageGenerator {
//Log log = ctx.logManager().getLog(BuildMessageGenerator.class); //Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
// encrypt the records so that the right elements will be visible at the right time // encrypt the records so that the right elements will be visible at the right time
for (int i = 0; i < msg.getRecordCount(); i++) { for (int i = 0; i < msg.getRecordCount(); i++) {
ByteArray rec = msg.getRecord(i); EncryptedBuildRecord rec = msg.getRecord(i);
Integer hopNum = order.get(i); Integer hopNum = order.get(i);
int hop = hopNum.intValue(); int hop = hopNum.intValue();
if ( (isBlank(cfg, hop)) || (!cfg.isInbound() && hop == 1) ) { if ( (isBlank(cfg, hop)) || (!cfg.isInbound() && hop == 1) ) {
@@ -166,12 +178,12 @@ public abstract class BuildMessageGenerator {
for (int j = hop-1; j >= stop; j--) { for (int j = hop-1; j >= stop; j--) {
HopConfig hopConfig = cfg.getConfig(j); HopConfig hopConfig = cfg.getConfig(j);
SessionKey key = hopConfig.getReplyKey(); SessionKey key = hopConfig.getReplyKey();
byte iv[] = hopConfig.getReplyIV().getData(); byte iv[] = hopConfig.getReplyIV();
int off = rec.getOffset();
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))
// log.debug(msg.getUniqueId() + ": pre-decrypting record " + i + "/" + hop + " for " + cfg // log.debug(msg.getUniqueId() + ": pre-decrypting record " + i + "/" + hop + " for " + cfg
// + " with " + key.toBase64() + "/" + Base64.encode(iv)); // + " with " + key.toBase64() + "/" + Base64.encode(iv));
ctx.aes().decrypt(rec.getData(), off, rec.getData(), off, key, iv, TunnelBuildMessage.RECORD_SIZE); // corrupts the SDS
ctx.aes().decrypt(rec.getData(), 0, rec.getData(), 0, key, iv, TunnelBuildMessage.RECORD_SIZE);
} }
} }
//if (log.shouldLog(Log.DEBUG)) //if (log.shouldLog(Log.DEBUG))

View File

@@ -2,12 +2,13 @@ package net.i2p.router.tunnel;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.ByteArray; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.i2np.BuildRequestRecord; import net.i2p.data.i2np.BuildRequestRecord;
import net.i2p.data.i2np.EncryptedBuildRecord;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.router.util.DecayingBloomFilter; import net.i2p.router.util.DecayingBloomFilter;
import net.i2p.router.util.DecayingHashSet; import net.i2p.router.util.DecayingHashSet;
@@ -32,7 +33,10 @@ public class BuildMessageProcessor {
* message (so that the reply can be placed in that position after going through the decrypted * message (so that the reply can be placed in that position after going through the decrypted
* request record). * request record).
* *
* @return the current hop's decrypted record * Note that this layer-decrypts the build records in-place.
* Do not call this more than once for a given message.
*
* @return the current hop's decrypted record or null on failure
*/ */
public BuildRequestRecord decrypt(I2PAppContext ctx, TunnelBuildMessage msg, Hash ourHash, PrivateKey privKey) { public BuildRequestRecord decrypt(I2PAppContext ctx, TunnelBuildMessage msg, Hash ourHash, PrivateKey privKey) {
Log log = ctx.logManager().getLog(getClass()); Log log = ctx.logManager().getLog(getClass());
@@ -44,35 +48,33 @@ public class BuildMessageProcessor {
long totalDup = 0; long totalDup = 0;
long beforeLoop = System.currentTimeMillis(); long beforeLoop = System.currentTimeMillis();
for (int i = 0; i < msg.getRecordCount(); i++) { for (int i = 0; i < msg.getRecordCount(); i++) {
ByteArray rec = msg.getRecord(i); EncryptedBuildRecord rec = msg.getRecord(i);
int off = rec.getOffset();
int len = BuildRequestRecord.PEER_SIZE; int len = BuildRequestRecord.PEER_SIZE;
long beforeEq = System.currentTimeMillis(); long beforeEq = System.currentTimeMillis();
boolean eq = DataHelper.eq(ourHash.getData(), 0, rec.getData(), off, len); boolean eq = DataHelper.eq(ourHash.getData(), 0, rec.getData(), 0, len);
totalEq += System.currentTimeMillis()-beforeEq; totalEq += System.currentTimeMillis()-beforeEq;
if (eq) { if (eq) {
long beforeIsDup = System.currentTimeMillis(); long beforeIsDup = System.currentTimeMillis();
boolean isDup = _filter.add(rec.getData(), off + len, 32); boolean isDup = _filter.add(rec.getData(), len, 32);
totalDup += System.currentTimeMillis()-beforeIsDup; totalDup += System.currentTimeMillis()-beforeIsDup;
if (isDup) { if (isDup) {
if (log.shouldLog(Log.WARN)) if (log.shouldLog(Log.WARN))
log.debug(msg.getUniqueId() + ": A record matching our hash was found, but it seems to be a duplicate"); log.debug(msg.getUniqueId() + ": A record matching our hash was found, but it seems to be a duplicate");
ctx.statManager().addRateData("tunnel.buildRequestDup", 1, 0); ctx.statManager().addRateData("tunnel.buildRequestDup", 1);
return null; return null;
} }
BuildRequestRecord req = new BuildRequestRecord();
beforeActualDecrypt = System.currentTimeMillis(); beforeActualDecrypt = System.currentTimeMillis();
boolean ok = req.decryptRecord(ctx, privKey, ourHash, rec); try {
afterActualDecrypt = System.currentTimeMillis(); BuildRequestRecord req = new BuildRequestRecord(ctx, privKey, rec);
if (ok) {
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug(msg.getUniqueId() + ": A record matching our hash was found and decrypted"); log.debug(msg.getUniqueId() + ": A record matching our hash was found and decrypted");
rv = req; rv = req;
} else { } catch (DataFormatException dfe) {
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug(msg.getUniqueId() + ": A record matching our hash was found, but could not be decrypted"); log.debug(msg.getUniqueId() + ": A record matching our hash was found, but could not be decrypted");
return null; // our hop is invalid? b0rkage return null; // our hop is invalid? b0rkage
} }
afterActualDecrypt = System.currentTimeMillis();
ourHop = i; ourHop = i;
} }
} }
@@ -89,11 +91,12 @@ public class BuildMessageProcessor {
int ivOff = 0; int ivOff = 0;
for (int i = 0; i < msg.getRecordCount(); i++) { for (int i = 0; i < msg.getRecordCount(); i++) {
if (i != ourHop) { if (i != ourHop) {
ByteArray data = msg.getRecord(i); EncryptedBuildRecord data = msg.getRecord(i);
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug("Encrypting record " + i + "/?/" + data.getOffset() + "/" + data.getValid() + " with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv, ivOff, 16)); log.debug("Encrypting record " + i + "/? with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv, ivOff, 16));
ctx.aes().encrypt(data.getData(), data.getOffset(), data.getData(), data.getOffset(), replyKey, // corrupts SDS
iv, ivOff, data.getValid()); ctx.aes().encrypt(data.getData(), 0, data.getData(), 0, replyKey,
iv, ivOff, data.length());
} }
} }
long afterEncrypt = System.currentTimeMillis(); long afterEncrypt = System.currentTimeMillis();

View File

@@ -4,10 +4,10 @@ import java.util.List;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.i2np.EncryptedBuildRecord;
import net.i2p.data.i2np.TunnelBuildReplyMessage; import net.i2p.data.i2np.TunnelBuildReplyMessage;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.SimpleByteCache; import net.i2p.util.SimpleByteCache;
@@ -34,6 +34,9 @@ public class BuildReplyHandler {
* Decrypt the tunnel build reply records. This overwrites the contents of the reply. * Decrypt the tunnel build reply records. This overwrites the contents of the reply.
* Thread safe (no state). * Thread safe (no state).
* *
* Note that this layer-decrypts the build records in-place.
* Do not call this more than once for a given message.
*
* @return status for the records (in record order), or null if the replies were not valid. Fake records * @return status for the records (in record order), or null if the replies were not valid. Fake records
* always have 0 as their value * always have 0 as their value
*/ */
@@ -70,7 +73,10 @@ public class BuildReplyHandler {
/** /**
* Decrypt the record (removing the layers of reply encyption) and read out the status * Decrypt the record (removing the layers of reply encyption) and read out the status
* *
* @return -1 on decrypt failure * Note that this layer-decrypts the build records in-place.
* Do not call this more than once for a given message.
*
* @return the status 0-255, or -1 on decrypt failure
*/ */
private int decryptRecord(TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) { private int decryptRecord(TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) {
if (BuildMessageGenerator.isBlank(cfg, hop)) { if (BuildMessageGenerator.isBlank(cfg, hop)) {
@@ -78,9 +84,8 @@ public class BuildReplyHandler {
log.debug(reply.getUniqueId() + ": Record " + recordNum + "/" + hop + " is fake, so consider it valid..."); log.debug(reply.getUniqueId() + ": Record " + recordNum + "/" + hop + " is fake, so consider it valid...");
return 0; return 0;
} }
ByteArray rec = reply.getRecord(recordNum); EncryptedBuildRecord rec = reply.getRecord(recordNum);
byte[] data = rec.getData(); byte[] data = rec.getData();
int off = rec.getOffset();
int start = cfg.getLength() - 1; int start = cfg.getLength() - 1;
if (cfg.isInbound()) if (cfg.isInbound())
start--; // the last hop in an inbound tunnel response doesn't actually encrypt start--; // the last hop in an inbound tunnel response doesn't actually encrypt
@@ -88,35 +93,34 @@ public class BuildReplyHandler {
for (int j = start; j >= hop; j--) { for (int j = start; j >= hop; j--) {
HopConfig hopConfig = cfg.getConfig(j); HopConfig hopConfig = cfg.getConfig(j);
SessionKey replyKey = hopConfig.getReplyKey(); SessionKey replyKey = hopConfig.getReplyKey();
byte replyIV[] = hopConfig.getReplyIV().getData(); byte replyIV[] = hopConfig.getReplyIV();
int replyIVOff = hopConfig.getReplyIV().getOffset();
if (log.shouldLog(Log.DEBUG)) { if (log.shouldLog(Log.DEBUG)) {
log.debug(reply.getUniqueId() + ": Decrypting record " + recordNum + "/" + hop + "/" + j + " with replyKey " log.debug(reply.getUniqueId() + ": Decrypting record " + recordNum + "/" + hop + "/" + j + " with replyKey "
+ replyKey.toBase64() + "/" + Base64.encode(replyIV, replyIVOff, 16) + ": " + cfg); + replyKey.toBase64() + "/" + Base64.encode(replyIV) + ": " + cfg);
log.debug(reply.getUniqueId() + ": before decrypt("+ off + "-"+(off+rec.getValid())+"): " + Base64.encode(data, off, rec.getValid())); log.debug(reply.getUniqueId() + ": before decrypt: " + Base64.encode(data));
log.debug(reply.getUniqueId() + ": Full reply rec: offset=" + off + ", sz=" + data.length + "/" + rec.getValid() + ", data=" + Base64.encode(data, off, TunnelBuildReplyMessage.RECORD_SIZE)); log.debug(reply.getUniqueId() + ": Full reply rec: sz=" + data.length + " data=" + Base64.encode(data, 0, TunnelBuildReplyMessage.RECORD_SIZE));
} }
ctx.aes().decrypt(data, off, data, off, replyKey, replyIV, replyIVOff, TunnelBuildReplyMessage.RECORD_SIZE); ctx.aes().decrypt(data, 0, data, 0, replyKey, replyIV, 0, TunnelBuildReplyMessage.RECORD_SIZE);
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug(reply.getUniqueId() + ": after decrypt: " + Base64.encode(data, off, rec.getValid())); log.debug(reply.getUniqueId() + ": after decrypt: " + Base64.encode(data));
} }
// ok, all of the layered encryption is stripped, so lets verify it // ok, all of the layered encryption is stripped, so lets verify it
// (formatted per BuildResponseRecord.create) // (formatted per BuildResponseRecord.create)
// don't cache the result // don't cache the result
//Hash h = ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH); //Hash h = ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH);
byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH); byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH, h, 0); ctx.sha().calculateHash(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH, h, 0);
boolean ok = DataHelper.eq(h, 0, data, off, Hash.HASH_LENGTH); boolean ok = DataHelper.eq(h, 0, data, 0, Hash.HASH_LENGTH);
if (!ok) { if (!ok) {
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug(reply.getUniqueId() + ": Failed verification on " + recordNum + "/" + hop + ": " + Base64.encode(h) + " calculated, " + log.debug(reply.getUniqueId() + ": Failed verification on " + recordNum + "/" + hop + ": " + Base64.encode(h) + " calculated, " +
Base64.encode(data, off, Hash.HASH_LENGTH) + " expected\n" + Base64.encode(data, 0, Hash.HASH_LENGTH) + " expected\n" +
"Record: " + Base64.encode(data, off+Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH)); "Record: " + Base64.encode(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH));
SimpleByteCache.release(h); SimpleByteCache.release(h);
return -1; return -1;
} else { } else {
SimpleByteCache.release(h); SimpleByteCache.release(h);
int rv = (int)DataHelper.fromLong(data, off + TunnelBuildReplyMessage.RECORD_SIZE - 1, 1); int rv = (int)DataHelper.fromLong(data, TunnelBuildReplyMessage.RECORD_SIZE - 1, 1);
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
log.debug(reply.getUniqueId() + ": Verified: " + rv + " for record " + recordNum + "/" + hop); log.debug(reply.getUniqueId() + ": Verified: " + rv + " for record " + recordNum + "/" + hop);
return rv; return rv;

View File

@@ -1,6 +1,5 @@
package net.i2p.router.tunnel; package net.i2p.router.tunnel;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
@@ -20,7 +19,7 @@ public class HopConfig {
private SessionKey _layerKey; private SessionKey _layerKey;
private SessionKey _ivKey; private SessionKey _ivKey;
private SessionKey _replyKey; private SessionKey _replyKey;
private ByteArray _replyIV; private byte[] _replyIV;
private long _creation; private long _creation;
private long _expiration; private long _expiration;
//private Map _options; //private Map _options;
@@ -87,9 +86,23 @@ public class HopConfig {
public SessionKey getReplyKey() { return _replyKey; } public SessionKey getReplyKey() { return _replyKey; }
public void setReplyKey(SessionKey key) { _replyKey = key; } public void setReplyKey(SessionKey key) { _replyKey = key; }
/** iv used to encrypt the reply sent for the new tunnel creation crypto */ /**
public ByteArray getReplyIV() { return _replyIV; } * IV used to encrypt the reply sent for the new tunnel creation crypto
public void setReplyIV(ByteArray iv) { _replyIV = iv; } *
* @return 16 bytes
*/
public byte[] getReplyIV() { return _replyIV; }
/**
* IV used to encrypt the reply sent for the new tunnel creation crypto
*
* @throws IllegalArgumentException if not 16 bytes
*/
public void setReplyIV(byte[] iv) {
if (iv.length != REPLY_IV_LENGTH)
throw new IllegalArgumentException();
_replyIV = iv;
}
/** when does this tunnel expire (in ms since the epoch)? */ /** when does this tunnel expire (in ms since the epoch)? */
public long getExpiration() { return _expiration; } public long getExpiration() { return _expiration; }

View File

@@ -6,7 +6,6 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.router.RouterIdentity; import net.i2p.data.router.RouterIdentity;
@@ -14,6 +13,7 @@ import net.i2p.data.router.RouterInfo;
import net.i2p.data.TunnelId; import net.i2p.data.TunnelId;
import net.i2p.data.i2np.BuildRequestRecord; import net.i2p.data.i2np.BuildRequestRecord;
import net.i2p.data.i2np.BuildResponseRecord; import net.i2p.data.i2np.BuildResponseRecord;
import net.i2p.data.i2np.EncryptedBuildRecord;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildReplyMessage; import net.i2p.data.i2np.TunnelBuildReplyMessage;
@@ -782,13 +782,13 @@ class BuildHandler implements Runnable {
return; return;
} }
byte reply[] = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId()); EncryptedBuildRecord reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
int records = state.msg.getRecordCount(); int records = state.msg.getRecordCount();
int ourSlot = -1; int ourSlot = -1;
for (int j = 0; j < records; j++) { for (int j = 0; j < records; j++) {
if (state.msg.getRecord(j) == null) { if (state.msg.getRecord(j) == null) {
ourSlot = j; ourSlot = j;
state.msg.setRecord(j, new ByteArray(reply)); state.msg.setRecord(j, reply);
//if (_log.shouldLog(Log.DEBUG)) //if (_log.shouldLog(Log.DEBUG))
// _log.debug("Full reply record for slot " + ourSlot + "/" + ourId + "/" + nextId + "/" + req.readReplyMessageId() // _log.debug("Full reply record for slot " + ourSlot + "/" + ourId + "/" + nextId + "/" + req.readReplyMessageId()
// + ": " + Base64.encode(reply)); // + ": " + Base64.encode(reply));

View File

@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
@@ -90,7 +89,7 @@ abstract class BuildRequestor {
cfg.getConfig(i-1).setSendTunnelId(cfg.getConfig(i).getReceiveTunnelId()); cfg.getConfig(i-1).setSendTunnelId(cfg.getConfig(i).getReceiveTunnelId());
byte iv[] = new byte[16]; byte iv[] = new byte[16];
ctx.random().nextBytes(iv); ctx.random().nextBytes(iv);
cfg.getConfig(i).setReplyIV(new ByteArray(iv)); cfg.getConfig(i).setReplyIV(iv);
cfg.getConfig(i).setReplyKey(ctx.keyGenerator().generateSessionKey()); cfg.getConfig(i).setReplyKey(ctx.keyGenerator().generateSessionKey());
} }
// This is in BuildExecutor.buildTunnel() now // This is in BuildExecutor.buildTunnel() now