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.data.ByteArray;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
@@ -9,7 +10,8 @@ import net.i2p.data.PublicKey;
|
||||
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.
|
||||
*
|
||||
* Cleartext:
|
||||
@@ -36,7 +38,7 @@ import net.i2p.data.SessionKey;
|
||||
*
|
||||
*/
|
||||
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
|
||||
@@ -55,11 +57,10 @@ public class BuildRequestRecord {
|
||||
/** we show 16 bytes of the peer hash outside the elGamal block */
|
||||
public static final int PEER_SIZE = 16;
|
||||
|
||||
public BuildRequestRecord(ByteArray data) { _data = data; }
|
||||
public BuildRequestRecord() { }
|
||||
|
||||
public ByteArray getData() { return _data; }
|
||||
public void setData(ByteArray data) { _data = data; }
|
||||
/**
|
||||
* @return 222 bytes, non-null
|
||||
*/
|
||||
public byte[] getData() { return _data; }
|
||||
|
||||
private static final int OFF_RECV_TUNNEL = 0;
|
||||
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_REQ_TIME = OFF_FLAG + 1;
|
||||
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 */
|
||||
public long readReceiveTunnelId() {
|
||||
return DataHelper.fromLong(_data.getData(), _data.getOffset() + 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);
|
||||
return DataHelper.fromLong(_data, OFF_RECV_TUNNEL, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* the gateway to which the reply should be sent.
|
||||
*/
|
||||
public Hash readNextIdentity() {
|
||||
//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 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
|
||||
*/
|
||||
public SessionKey readLayerKey() {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tunnel IV encryption key that the current hop should use
|
||||
*/
|
||||
public SessionKey readIVKey() {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session key that should be used to encrypt the reply
|
||||
*/
|
||||
public SessionKey readReplyKey() {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* IV that should be used to encrypt the reply
|
||||
*/
|
||||
public byte[] readReplyIV() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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
|
||||
* fields refer to where the reply should be sent.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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,
|
||||
* this specifies the message ID with which the reply should be sent.
|
||||
*/
|
||||
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 15-527: ElGamal-2048 encrypted block
|
||||
* </pre>
|
||||
*
|
||||
* @return non-null
|
||||
*/
|
||||
public void encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer, byte out[], int outOffset) {
|
||||
System.arraycopy(toPeer.getData(), 0, out, outOffset, PEER_SIZE);
|
||||
byte preEncr[] = new byte[OFF_SEND_MSG_ID + 4 + PADDING_SIZE];
|
||||
System.arraycopy(_data.getData(), _data.getOffset(), preEncr, 0, preEncr.length);
|
||||
byte encrypted[] = ctx.elGamalEngine().encrypt(preEncr, toKey);
|
||||
public EncryptedBuildRecord encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer) {
|
||||
byte[] out = new byte[EncryptedBuildRecord.LENGTH];
|
||||
System.arraycopy(toPeer.getData(), 0, out, 0, PEER_SIZE);
|
||||
byte encrypted[] = ctx.elGamalEngine().encrypt(_data, toKey);
|
||||
// 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
|
||||
System.arraycopy(encrypted, 1, out, outOffset + PEER_SIZE, 256);
|
||||
System.arraycopy(encrypted, 258, out, outOffset + 256 + PEER_SIZE, 256);
|
||||
System.arraycopy(encrypted, 1, out, 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
|
||||
* 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) {
|
||||
if (DataHelper.eq(ourIdent.getData(), 0, encryptedRecord.getData(), encryptedRecord.getOffset(), PEER_SIZE)) {
|
||||
public BuildRequestRecord(I2PAppContext ctx, PrivateKey ourKey,
|
||||
EncryptedBuildRecord encryptedRecord) throws DataFormatException {
|
||||
byte preDecrypt[] = new byte[514];
|
||||
System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE, preDecrypt, 1, 256);
|
||||
System.arraycopy(encryptedRecord.getData(), encryptedRecord.getOffset() + PEER_SIZE + 256, preDecrypt, 258, 256);
|
||||
System.arraycopy(encryptedRecord.getData(), PEER_SIZE, preDecrypt, 1, 256);
|
||||
System.arraycopy(encryptedRecord.getData(), PEER_SIZE + 256, preDecrypt, 258, 256);
|
||||
byte decrypted[] = ctx.elGamalEngine().decrypt(preDecrypt, ourKey);
|
||||
if (decrypted != null) {
|
||||
_data = new ByteArray(decrypted);
|
||||
_data.setOffset(0);
|
||||
return true;
|
||||
_data = decrypted;
|
||||
} 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
|
||||
* 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 isInGateway are we the gateway of an inbound 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,
|
||||
boolean isOutEndpoint) {
|
||||
if ( (_data == null) || (_data.getData() != null) )
|
||||
_data = new ByteArray();
|
||||
byte buf[] = new byte[OFF_SEND_MSG_ID+4+PADDING_SIZE];
|
||||
_data.setData(buf);
|
||||
byte buf[] = new byte[LENGTH];
|
||||
_data = buf;
|
||||
|
||||
/* bytes 0-3: tunnel ID to receive messages as
|
||||
* bytes 4-35: local router identity hash
|
||||
|
||||
@@ -7,12 +7,17 @@ import net.i2p.data.SessionKey;
|
||||
//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).
|
||||
*
|
||||
* When decrypted:
|
||||
*
|
||||
*<pre>
|
||||
* Bytes 0-31 contain the hash of bytes 32-527
|
||||
* Bytes 32-526 contain random data.
|
||||
* Byte 527 contains the reply.
|
||||
*</pre>
|
||||
*/
|
||||
public class BuildResponseRecord {
|
||||
|
||||
@@ -20,10 +25,12 @@ public class BuildResponseRecord {
|
||||
* Create a new encrypted response
|
||||
*
|
||||
* @param status the response 0-255
|
||||
* @param replyIV 16 bytes
|
||||
* @param responseMessageId unused except for debugging
|
||||
* @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);
|
||||
byte rv[] = new byte[TunnelBuildReplyMessage.RECORD_SIZE];
|
||||
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);
|
||||
//if (log.shouldLog(Log.DEBUG))
|
||||
// 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;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* Base for TBM, TBRM, VTBM, VTBRM
|
||||
@@ -18,7 +17,7 @@ import net.i2p.data.ByteArray;
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
|
||||
protected ByteArray _records[];
|
||||
protected EncryptedBuildRecord _records[];
|
||||
protected int RECORD_COUNT;
|
||||
public static final int MAX_RECORD_COUNT = 8;
|
||||
|
||||
@@ -31,14 +30,14 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
|
||||
super(context);
|
||||
if (records > 0) {
|
||||
RECORD_COUNT = records;
|
||||
_records = new ByteArray[records];
|
||||
_records = new EncryptedBuildRecord[records];
|
||||
}
|
||||
// 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 */
|
||||
public int getRecordCount() { return RECORD_COUNT; }
|
||||
@@ -57,7 +56,7 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
|
||||
int off = offset + (i * RECORD_SIZE);
|
||||
byte rec[] = new byte[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)
|
||||
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
|
||||
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;
|
||||
}
|
||||
return curIndex;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
@@ -36,7 +35,7 @@ public class VariableTunnelBuildMessage extends TunnelBuildMessage {
|
||||
RECORD_COUNT = r;
|
||||
if (dataSize != calculateWrittenLength())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -51,7 +50,7 @@ public class VariableTunnelBuildMessage extends TunnelBuildMessage {
|
||||
// can't call super, written length check will fail
|
||||
//return super.writeMessageBody(out, curIndex + 1);
|
||||
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;
|
||||
}
|
||||
return curIndex;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
@@ -38,7 +37,7 @@ public class VariableTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
RECORD_COUNT = r;
|
||||
if (dataSize != calculateWrittenLength())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -53,7 +52,7 @@ public class VariableTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
// can't call super, written length check will fail
|
||||
//return super.writeMessageBody(out, curIndex + 1);
|
||||
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;
|
||||
}
|
||||
return curIndex;
|
||||
|
||||
Reference in New Issue
Block a user