From bb19fcdac353aa4d2fa01b6f57dcf9f03183a545 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Tue, 22 Jun 2021 09:06:40 -0400
Subject: [PATCH] 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
---
 .../net/i2p/data/i2np/BuildRequestRecord.java | 17 +++---
 .../net/i2p/data/i2np/I2NPMessageImpl.java    |  3 -
 .../data/i2np/ShortTunnelBuildMessage.java    |  2 +-
 .../router/tunnel/OutboundGatewayMessage.java |  1 -
 .../i2p/router/tunnel/TunnelDispatcher.java   |  3 +-
 .../i2p/router/tunnel/pool/BuildHandler.java  | 57 +++----------------
 .../tunnel/pool/BuildMessageGenerator.java    |  6 +-
 .../router/tunnel/pool/BuildReplyHandler.java |  3 +-
 .../router/tunnel/pool/BuildRequestor.java    | 10 +---
 .../pool/BuildMessageTestStandalone.java      | 19 +------
 10 files changed, 29 insertions(+), 92 deletions(-)

diff --git a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
index d70d92e3a4..595b140b6a 100644
--- a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
+++ b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
@@ -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
      */
diff --git a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
index 9c8a636a33..46a8dde39b 100644
--- a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
+++ b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
@@ -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
diff --git a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java b/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java
index 2fc6e943cc..4740e1c84c 100644
--- a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java
+++ b/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java
@@ -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) {
diff --git a/router/java/src/net/i2p/router/tunnel/OutboundGatewayMessage.java b/router/java/src/net/i2p/router/tunnel/OutboundGatewayMessage.java
index 48d0ccd775..eb8b27db8f 100644
--- a/router/java/src/net/i2p/router/tunnel/OutboundGatewayMessage.java
+++ b/router/java/src/net/i2p/router/tunnel/OutboundGatewayMessage.java
@@ -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:
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
index f7ffc18c78..96877b2ee0 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
@@ -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;
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
index 87bf37c3ce..67665342b2 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
@@ -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);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildMessageGenerator.java b/router/java/src/net/i2p/router/tunnel/pool/BuildMessageGenerator.java
index 4e7aa3e059..1ab9117db9 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildMessageGenerator.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildMessageGenerator.java
@@ -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
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildReplyHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildReplyHandler.java
index 92726222eb..28f1ba2b6b 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildReplyHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildReplyHandler.java
@@ -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())) {
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
index bba144f18c..360bb5e5fa 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
@@ -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);
diff --git a/router/java/test/junit/net/i2p/router/tunnel/pool/BuildMessageTestStandalone.java b/router/java/test/junit/net/i2p/router/tunnel/pool/BuildMessageTestStandalone.java
index e45897881d..0e60883fdd 100644
--- a/router/java/test/junit/net/i2p/router/tunnel/pool/BuildMessageTestStandalone.java
+++ b/router/java/test/junit/net/i2p/router/tunnel/pool/BuildMessageTestStandalone.java
@@ -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);
         
-- 
GitLab