Compare commits

...

3 Commits

Author SHA1 Message Date
zzz
6e1c419ec7 short build records 2021-05-22 15:40:59 -04:00
zzz
2693cf97c4 short build records 2021-05-22 15:33:05 -04:00
zzz
aa2da6da45 Hook in new prop. 157 messages
WIP, incomplete, untested
2021-05-06 09:38:19 -04:00
12 changed files with 387 additions and 88 deletions

View File

@@ -57,7 +57,7 @@ import net.i2p.router.RouterContext;
* bytes 16-527: ElGamal encrypted block (discarding zero bytes at elg[0] and elg[257]) * bytes 16-527: ElGamal encrypted block (discarding zero bytes at elg[0] and elg[257])
* </pre> * </pre>
* *
* New ECIES format, ref: proposal 152: * ECIES long record format, ref: proposal 152:
* *
* Holds the unencrypted 464-byte tunnel request record, * Holds the unencrypted 464-byte tunnel request record,
* with a constructor for ECIES decryption and a method for ECIES encryption. * with a constructor for ECIES decryption and a method for ECIES encryption.
@@ -90,6 +90,36 @@ import net.i2p.router.RouterContext;
* bytes 512-527: Poly1305 MAC * bytes 512-527: Poly1305 MAC
* </pre> * </pre>
* *
* ECIES short record format, ref: proposal 157:
*
* Holds the unencrypted 172-byte tunnel request record,
* with a constructor for ECIES decryption and a method for ECIES encryption.
* Iterative AES encryption/decryption is done elsewhere.
*
* Cleartext:
* <pre>
* bytes 0-3: tunnel ID to receive messages as, nonzero
* bytes 4-7: next tunnel ID, nonzero
* bytes 8-39: next router identity hash
* byte 40: flags
* bytes 41-42: more flags, unused, set to 0 for compatibility
* byte 43: layer enc. type
* bytes 44-47: request time (in minutes since the epoch, rounded down)
* bytes 48-51: request expiration (in seconds since creation)
* bytes 52-55: next message ID
* bytes 56-x: tunnel build options (Mapping)
* bytes x-x: other data as implied by flags or options
* bytes x-171: random padding
* </pre>
*
* Encrypted:
* <pre>
* bytes 0-15: Hop's truncated identity hash
* bytes 16-47: Sender's ephemeral X25519 public key
* bytes 48-219: ChaCha20 encrypted BuildRequestRecord
* bytes 220-235: Poly1305 MAC
* </pre>
*
*/ */
public class BuildRequestRecord { public class BuildRequestRecord {
private final byte[] _data; private final byte[] _data;
@@ -138,7 +168,7 @@ public class BuildRequestRecord {
// 222 // 222
private static final int LENGTH = OFF_SEND_MSG_ID + 4 + PADDING_SIZE; private static final int LENGTH = OFF_SEND_MSG_ID + 4 + PADDING_SIZE;
// New ECIES format // ECIES long record format
private static final int OFF_SEND_TUNNEL_EC = OFF_OUR_IDENT; private static final int OFF_SEND_TUNNEL_EC = OFF_OUR_IDENT;
private static final int OFF_SEND_IDENT_EC = OFF_SEND_TUNNEL_EC + 4; private static final int OFF_SEND_IDENT_EC = OFF_SEND_TUNNEL_EC + 4;
private static final int OFF_LAYER_KEY_EC = OFF_SEND_IDENT_EC + Hash.HASH_LENGTH; private static final int OFF_LAYER_KEY_EC = OFF_SEND_IDENT_EC + Hash.HASH_LENGTH;
@@ -152,6 +182,16 @@ public class BuildRequestRecord {
private static final int OFF_OPTIONS = OFF_SEND_MSG_ID_EC + 4; private static final int OFF_OPTIONS = OFF_SEND_MSG_ID_EC + 4;
private static final int LENGTH_EC = 464; private static final int LENGTH_EC = 464;
private static final int MAX_OPTIONS_LENGTH = LENGTH_EC - OFF_OPTIONS; // includes options length private static final int MAX_OPTIONS_LENGTH = LENGTH_EC - OFF_OPTIONS; // includes options length
// ECIES short record format
private static final int OFF_FLAG_EC_SHORT = OFF_SEND_IDENT_EC + Hash.HASH_LENGTH;
private static final int OFF_LAYER_ENC_TYPE = OFF_FLAG_EC_SHORT + 3;
private static final int OFF_REQ_TIME_EC_SHORT = OFF_LAYER_ENC_TYPE + 1;
private static final int OFF_EXPIRATION_SHORT = OFF_REQ_TIME_EC + 4;
private static final int OFF_SEND_MSG_ID_EC_SHORT = OFF_EXPIRATION + 4;
private static final int OFF_OPTIONS_SHORT = OFF_SEND_MSG_ID_EC_SHORT + 4;
private static final int LENGTH_EC_SHORT = 172;
private static final int MAX_OPTIONS_LENGTH_SHORT = LENGTH_EC_SHORT - OFF_OPTIONS_SHORT; // includes options length
private static final boolean TEST = false; private static final boolean TEST = false;
private static KeyFactory TESTKF; private static KeyFactory TESTKF;
@@ -225,7 +265,8 @@ public class BuildRequestRecord {
* *
*/ */
public boolean readIsInboundGateway() { public boolean readIsInboundGateway() {
int off = _isEC ? OFF_FLAG_EC : OFF_FLAG; int off = _isEC ? ( _data.length == LENGTH_EC_SHORT ? OFF_FLAG_EC_SHORT : OFF_FLAG_EC)
: OFF_FLAG;
return (_data[off] & FLAG_UNRESTRICTED_PREV) != 0; return (_data[off] & FLAG_UNRESTRICTED_PREV) != 0;
} }
@@ -234,7 +275,8 @@ public class BuildRequestRecord {
* fields refer to where the reply should be sent. * fields refer to where the reply should be sent.
*/ */
public boolean readIsOutboundEndpoint() { public boolean readIsOutboundEndpoint() {
int off = _isEC ? OFF_FLAG_EC : OFF_FLAG; int off = _isEC ? ( _data.length == LENGTH_EC_SHORT ? OFF_FLAG_EC_SHORT : OFF_FLAG_EC)
: OFF_FLAG;
return (_data[off] & FLAG_OUTBOUND_ENDPOINT) != 0; return (_data[off] & FLAG_OUTBOUND_ENDPOINT) != 0;
} }
@@ -244,8 +286,10 @@ public class BuildRequestRecord {
* This ignores leap seconds. * This ignores leap seconds.
*/ */
public long readRequestTime() { public long readRequestTime() {
if (_isEC) if (_isEC) {
return DataHelper.fromLong(_data, OFF_REQ_TIME_EC, 4) * (60 * 1000L); int off = _data.length == LENGTH_EC_SHORT ? OFF_REQ_TIME_EC_SHORT : OFF_REQ_TIME_EC;
return DataHelper.fromLong(_data, off, 4) * (60 * 1000L);
}
return DataHelper.fromLong(_data, OFF_REQ_TIME, 4) * (60 * 60 * 1000L); return DataHelper.fromLong(_data, OFF_REQ_TIME, 4) * (60 * 60 * 1000L);
} }
@@ -254,7 +298,8 @@ public class BuildRequestRecord {
* 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() {
int off = _isEC ? OFF_SEND_MSG_ID_EC : OFF_SEND_MSG_ID; int off = _isEC ? ( _data.length == LENGTH_EC_SHORT ? OFF_SEND_MSG_ID_EC_SHORT : OFF_SEND_MSG_ID_EC)
: OFF_SEND_MSG_ID;
return DataHelper.fromLong(_data, off, 4); return DataHelper.fromLong(_data, off, 4);
} }
@@ -265,7 +310,8 @@ public class BuildRequestRecord {
public long readExpiration() { public long readExpiration() {
if (!_isEC) if (!_isEC)
return DEFAULT_EXPIRATION_SECONDS * 1000L; return DEFAULT_EXPIRATION_SECONDS * 1000L;
return DataHelper.fromLong(_data, OFF_EXPIRATION, 4) * 1000L; int off = _data.length == LENGTH_EC_SHORT ? OFF_EXPIRATION_SHORT : OFF_EXPIRATION;
return DataHelper.fromLong(_data, off, 4) * 1000L;
} }
/** /**
@@ -276,7 +322,11 @@ public class BuildRequestRecord {
public Properties readOptions() { public Properties readOptions() {
if (!_isEC) if (!_isEC)
return null; return null;
ByteArrayInputStream in = new ByteArrayInputStream(_data, OFF_OPTIONS, MAX_OPTIONS_LENGTH); ByteArrayInputStream in;
if (_data.length == LENGTH_EC_SHORT)
in = new ByteArrayInputStream(_data, OFF_OPTIONS_SHORT, MAX_OPTIONS_LENGTH_SHORT);
else
in = new ByteArrayInputStream(_data, OFF_OPTIONS, MAX_OPTIONS_LENGTH);
try { try {
return DataHelper.readProperties(in, null); return DataHelper.readProperties(in, null);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
@@ -286,6 +336,17 @@ public class BuildRequestRecord {
} }
} }
/**
* ECIES short record only.
* @return 0 for ElGamal or ECIES long record
* @since 0.9.51
*/
public int readLayerEncryptionType() {
if (_data.length == LENGTH_EC_SHORT)
return _data[OFF_LAYER_ENC_TYPE] & 0xff;
return 0;
}
/** /**
* Encrypt the record to the specified peer. The result is formatted as: <pre> * Encrypt the record to the specified peer. The result is formatted as: <pre>
* 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)
@@ -324,7 +385,8 @@ public class BuildRequestRecord {
EncType type = toKey.getType(); EncType type = toKey.getType();
if (type != EncType.ECIES_X25519) if (type != EncType.ECIES_X25519)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
byte[] out = new byte[EncryptedBuildRecord.LENGTH]; boolean isShort = _data.length == LENGTH_EC_SHORT;
byte[] out = new byte[isShort ? ShortEncryptedBuildRecord.LENGTH : EncryptedBuildRecord.LENGTH];
System.arraycopy(toPeer.getData(), 0, out, 0, PEER_SIZE); System.arraycopy(toPeer.getData(), 0, out, 0, PEER_SIZE);
HandshakeState state = null; HandshakeState state = null;
try { try {
@@ -332,8 +394,8 @@ public class BuildRequestRecord {
state = new HandshakeState(HandshakeState.PATTERN_ID_N, HandshakeState.INITIATOR, kf); state = new HandshakeState(HandshakeState.PATTERN_ID_N, HandshakeState.INITIATOR, kf);
state.getRemotePublicKey().setPublicKey(toKey.getData(), 0); state.getRemotePublicKey().setPublicKey(toKey.getData(), 0);
state.start(); state.start();
state.writeMessage(out, PEER_SIZE, _data, 0, LENGTH_EC); state.writeMessage(out, PEER_SIZE, _data, 0, _data.length);
EncryptedBuildRecord rv = new EncryptedBuildRecord(out); EncryptedBuildRecord rv = isShort ? new ShortEncryptedBuildRecord(out) : new EncryptedBuildRecord(out);
_chachaReplyKey = new SessionKey(state.getChainingKey()); _chachaReplyKey = new SessionKey(state.getChainingKey());
_chachaReplyAD = new byte[32]; _chachaReplyAD = new byte[32];
System.arraycopy(state.getHandshakeHash(), 0, _chachaReplyAD, 0, 32); System.arraycopy(state.getHandshakeHash(), 0, _chachaReplyAD, 0, 32);
@@ -414,8 +476,10 @@ public class BuildRequestRecord {
state.getLocalKeyPair().setKeys(ourKey.getData(), 0, state.getLocalKeyPair().setKeys(ourKey.getData(), 0,
ourKey.toPublic().getData(), 0); ourKey.toPublic().getData(), 0);
state.start(); state.start();
decrypted = new byte[LENGTH_EC]; int len = encryptedRecord.length();
state.readMessage(encrypted, PEER_SIZE, EncryptedBuildRecord.LENGTH - PEER_SIZE, boolean isShort = len == ShortEncryptedBuildRecord.LENGTH;
decrypted = new byte[isShort ? LENGTH_EC_SHORT : LENGTH_EC];
state.readMessage(encrypted, PEER_SIZE, len - PEER_SIZE,
decrypted, 0); decrypted, 0);
_chachaReplyKey = new SessionKey(state.getChainingKey()); _chachaReplyKey = new SessionKey(state.getChainingKey());
_chachaReplyAD = new byte[32]; _chachaReplyAD = new byte[32];
@@ -507,7 +571,7 @@ public class BuildRequestRecord {
* 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.
* *
* ECIES only. ElGamal constructor above. * ECIES long record only. ElGamal constructor above.
* *
* @param receiveTunnelId tunnel the current hop will receive messages on * @param receiveTunnelId tunnel the current hop will receive messages on
* @param nextTunnelId id for the next hop, or where we send the reply (if we are the outbound endpoint) * @param nextTunnelId id for the next hop, or where we send the reply (if we are the outbound endpoint)
@@ -559,6 +623,55 @@ public class BuildRequestRecord {
} }
} }
/**
* Populate this instance with data. A new buffer is created to contain the data, with the
* necessary randomized padding.
*
* ECIES short record only. ElGamal constructor above.
*
* @param receiveTunnelId tunnel the current hop will receive messages on
* @param nextTunnelId id for the next hop, or where we send the reply (if we are the outbound endpoint)
* @param nextHop next hop's identity, or where we send the reply (if we are the outbound endpoint)
* @param nextMsgId message ID to use when sending on to the next hop (or for the reply)
* @param isInGateway are we the gateway of an inbound tunnel?
* @param isOutEndpoint are we the endpoint of an outbound tunnel?
* @param options 116 bytes max when serialized
* @since 0.9.51
* @throws IllegalArgumentException if options too long
*/
public BuildRequestRecord(I2PAppContext ctx, long receiveTunnelId, long nextTunnelId, Hash nextHop, long nextMsgId,
boolean isInGateway, boolean isOutEndpoint, Properties options) {
byte buf[] = new byte[LENGTH_EC_SHORT];
_data = buf;
_isEC = true;
DataHelper.toLong(buf, OFF_RECV_TUNNEL, 4, receiveTunnelId);
DataHelper.toLong(buf, OFF_SEND_TUNNEL_EC, 4, nextTunnelId);
System.arraycopy(nextHop.getData(), 0, buf, OFF_SEND_IDENT_EC, Hash.HASH_LENGTH);
if (isInGateway)
buf[OFF_FLAG_EC_SHORT] |= FLAG_UNRESTRICTED_PREV;
else if (isOutEndpoint)
buf[OFF_FLAG_EC_SHORT] |= FLAG_OUTBOUND_ENDPOINT;
// 2 bytes unused flags = 0
// 1 byte layer enc. type = 0
long truncatedMinute = ctx.clock().now();
// prevent hop identification at top of the minute
truncatedMinute -= ctx.random().nextInt(2048);
// this ignores leap seconds
truncatedMinute /= (60*1000L);
DataHelper.toLong(buf, OFF_REQ_TIME_EC_SHORT, 4, truncatedMinute);
DataHelper.toLong(buf, OFF_EXPIRATION_SHORT, 4, DEFAULT_EXPIRATION_SECONDS);
DataHelper.toLong(buf, OFF_SEND_MSG_ID_EC_SHORT, 4, nextMsgId);
try {
int off = DataHelper.toProperties(buf, OFF_OPTIONS_SHORT, options);
int sz = LENGTH_EC_SHORT - off;
if (sz > 0)
ctx.random().nextBytes(buf, off, sz);
} catch (Exception e) {
throw new IllegalArgumentException("options", e);
}
}
/** /**
* @since 0.9.24 * @since 0.9.24
*/ */
@@ -566,6 +679,8 @@ public class BuildRequestRecord {
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(256); StringBuilder buf = new StringBuilder(256);
buf.append(_isEC ? "ECIES" : "ElGamal"); buf.append(_isEC ? "ECIES" : "ElGamal");
if (_data.length == LENGTH_EC_SHORT)
buf.append(" short ");
buf.append(" BRR "); buf.append(" BRR ");
boolean isIBGW = readIsInboundGateway(); boolean isIBGW = readIsInboundGateway();
boolean isOBEP = readIsOutboundEndpoint(); boolean isOBEP = readIsOutboundEndpoint();
@@ -578,12 +693,14 @@ public class BuildRequestRecord {
buf.append("part. in: ").append(readReceiveTunnelId()) buf.append("part. in: ").append(readReceiveTunnelId())
.append(" out: ").append(readNextTunnelId()); .append(" out: ").append(readNextTunnelId());
} }
buf.append(" to: ").append(readNextIdentity()) buf.append(" to: ").append(readNextIdentity());
.append(" layer key: ").append(readLayerKey()) if (_data.length != LENGTH_EC_SHORT) {
.append(" IV key: ").append(readIVKey()) buf.append(" layer key: ").append(readLayerKey())
.append(" reply key: ").append(readReplyKey()) .append(" IV key: ").append(readIVKey())
.append(" reply IV: ").append(Base64.encode(readReplyIV())) .append(" reply key: ").append(readReplyKey())
.append(" time: ").append(DataHelper.formatTime(readRequestTime())) .append(" reply IV: ").append(Base64.encode(readReplyIV()));
}
buf.append(" time: ").append(DataHelper.formatTime(readRequestTime()))
.append(" reply msg id: ").append(readReplyMessageId()) .append(" reply msg id: ").append(readReplyMessageId())
.append(" expires in: ").append(DataHelper.formatDuration(readExpiration())); .append(" expires in: ").append(DataHelper.formatDuration(readExpiration()));
if (_isEC) { if (_isEC) {

View File

@@ -46,7 +46,7 @@ public class BuildResponseRecord {
} }
/** /**
* Create a new encrypted response. * Create a new encrypted response (long record).
* ChaCha/Poly only for ECIES routers. * ChaCha/Poly only for ECIES routers.
* *
* @param status the response 0-255 * @param status the response 0-255
@@ -77,6 +77,38 @@ public class BuildResponseRecord {
return new EncryptedBuildRecord(rv); return new EncryptedBuildRecord(rv);
} }
/**
* Create a new encrypted response (short record).
* ChaCha/Poly only for ECIES routers.
*
* @param status the response 0-255
* @param replyAD 32 bytes
* @param options 116 bytes max when serialized
* @return a 236-byte response record
* @throws IllegalArgumentException if options too big or on encryption failure
* @since 0.9.451
*/
public static ShortEncryptedBuildRecord createShort(I2PAppContext ctx, int status, SessionKey replyKey,
byte replyAD[], Properties options) {
byte rv[] = new byte[ShortTunnelBuildMessage.SHORT_RECORD_SIZE];
int off;
try {
off = DataHelper.toProperties(rv, 0, options);
} catch (Exception e) {
throw new IllegalArgumentException("options", e);
}
int sz = ShortTunnelBuildMessage.SHORT_RECORD_SIZE - off - 1;
if (sz > 0)
ctx.random().nextBytes(rv, off, sz);
else if (sz < 0)
throw new IllegalArgumentException("options");
rv[ShortTunnelBuildMessage.SHORT_RECORD_SIZE - 17] = (byte) status;
boolean ok = encryptAEADBlock(replyAD, rv, replyKey);
if (!ok)
throw new IllegalArgumentException("encrypt fail");
return new ShortEncryptedBuildRecord(rv);
}
/** /**
* Encrypts in place * Encrypts in place
* @param ad non-null * @param ad non-null

View File

@@ -438,6 +438,15 @@ public abstract class I2NPMessageImpl implements I2NPMessage {
// since 0.7.10 // since 0.7.10
case VariableTunnelBuildReplyMessage.MESSAGE_TYPE: case VariableTunnelBuildReplyMessage.MESSAGE_TYPE:
return new VariableTunnelBuildReplyMessage(context); return new VariableTunnelBuildReplyMessage(context);
// since 0.9.51
case InboundTunnelBuildMessage.MESSAGE_TYPE:
return new InboundTunnelBuildMessage(context);
// since 0.9.51
case OutboundTunnelBuildReplyMessage.MESSAGE_TYPE:
return new OutboundTunnelBuildReplyMessage(context);
// since 0.9.51
case ShortTunnelBuildMessage.MESSAGE_TYPE:
return new ShortTunnelBuildMessage(context);
default: default:
// unused // unused
Builder builder = _builders.get(Integer.valueOf(type)); Builder builder = _builders.get(Integer.valueOf(type));

View File

@@ -30,6 +30,16 @@ public class InboundTunnelBuildMessage extends TunnelBuildMessage {
super(context, records); super(context, records);
} }
/**
* @param record must be ShortEncryptedBuildRecord or null
*/
@Override
public void setRecord(int index, EncryptedBuildRecord record) {
if (record != null && record.length() != SHORT_RECORD_SIZE)
throw new IllegalArgumentException();
super.setRecord(index, record);
}
/** /**
* Set the slot and data for the plaintext record. * Set the slot and data for the plaintext record.
* @throws IllegalArgumentException on bad slot or data length. * @throws IllegalArgumentException on bad slot or data length.
@@ -71,7 +81,7 @@ public class InboundTunnelBuildMessage extends TunnelBuildMessage {
throw new I2NPMessageException("Bad record count " + r); throw new I2NPMessageException("Bad record count " + r);
RECORD_COUNT = r; RECORD_COUNT = r;
int _plaintextSlot = data[offset++] & 0xff; int _plaintextSlot = data[offset++] & 0xff;
if (_plaintextSlot < 0 || _plaintextSlot >= r) if (_plaintextSlot >= r)
throw new I2NPMessageException("Bad slot " + _plaintextSlot); throw new I2NPMessageException("Bad slot " + _plaintextSlot);
int size = (int) DataHelper.fromLong(data, offset, 2); int size = (int) DataHelper.fromLong(data, offset, 2);
if (size <= 0 || size > MAX_PLAINTEXT_RECORD_SIZE) if (size <= 0 || size > MAX_PLAINTEXT_RECORD_SIZE)

View File

@@ -30,6 +30,16 @@ public class OutboundTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
super(context, records); super(context, records);
} }
/**
* @param record must be ShortEncryptedBuildRecord or null
*/
@Override
public void setRecord(int index, EncryptedBuildRecord record) {
if (record != null && record.length() != SHORT_RECORD_SIZE)
throw new IllegalArgumentException();
super.setRecord(index, record);
}
/** /**
* Set the slot and data for the plaintext record. * Set the slot and data for the plaintext record.
* @throws IllegalArgumentException on bad slot or data length. * @throws IllegalArgumentException on bad slot or data length.

View File

@@ -21,6 +21,16 @@ public class ShortTunnelBuildMessage extends TunnelBuildMessage {
super(context, records); super(context, records);
} }
/**
* @param record must be ShortEncryptedBuildRecord or null
*/
@Override
public void setRecord(int index, EncryptedBuildRecord record) {
if (record != null && record.length() != SHORT_RECORD_SIZE)
throw new IllegalArgumentException();
super.setRecord(index, record);
}
@Override @Override
protected int calculateWrittenLength() { return 1 + (RECORD_COUNT * SHORT_RECORD_SIZE); } protected int calculateWrittenLength() { return 1 + (RECORD_COUNT * SHORT_RECORD_SIZE); }

View File

@@ -35,8 +35,14 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
// else will be initialized by readMessage() // else will be initialized by readMessage()
} }
/**
* @param record may be null
*/
public void setRecord(int index, EncryptedBuildRecord record) { _records[index] = record; } public void setRecord(int index, EncryptedBuildRecord record) { _records[index] = record; }
/**
* @return may be null
*/
public EncryptedBuildRecord getRecord(int index) { return _records[index]; } public EncryptedBuildRecord getRecord(int index) { return _records[index]; }
/** @since 0.7.12 */ /** @since 0.7.12 */

View File

@@ -11,6 +11,9 @@ 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.EncryptedBuildRecord;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.ShortEncryptedBuildRecord;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@@ -32,14 +35,17 @@ public abstract class BuildMessageGenerator {
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, RouterContext ctx, PublicKey peerKey) { long replyTunnel, RouterContext ctx, PublicKey peerKey) {
int mtype = msg.getType();
boolean isShort = mtype == InboundTunnelBuildMessage.MESSAGE_TYPE || mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
EncryptedBuildRecord erec; EncryptedBuildRecord erec;
if (peerKey != null) { if (peerKey != null) {
boolean isEC = peerKey.getType() == EncType.ECIES_X25519; boolean isEC = peerKey.getType() == EncType.ECIES_X25519;
BuildRequestRecord req; BuildRequestRecord req;
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, isEC); //// TODO if we're the OBEP
req = createUnencryptedRecord(ctx, cfg, hop, replyRouter, replyTunnel, isEC, isShort);
else else
req = createUnencryptedRecord(ctx, cfg, hop, null, -1, isEC); req = createUnencryptedRecord(ctx, cfg, hop, null, -1, isEC, isShort);
if (req == null) if (req == null)
throw new IllegalArgumentException("hop bigger than config"); throw new IllegalArgumentException("hop bigger than config");
Hash peer = cfg.getPeer(hop); Hash peer = cfg.getPeer(hop);
@@ -50,26 +56,31 @@ public abstract class BuildMessageGenerator {
erec = req.encryptRecord(ctx, peerKey, peer); erec = req.encryptRecord(ctx, peerKey, peer);
} }
} else { } else {
byte encrypted[] = new byte[TunnelBuildMessage.RECORD_SIZE]; int len = isShort ? ShortTunnelBuildMessage.SHORT_RECORD_SIZE : TunnelBuildMessage.RECORD_SIZE;
byte encrypted[] = new byte[len];
if (cfg.isInbound() && hop + 1 == cfg.getLength()) { // IBEP if (cfg.isInbound() && hop + 1 == cfg.getLength()) { // IBEP
System.arraycopy(cfg.getPeer(hop).getData(), 0, encrypted, 0, BuildRequestRecord.PEER_SIZE); System.arraycopy(cfg.getPeer(hop).getData(), 0, encrypted, 0, BuildRequestRecord.PEER_SIZE);
ctx.random().nextBytes(encrypted, BuildRequestRecord.PEER_SIZE, TunnelBuildMessage.RECORD_SIZE - BuildRequestRecord.PEER_SIZE); ctx.random().nextBytes(encrypted, BuildRequestRecord.PEER_SIZE, len - BuildRequestRecord.PEER_SIZE);
byte[] h = new byte[Hash.HASH_LENGTH]; byte[] h = new byte[Hash.HASH_LENGTH];
ctx.sha().calculateHash(encrypted, 0, TunnelBuildMessage.RECORD_SIZE, h, 0); ctx.sha().calculateHash(encrypted, 0, len, h, 0);
cfg.setBlankHash(new Hash(h)); cfg.setBlankHash(new Hash(h));
} else { } else {
ctx.random().nextBytes(encrypted); ctx.random().nextBytes(encrypted);
} }
erec = new EncryptedBuildRecord(encrypted); erec = isShort ? new ShortEncryptedBuildRecord(encrypted) : new EncryptedBuildRecord(encrypted);
} }
msg.setRecord(recordNum, erec); msg.setRecord(recordNum, erec);
} }
/** /**
* Returns null if hop >= cfg.length * Returns null if hop >= cfg.length
*
* @param isEC must be true if isShort is true
* @param isShort short EC record
*/ */
private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop, private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop,
Hash replyRouter, long replyTunnel, boolean isEC) { Hash replyRouter, long replyTunnel, boolean isEC,
boolean isShort) {
if (hop < cfg.getLength()) { if (hop < cfg.getLength()) {
// ok, now lets fill in some data // ok, now lets fill in some data
HopConfig hopConfig = cfg.getConfig(hop); HopConfig hopConfig = cfg.getConfig(hop);
@@ -114,9 +125,15 @@ public abstract class BuildMessageGenerator {
BuildRequestRecord rec; BuildRequestRecord rec;
if (isEC) { if (isEC) {
// TODO pass properties from cfg // TODO pass properties from cfg
rec = new BuildRequestRecord(ctx, recvTunnelId, nextTunnelId, nextPeer, if (isShort) {
nextMsgId, layerKey, ivKey, replyKey, rec = new BuildRequestRecord(ctx, recvTunnelId, nextTunnelId, nextPeer,
iv, isInGW, isOutEnd, EmptyProperties.INSTANCE); nextMsgId, layerKey, ivKey, replyKey,
iv, isInGW, isOutEnd, EmptyProperties.INSTANCE);
} else {
rec = new BuildRequestRecord(ctx, recvTunnelId, nextTunnelId, nextPeer,
nextMsgId,
isInGW, isOutEnd, EmptyProperties.INSTANCE);
}
} else { } else {
rec = new BuildRequestRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer, rec = new BuildRequestRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer,
nextMsgId, layerKey, ivKey, replyKey, nextMsgId, layerKey, ivKey, replyKey,
@@ -138,6 +155,9 @@ public abstract class BuildMessageGenerator {
*/ */
public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg, public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg,
TunnelCreatorConfig cfg, List<Integer> order) { TunnelCreatorConfig cfg, List<Integer> order) {
////// TODO check msg type for short (STBM or ITBM)
int mtype = msg.getType();
boolean isShort = mtype == InboundTunnelBuildMessage.MESSAGE_TYPE || mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
// 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++) {
EncryptedBuildRecord rec = msg.getRecord(i); EncryptedBuildRecord rec = msg.getRecord(i);

View File

@@ -59,6 +59,7 @@ class OutboundGatewayMessage extends PendingGatewayMessage implements CDPQEntry
return 1000; return 1000;
// building new IB tunnel // building new IB tunnel
case ShortTunnelBuildMessage.MESSAGE_TYPE:
case TunnelBuildMessage.MESSAGE_TYPE: case TunnelBuildMessage.MESSAGE_TYPE:
case VariableTunnelBuildMessage.MESSAGE_TYPE: case VariableTunnelBuildMessage.MESSAGE_TYPE:
return 500; return 500;
@@ -78,6 +79,8 @@ class OutboundGatewayMessage extends PendingGatewayMessage implements CDPQEntry
// these shouldn't go into a OBGW // these shouldn't go into a OBGW
case DatabaseSearchReplyMessage.MESSAGE_TYPE: case DatabaseSearchReplyMessage.MESSAGE_TYPE:
case DataMessage.MESSAGE_TYPE: case DataMessage.MESSAGE_TYPE:
case InboundTunnelBuildMessage.MESSAGE_TYPE:
case OutboundTunnelBuildReplyMessage.MESSAGE_TYPE:
case TunnelBuildReplyMessage.MESSAGE_TYPE: case TunnelBuildReplyMessage.MESSAGE_TYPE:
case TunnelDataMessage.MESSAGE_TYPE: case TunnelDataMessage.MESSAGE_TYPE:
case TunnelGatewayMessage.MESSAGE_TYPE: case TunnelGatewayMessage.MESSAGE_TYPE:

View File

@@ -11,6 +11,9 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.TunnelId; import net.i2p.data.TunnelId;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.OutboundTunnelBuildReplyMessage;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildReplyMessage; import net.i2p.data.i2np.TunnelBuildReplyMessage;
import net.i2p.data.i2np.TunnelDataMessage; import net.i2p.data.i2np.TunnelDataMessage;
@@ -774,13 +777,15 @@ public class TunnelDispatcher implements Service {
float factor; float factor;
if (loc == Location.OBEP) { if (loc == Location.OBEP) {
// we don't need to check for VTBRM/TBRM as that happens at tunnel creation // we don't need to check for VTBRM/TBRM as that happens at tunnel creation
if (type == VariableTunnelBuildMessage.MESSAGE_TYPE || type == TunnelBuildMessage.MESSAGE_TYPE) if (type == VariableTunnelBuildMessage.MESSAGE_TYPE || type == TunnelBuildMessage.MESSAGE_TYPE ||
type == ShortTunnelBuildMessage.MESSAGE_TYPE)
factor = 1 / 1.5f; factor = 1 / 1.5f;
else else
factor = 1.5f; factor = 1.5f;
} else if (loc == Location.IBGW) { } else if (loc == Location.IBGW) {
// we don't need to check for VTBM/TBM as that happens at tunnel creation // we don't need to check for VTBM/TBM as that happens at tunnel creation
if (type == VariableTunnelBuildReplyMessage.MESSAGE_TYPE || type == TunnelBuildReplyMessage.MESSAGE_TYPE) if (type == VariableTunnelBuildReplyMessage.MESSAGE_TYPE || type == TunnelBuildReplyMessage.MESSAGE_TYPE ||
type == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE || type == InboundTunnelBuildMessage.MESSAGE_TYPE)
factor = 1 / (1.5f * 1.5f * 1.5f); factor = 1 / (1.5f * 1.5f * 1.5f);
else else
factor = 1 / 1.5f; factor = 1 / 1.5f;

View File

@@ -13,11 +13,15 @@ import net.i2p.data.EmptyProperties;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.router.RouterIdentity; import net.i2p.data.router.RouterIdentity;
import net.i2p.data.router.RouterInfo; import net.i2p.data.router.RouterInfo;
import net.i2p.data.SessionKey;
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.EncryptedBuildRecord;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.OutboundTunnelBuildReplyMessage;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildReplyMessage; import net.i2p.data.i2np.TunnelBuildReplyMessage;
import net.i2p.data.i2np.TunnelGatewayMessage; import net.i2p.data.i2np.TunnelGatewayMessage;
@@ -28,6 +32,8 @@ import net.i2p.router.Job;
import net.i2p.router.JobImpl; import net.i2p.router.JobImpl;
import net.i2p.router.OutNetMessage; import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.crypto.ratchet.RatchetSessionTag;
import net.i2p.router.networkdb.kademlia.MessageWrapper;
import net.i2p.router.peermanager.TunnelHistory; import net.i2p.router.peermanager.TunnelHistory;
import net.i2p.router.tunnel.BuildMessageProcessor; import net.i2p.router.tunnel.BuildMessageProcessor;
import net.i2p.router.tunnel.BuildReplyHandler; import net.i2p.router.tunnel.BuildReplyHandler;
@@ -166,6 +172,9 @@ class BuildHandler implements Runnable {
ctx.inNetMessagePool().registerHandlerJobBuilder(TunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(TunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(InboundTunnelBuildMessage.MESSAGE_TYPE, tbmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(ShortTunnelBuildMessage.MESSAGE_TYPE, tbmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(OutboundTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
} }
/** /**
@@ -722,6 +731,21 @@ class BuildHandler implements Runnable {
_log.warn("Dropping build request, we are the previous hop: " + req); _log.warn("Dropping build request, we are the previous hop: " + req);
return; return;
} }
if (state.msg.getType() == InboundTunnelBuildMessage.MESSAGE_TYPE) {
// can only be at IBGW
_context.statManager().addRateData("tunnel.rejectHostile", 1);
if (_log.shouldWarn())
_log.warn("Dropping ITBM, we are not IBGW: " + req);
return;
}
} else {
if (state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE) {
// cannot be at IBGW
_context.statManager().addRateData("tunnel.rejectHostile", 1);
if (_log.shouldWarn())
_log.warn("Dropping STBM, we are IBGW: " + req);
return;
}
} }
if ((!isOutEnd) && (!isInGW)) { if ((!isOutEnd) && (!isInGW)) {
// Previous and next hop the same? Don't help somebody be evil. Drop it without a reply. // Previous and next hop the same? Don't help somebody be evil. Drop it without a reply.
@@ -948,20 +972,44 @@ class BuildHandler implements Runnable {
if (isEC) { if (isEC) {
// TODO options // TODO options
Properties props = EmptyProperties.INSTANCE; Properties props = EmptyProperties.INSTANCE;
reply = BuildResponseRecord.create(_context, response, req.getChaChaReplyKey(), req.getChaChaReplyAD(), props); if (state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE) {
if (isOutEnd) {
// reply will be sent in plaintext in a OTBRM, see below
reply = null;
} else {
// short build record
reply = BuildResponseRecord.createShort(_context, response, req.getChaChaReplyKey(), req.getChaChaReplyAD(), props);
}
} else {
reply = BuildResponseRecord.create(_context, response, req.getChaChaReplyKey(), req.getChaChaReplyAD(), props);
}
} else { } else {
reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId()); 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++) { ShortTunnelBuildMessage stbm = null;
if (state.msg.getRecord(j) == null) { if (state.msg.getType() == InboundTunnelBuildMessage.MESSAGE_TYPE) {
ourSlot = j; // IBGW only (enforced above)
state.msg.setRecord(j, reply); // Create a ShortTunnelBuildMessage and populate it for sending
//if (_log.shouldLog(Log.DEBUG)) InboundTunnelBuildMessage itbm = (InboundTunnelBuildMessage) state.msg;
// _log.debug("Full reply record for slot " + ourSlot + "/" + ourId + "/" + nextId + "/" + req.readReplyMessageId() ourSlot = itbm.getPlaintextSlot();
// + ": " + Base64.encode(reply)); stbm = new ShortTunnelBuildMessage(_context, records);
break; for (int j = 0; j < records; j++) {
if (j == ourSlot)
stbm.setRecord(j, reply);
else
stbm.setRecord(j, itbm.getRecord(j));
}
} else {
for (int j = 0; j < records; j++) {
if (state.msg.getRecord(j) == null) {
ourSlot = j;
if (!(isOutEnd && state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE))
state.msg.setRecord(j, reply);
// else reply will be sent in plaintext
break;
}
} }
} }
@@ -973,9 +1021,14 @@ class BuildHandler implements Runnable {
// now actually send the response // now actually send the response
long expires = now + NEXT_HOP_SEND_TIMEOUT; long expires = now + NEXT_HOP_SEND_TIMEOUT;
if (!isOutEnd) { if (!isOutEnd) {
state.msg.setUniqueId(req.readReplyMessageId()); TunnelBuildMessage nextMessage;
state.msg.setMessageExpiration(expires); if (stbm != null)
OutNetMessage msg = new OutNetMessage(_context, state.msg, expires, PRIORITY, nextPeerInfo); nextMessage = stbm;
else
nextMessage = state.msg;
nextMessage.setUniqueId(req.readReplyMessageId());
nextMessage.setMessageExpiration(expires);
OutNetMessage msg = new OutNetMessage(_context, nextMessage, expires, PRIORITY, nextPeerInfo);
if (response == 0) if (response == 0)
msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg)); msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
_context.outNetMessagePool().add(msg); _context.outNetMessagePool().add(msg);
@@ -984,16 +1037,36 @@ class BuildHandler implements Runnable {
// send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage // send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage
// (enough layers jrandom?) // (enough layers jrandom?)
TunnelBuildReplyMessage replyMsg; TunnelBuildReplyMessage replyMsg;
if (records == TunnelBuildMessage.MAX_RECORD_COUNT) if (state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE) {
OutboundTunnelBuildReplyMessage otbrm = new OutboundTunnelBuildReplyMessage(_context, records);
otbrm.setPlaintextRecord(ourSlot, null); // TODO
replyMsg = otbrm;
} else if (records == TunnelBuildMessage.MAX_RECORD_COUNT) {
replyMsg = new TunnelBuildReplyMessage(_context); replyMsg = new TunnelBuildReplyMessage(_context);
else } else {
replyMsg = new VariableTunnelBuildReplyMessage(_context, records); replyMsg = new VariableTunnelBuildReplyMessage(_context, records);
for (int i = 0; i < records; i++) }
for (int i = 0; i < records; i++) {
replyMsg.setRecord(i, state.msg.getRecord(i)); replyMsg.setRecord(i, state.msg.getRecord(i));
}
replyMsg.setUniqueId(req.readReplyMessageId()); replyMsg.setUniqueId(req.readReplyMessageId());
replyMsg.setMessageExpiration(expires); replyMsg.setMessageExpiration(expires);
I2NPMessage outMessage;
if (state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE) {
// garlic encrypt
SessionKey sk = null; // TODO
RatchetSessionTag st = null; // TODO
outMessage = MessageWrapper.wrap(_context, replyMsg, sk, st);
if (outMessage == null) {
if (_log.shouldWarn())
_log.warn("OTBRM encrypt fail");
return;
}
} else {
outMessage = replyMsg;
}
TunnelGatewayMessage m = new TunnelGatewayMessage(_context); TunnelGatewayMessage m = new TunnelGatewayMessage(_context);
m.setMessage(replyMsg); m.setMessage(outMessage);
m.setMessageExpiration(expires); m.setMessageExpiration(expires);
m.setTunnelId(new TunnelId(nextId)); m.setTunnelId(new TunnelId(nextId));
if (_context.routerHash().equals(nextPeer)) { if (_context.routerHash().equals(nextPeer)) {

View File

@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import net.i2p.crypto.EncType;
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;
@@ -11,6 +12,7 @@ import net.i2p.data.router.RouterInfo;
import net.i2p.data.TunnelId; import net.i2p.data.TunnelId;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.InboundTunnelBuildMessage; import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildMessage; import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.data.i2np.VariableTunnelBuildMessage; import net.i2p.data.i2np.VariableTunnelBuildMessage;
import net.i2p.router.JobImpl; import net.i2p.router.JobImpl;
@@ -31,8 +33,9 @@ import net.i2p.util.VersionComparator;
*/ */
abstract class BuildRequestor { abstract class BuildRequestor {
private static final List<Integer> ORDER = new ArrayList<Integer>(TunnelBuildMessage.MAX_RECORD_COUNT); private static final List<Integer> ORDER = new ArrayList<Integer>(TunnelBuildMessage.MAX_RECORD_COUNT);
//private static final String MIN_VARIABLE_VERSION = "0.7.12"; private static final String MIN_NEWTBM_VERSION = "0.9.51";
private static final boolean SEND_VARIABLE = true; private static final boolean SEND_VARIABLE = true;
private static final boolean SEND_SHORT = false;
private static final int SHORT_RECORDS = 4; private static final int SHORT_RECORDS = 4;
private static final List<Integer> SHORT_ORDER = new ArrayList<Integer>(SHORT_RECORDS); private static final List<Integer> SHORT_ORDER = new ArrayList<Integer>(SHORT_RECORDS);
/** 5 (~2600 bytes) fits nicely in 3 tunnel messages */ /** 5 (~2600 bytes) fits nicely in 3 tunnel messages */
@@ -268,18 +271,18 @@ abstract class BuildRequestor {
return true; return true;
} }
/** @since 0.7.12 */ /**
/**** * @since 0.9.51
we can assume everybody supports variable now... */
keep this here for the next time we change the build protocol private static boolean supportsShortTBM(RouterContext ctx, Hash h) {
private static boolean supportsVariable(RouterContext ctx, Hash h) {
RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(h); RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(h);
if (ri == null) if (ri == null)
return false; return false;
if (ri.getIdentity().getPublicKey().getType() != EncType.ECIES_X25519)
return false;
String v = ri.getVersion(); String v = ri.getVersion();
return VersionComparator.comp(v, MIN_VARIABLE_VERSION) >= 0; return VersionComparator.comp(v, MIN_NEWTBM_VERSION) >= 0;
} }
****/
/** /**
* If the tunnel is short enough, and everybody in the tunnel, and the * If the tunnel is short enough, and everybody in the tunnel, and the
@@ -294,53 +297,54 @@ keep this here for the next time we change the build protocol
long replyTunnel = 0; long replyTunnel = 0;
Hash replyRouter; Hash replyRouter;
boolean useVariable = SEND_VARIABLE && cfg.getLength() <= MEDIUM_RECORDS; boolean useVariable = SEND_VARIABLE && cfg.getLength() <= MEDIUM_RECORDS;
boolean useShortTBM = SEND_SHORT && ctx.keyManager().getPublicKey().getType() == EncType.ECIES_X25519;
if (cfg.isInbound()) { if (cfg.isInbound()) {
//replyTunnel = 0; // as above //replyTunnel = 0; // as above
replyRouter = ctx.routerHash(); replyRouter = ctx.routerHash();
/**** if (useShortTBM) {
we can assume everybody supports variable now... // check all the tunnel peers except ourselves
keep this here for the next time we change the build protocol for (int i = 0; i < cfg.getLength() - 1; i++) {
if (useVariable) { if (!supportsShortTBM(ctx, cfg.getPeer(i))) {
// check the reply OBEP and all the tunnel peers except ourselves useShortTBM = false;
if (!supportsVariable(ctx, pairedTunnel.getPeer(pairedTunnel.getLength() - 1))) { break;
useVariable = false;
} else {
for (int i = 0; i < cfg.getLength() - 1; i++) {
if (!supportsVariable(ctx, cfg.getPeer(i))) {
useVariable = false;
break;
}
} }
} }
} }
****/
} else { } else {
replyTunnel = pairedTunnel.getReceiveTunnelId(0).getTunnelId(); replyTunnel = pairedTunnel.getReceiveTunnelId(0).getTunnelId();
replyRouter = pairedTunnel.getPeer(0); replyRouter = pairedTunnel.getPeer(0);
/**** if (useShortTBM) {
we can assume everybody supports variable now // check all the tunnel peers except ourselves
keep this here for the next time we change the build protocol for (int i = 1; i < cfg.getLength() - 1; i++) {
if (useVariable) { if (!supportsShortTBM(ctx, cfg.getPeer(i))) {
// check the reply IBGW and all the tunnel peers except ourselves useShortTBM = false;
if (!supportsVariable(ctx, replyRouter)) { break;
useVariable = false;
} else {
for (int i = 1; i < cfg.getLength() - 1; i++) {
if (!supportsVariable(ctx, cfg.getPeer(i))) {
useVariable = false;
break;
}
} }
} }
} }
****/
} }
// populate and encrypt the message // populate and encrypt the message
TunnelBuildMessage msg; TunnelBuildMessage msg;
List<Integer> order; List<Integer> order;
if (useVariable) { if (useShortTBM) {
int len;
if (cfg.getLength() <= SHORT_RECORDS) {
len = SHORT_RECORDS;
order = new ArrayList<Integer>(SHORT_ORDER);
} else if (cfg.getLength() <= MEDIUM_RECORDS) {
len = MEDIUM_RECORDS;
order = new ArrayList<Integer>(MEDIUM_ORDER);
} else {
len = TunnelBuildMessage.MAX_RECORD_COUNT;
order = new ArrayList<Integer>(ORDER);
}
if (cfg.isInbound())
msg = new InboundTunnelBuildMessage(ctx, len);
else
msg = new ShortTunnelBuildMessage(ctx, len);
} else if (useVariable) {
if (cfg.getLength() <= SHORT_RECORDS) { if (cfg.getLength() <= SHORT_RECORDS) {
msg = new VariableTunnelBuildMessage(ctx, SHORT_RECORDS); msg = new VariableTunnelBuildMessage(ctx, SHORT_RECORDS);
order = new ArrayList<Integer>(SHORT_ORDER); order = new ArrayList<Integer>(SHORT_ORDER);