forked from I2P_Developers/i2p.i2p
I2NP:
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:
@@ -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
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
router/java/src/net/i2p/data/i2np/EncryptedBuildRecord.java
Normal file
32
router/java/src/net/i2p/data/i2np/EncryptedBuildRecord.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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))
|
||||||
|
@@ -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();
|
||||||
|
@@ -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;
|
||||||
|
@@ -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; }
|
||||||
|
@@ -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));
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user