Tunnels: Changes for new build messages (Prop. 157)

- Remove ITBM, change record length from 236 to 218 bytes
- Fix check of blank record in BuildReplyHandler
- Fix offset constants for short record in BuildRequestRecord
- Fix BuildMessageTestStandalone test 6 (short inbound)
- ITBM class removal TODO
This commit is contained in:
zzz
2021-06-22 09:06:40 -04:00
parent d71a472367
commit bb19fcdac3
10 changed files with 29 additions and 92 deletions

View File

@@ -94,7 +94,7 @@ import net.i2p.router.RouterContext;
*
* ECIES short record format, ref: proposal 157:
*
* Holds the unencrypted 172-byte tunnel request record,
* Holds the unencrypted 154-byte tunnel request record,
* with a constructor for ECIES decryption and a method for ECIES encryption.
* Iterative AES encryption/decryption is done elsewhere.
*
@@ -111,15 +111,15 @@ import net.i2p.router.RouterContext;
* 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
* bytes x-153: 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
* bytes 48-201: ChaCha20 encrypted BuildRequestRecord
* bytes 202-217: Poly1305 MAC
* </pre>
*
*/
@@ -189,10 +189,11 @@ public class BuildRequestRecord {
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_EXPIRATION_SHORT = OFF_REQ_TIME_EC_SHORT + 4;
private static final int OFF_SEND_MSG_ID_EC_SHORT = OFF_EXPIRATION_SHORT + 4;
private static final int OFF_OPTIONS_SHORT = OFF_SEND_MSG_ID_EC_SHORT + 4;
private static final int LENGTH_EC_SHORT = 172;
// 16 byte trunc. hash, 32 byte eph. key, 16 byte MAC
private static final int LENGTH_EC_SHORT = ShortTunnelBuildMessage.SHORT_RECORD_SIZE - (16 + 32 + 16);
private static final int MAX_OPTIONS_LENGTH_SHORT = LENGTH_EC_SHORT - OFF_OPTIONS_SHORT; // includes options length
private static final boolean TEST = false;
@@ -644,7 +645,7 @@ public class BuildRequestRecord {
* @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
* @param options 98 bytes max when serialized
* @since 0.9.51
* @throws IllegalArgumentException if options too long
*/

View File

@@ -439,9 +439,6 @@ public abstract class I2NPMessageImpl implements I2NPMessage {
case VariableTunnelBuildReplyMessage.MESSAGE_TYPE:
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

View File

@@ -10,7 +10,7 @@ import net.i2p.I2PAppContext;
*/
public class ShortTunnelBuildMessage extends TunnelBuildMessage {
public static final int MESSAGE_TYPE = 25;
public static final int SHORT_RECORD_SIZE = 236;
public static final int SHORT_RECORD_SIZE = 218;
/** zero record count, will be set with readMessage() */
public ShortTunnelBuildMessage(I2PAppContext context) {

View File

@@ -79,7 +79,6 @@ class OutboundGatewayMessage extends PendingGatewayMessage implements CDPQEntry
// these shouldn't go into a OBGW
case DatabaseSearchReplyMessage.MESSAGE_TYPE:
case DataMessage.MESSAGE_TYPE:
case InboundTunnelBuildMessage.MESSAGE_TYPE:
case OutboundTunnelBuildReplyMessage.MESSAGE_TYPE:
case TunnelBuildReplyMessage.MESSAGE_TYPE:
case TunnelDataMessage.MESSAGE_TYPE:

View File

@@ -11,7 +11,6 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.TunnelId;
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;
@@ -785,7 +784,7 @@ public class TunnelDispatcher implements Service {
} else if (loc == Location.IBGW) {
// we don't need to check for VTBM/TBM as that happens at tunnel creation
if (type == VariableTunnelBuildReplyMessage.MESSAGE_TYPE || type == TunnelBuildReplyMessage.MESSAGE_TYPE ||
type == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE || type == InboundTunnelBuildMessage.MESSAGE_TYPE)
type == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE)
factor = 1 / (1.5f * 1.5f * 1.5f);
else
factor = 1 / 1.5f;

View File

@@ -19,7 +19,6 @@ import net.i2p.data.i2np.BuildRequestRecord;
import net.i2p.data.i2np.BuildResponseRecord;
import net.i2p.data.i2np.EncryptedBuildRecord;
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.ShortTunnelBuildReplyMessage;
@@ -173,7 +172,6 @@ class BuildHandler implements Runnable {
ctx.inNetMessagePool().registerHandlerJobBuilder(TunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb);
ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb);
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);
}
@@ -736,21 +734,6 @@ class BuildHandler implements Runnable {
_log.warn("Dropping build request, we are the previous hop: " + req);
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)) {
// Previous and next hop the same? Don't help somebody be evil. Drop it without a reply.
@@ -997,33 +980,13 @@ class BuildHandler implements Runnable {
}
int records = state.msg.getRecordCount();
int ourSlot = -1;
ShortTunnelBuildMessage stbm = null;
if (state.msg.getType() == InboundTunnelBuildMessage.MESSAGE_TYPE) {
if (!HANDLE_SHORT) {
if (_log.shouldWarn())
_log.warn("Unsupported ITBM");
return;
}
// IBGW only (enforced above)
// Create a ShortTunnelBuildMessage and populate it for sending
InboundTunnelBuildMessage itbm = (InboundTunnelBuildMessage) state.msg;
ourSlot = itbm.getPlaintextSlot();
stbm = new ShortTunnelBuildMessage(_context, records);
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;
}
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;
}
}
@@ -1035,11 +998,7 @@ class BuildHandler implements Runnable {
// now actually send the response
long expires = now + NEXT_HOP_SEND_TIMEOUT;
if (!isOutEnd) {
TunnelBuildMessage nextMessage;
if (stbm != null)
nextMessage = stbm;
else
nextMessage = state.msg;
TunnelBuildMessage nextMessage = state.msg;
nextMessage.setUniqueId(req.readReplyMessageId());
nextMessage.setMessageExpiration(expires);
OutNetMessage msg = new OutNetMessage(_context, nextMessage, expires, PRIORITY, nextPeerInfo);

View File

@@ -12,7 +12,6 @@ import net.i2p.data.SessionKey;
import net.i2p.data.i2np.BuildRequestRecord;
import net.i2p.data.i2np.EncryptedBuildRecord;
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;
@@ -41,7 +40,7 @@ abstract class BuildMessageGenerator {
TunnelCreatorConfig cfg, Hash replyRouter,
long replyTunnel, RouterContext ctx, PublicKey peerKey) {
int mtype = msg.getType();
boolean isShort = mtype == InboundTunnelBuildMessage.MESSAGE_TYPE || mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
boolean isShort = mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
EncryptedBuildRecord erec;
if (peerKey != null) {
boolean isEC = peerKey.getType() == EncType.ECIES_X25519;
@@ -56,6 +55,7 @@ abstract class BuildMessageGenerator {
Hash peer = cfg.getPeer(hop);
if (isEC) {
erec = req.encryptECIESRecord(ctx, peerKey, peer);
// TODO if isShort, set derived keys in coonfig
cfg.setChaChaReplyKeys(hop, req.getChaChaReplyKey(), req.getChaChaReplyAD());
} else {
erec = req.encryptRecord(ctx, peerKey, peer);
@@ -163,7 +163,7 @@ abstract class BuildMessageGenerator {
public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg,
TunnelCreatorConfig cfg, List<Integer> order) {
int mtype = msg.getType();
boolean isShort = mtype == InboundTunnelBuildMessage.MESSAGE_TYPE || mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
boolean isShort = mtype == ShortTunnelBuildMessage.MESSAGE_TYPE;
int size = isShort ? ShortTunnelBuildMessage.SHORT_RECORD_SIZE : TunnelBuildMessage.RECORD_SIZE;
byte[] chachaIV = isShort ? new byte[12] : null;
// encrypt the records so that the right elements will be visible at the right time

View File

@@ -62,7 +62,8 @@ class BuildReplyHandler {
log.debug(reply.getUniqueId() + ": skipping record " + i + "/" + hop + " for: " + cfg);
if (cfg.isInbound() && hop + 1 == cfg.getLength()) { // IBEP
byte[] h1 = new byte[Hash.HASH_LENGTH];
ctx.sha().calculateHash(reply.getRecord(i).getData(), 0, TunnelBuildReplyMessage.RECORD_SIZE, h1, 0);
byte[] data = reply.getRecord(i).getData();
ctx.sha().calculateHash(data, 0, data.length, h1, 0);
// get stored hash put here by BuildMessageGenerator
Hash h2 = cfg.getBlankHash();
if (h2 != null && DataHelper.eq(h1, h2.getData())) {

View File

@@ -11,7 +11,6 @@ import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterInfo;
import net.i2p.data.TunnelId;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.TunnelBuildMessage;
import net.i2p.data.i2np.VariableTunnelBuildMessage;
@@ -211,8 +210,8 @@ abstract class BuildRequestor {
//long beforeDispatch = System.currentTimeMillis();
if (cfg.isInbound()) {
Hash ibgw = cfg.getPeer(0);
if (msg.getType() == InboundTunnelBuildMessage.MESSAGE_TYPE) {
// ITBM is garlic encrypted to the IBGW, to hide it from the OBEP
if (msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE) {
// STBM is garlic encrypted to the IBGW, to hide it from the OBEP
RouterInfo peer = ctx.netDb().lookupRouterInfoLocally(ibgw);
if (peer != null) {
I2NPMessage enc = MessageWrapper.wrap(ctx, msg, peer);
@@ -339,10 +338,7 @@ abstract class BuildRequestor {
len = TunnelBuildMessage.MAX_RECORD_COUNT;
order = new ArrayList<Integer>(ORDER);
}
if (cfg.isInbound())
msg = new InboundTunnelBuildMessage(ctx, len);
else
msg = new ShortTunnelBuildMessage(ctx, len);
msg = new ShortTunnelBuildMessage(ctx, len);
} else if (useVariable) {
if (cfg.getLength() <= SHORT_RECORDS) {
msg = new VariableTunnelBuildMessage(ctx, SHORT_RECORDS);

View File

@@ -24,7 +24,6 @@ import net.i2p.data.i2np.EncryptedBuildRecord;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.I2NPMessageException;
import net.i2p.data.i2np.I2NPMessageHandler;
import net.i2p.data.i2np.InboundTunnelBuildMessage;
import net.i2p.data.i2np.OutboundTunnelBuildReplyMessage;
import net.i2p.data.i2np.ShortTunnelBuildMessage;
import net.i2p.data.i2np.ShortTunnelBuildReplyMessage;
@@ -84,20 +83,8 @@ public class BuildMessageTestStandalone extends TestCase {
// populate and encrypt the message
TunnelBuildMessage msg;
if (testType == 3) {
if (testType == 3 || testType == 6) {
msg = new ShortTunnelBuildMessage(ctx, TunnelBuildMessage.MAX_RECORD_COUNT);
} else if (testType == 6) {
InboundTunnelBuildMessage itbm = new InboundTunnelBuildMessage(ctx, TunnelBuildMessage.MAX_RECORD_COUNT);
// set plaintext record for ibgw
for (int i = 0; i < order.size(); i++) {
int hop = order.get(i).intValue();
if (hop == 0) {
// TODO
itbm.setPlaintextRecord(i, new byte[100]);
break;
}
}
msg = itbm;
} else {
msg = new TunnelBuildMessage(ctx);
}
@@ -109,9 +96,7 @@ public class BuildMessageTestStandalone extends TestCase {
PublicKey key = null;
if (hop < end)
key = _pubKeys[hop];
// don't do this for ibgw in itbm
if (testType != 6 || hop != 0)
BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
}
BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);