forked from I2P_Developers/i2p.i2p
Tunnels: Continue work on prop. 157
- Add new internal-only ShortTunnelBuildReplyMessage, for processing of STBM as a reply. - Add support for inbound tunnel tests to TunnelBuildMessageStandalone. The ITBM test is WIP. - Add checks for unset plaintext record in ITBM and OTBRM
This commit is contained in:
@@ -69,7 +69,11 @@ public class InboundTunnelBuildMessage extends TunnelBuildMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateWrittenLength() { return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE); }
|
||||
protected int calculateWrittenLength() {
|
||||
if (_plaintextRecord == null)
|
||||
throw new IllegalStateException("Plaintext record not set");
|
||||
return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
@@ -108,6 +112,8 @@ public class InboundTunnelBuildMessage extends TunnelBuildMessage {
|
||||
|
||||
@Override
|
||||
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
|
||||
if (_plaintextRecord == null)
|
||||
throw new I2NPMessageException("Plaintext record not set");
|
||||
int remaining = out.length - (curIndex + calculateWrittenLength());
|
||||
if (remaining < 0)
|
||||
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
|
||||
|
||||
@@ -128,7 +128,11 @@ public class OutboundTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateWrittenLength() { return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE); }
|
||||
protected int calculateWrittenLength() {
|
||||
if (_plaintextRecord == null)
|
||||
throw new IllegalStateException("Plaintext record not set");
|
||||
return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
@@ -167,6 +171,8 @@ public class OutboundTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
|
||||
@Override
|
||||
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
|
||||
if (_plaintextRecord == null)
|
||||
throw new I2NPMessageException("Plaintext record not set");
|
||||
int remaining = out.length - (curIndex + calculateWrittenLength());
|
||||
if (remaining < 0)
|
||||
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Internal use only, to convert an inbound STBM to a reply.
|
||||
* Never serialized/deserialized/sent/received.
|
||||
* See BuildHandler and BuildReplyHandler.
|
||||
*
|
||||
* @since 0.9.51
|
||||
*/
|
||||
public class ShortTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
/**
|
||||
* Impossible value, more than 1 byte
|
||||
*/
|
||||
public static final int MESSAGE_TYPE = 999;
|
||||
public static final int SHORT_RECORD_SIZE = ShortTunnelBuildMessage.SHORT_RECORD_SIZE;
|
||||
|
||||
public ShortTunnelBuildReplyMessage(I2PAppContext context, int 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
|
||||
protected int calculateWrittenLength() { return 0; }
|
||||
|
||||
@Override
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@Override
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@Override
|
||||
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
buf.append("[ShortTunnelBuildReplyMessage: " +
|
||||
"\n\tID: ").append(getUniqueId())
|
||||
.append("\n\tRecords: ").append(getRecordCount())
|
||||
.append(']');
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ 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;
|
||||
import net.i2p.data.i2np.TunnelBuildMessage;
|
||||
import net.i2p.data.i2np.TunnelBuildReplyMessage;
|
||||
import net.i2p.data.i2np.TunnelGatewayMessage;
|
||||
@@ -566,12 +567,15 @@ class BuildHandler implements Runnable {
|
||||
private void handleRequestAsInboundEndpoint(BuildEndMessageState state) {
|
||||
int records = state.msg.getRecordCount();
|
||||
TunnelBuildReplyMessage msg;
|
||||
if (records == TunnelBuildMessage.MAX_RECORD_COUNT)
|
||||
if (state.msg.getType() == ShortTunnelBuildMessage.MESSAGE_TYPE)
|
||||
msg = new ShortTunnelBuildReplyMessage(_context, records);
|
||||
else if (records == TunnelBuildMessage.MAX_RECORD_COUNT)
|
||||
msg = new TunnelBuildReplyMessage(_context);
|
||||
else
|
||||
msg = new VariableTunnelBuildReplyMessage(_context, records);
|
||||
for (int i = 0; i < records; i++)
|
||||
for (int i = 0; i < records; i++) {
|
||||
msg.setRecord(i, state.msg.getRecord(i));
|
||||
}
|
||||
msg.setUniqueId(state.msg.getUniqueId());
|
||||
handleReply(msg, state.cfg, System.currentTimeMillis() - state.recvTime);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.i2p.data.i2np.BuildResponseRecord;
|
||||
import net.i2p.data.i2np.EncryptedBuildRecord;
|
||||
import net.i2p.data.i2np.OutboundTunnelBuildReplyMessage;
|
||||
import net.i2p.data.i2np.ShortEncryptedBuildRecord;
|
||||
import net.i2p.data.i2np.ShortTunnelBuildReplyMessage;
|
||||
import net.i2p.data.i2np.TunnelBuildReplyMessage;
|
||||
import net.i2p.router.tunnel.TunnelCreatorConfig;
|
||||
import net.i2p.util.Log;
|
||||
@@ -52,7 +53,6 @@ class BuildReplyHandler {
|
||||
log.error("Corrupted build reply, expected " + recordOrder.size() + " records, got " + reply.getRecordCount());
|
||||
return null;
|
||||
}
|
||||
boolean isShort = reply.getType() == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE;
|
||||
int rv[] = new int[reply.getRecordCount()];
|
||||
for (int i = 0; i < rv.length; i++) {
|
||||
int hop = recordOrder.get(i).intValue();
|
||||
@@ -89,7 +89,7 @@ class BuildReplyHandler {
|
||||
rv[i] = ok;
|
||||
}
|
||||
}
|
||||
if (isShort) {
|
||||
if (reply.getType() == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE) {
|
||||
OutboundTunnelBuildReplyMessage otbrm = (OutboundTunnelBuildReplyMessage) reply;
|
||||
rv[otbrm.getPlaintextSlot()] = otbrm.getPlaintextReply();
|
||||
}
|
||||
@@ -107,9 +107,10 @@ class BuildReplyHandler {
|
||||
*/
|
||||
private int decryptRecord(TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) {
|
||||
EncryptedBuildRecord rec = reply.getRecord(recordNum);
|
||||
boolean isShort = reply.getType() == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE;
|
||||
int type = reply.getType();
|
||||
boolean isOTBRM = type == OutboundTunnelBuildReplyMessage.MESSAGE_TYPE;
|
||||
if (rec == null) {
|
||||
if (!isShort) {
|
||||
if (!isOTBRM) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Missing record " + recordNum);
|
||||
return -1;
|
||||
@@ -135,6 +136,7 @@ class BuildReplyHandler {
|
||||
if (isEC)
|
||||
end++;
|
||||
// do we need to adjust this for the endpoint?
|
||||
boolean isShort = isOTBRM || type == ShortTunnelBuildReplyMessage.MESSAGE_TYPE;
|
||||
if (isShort) {
|
||||
byte iv[] = new byte[12];
|
||||
for (int j = start; j >= end; j--) {
|
||||
|
||||
@@ -24,8 +24,10 @@ 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;
|
||||
import net.i2p.data.i2np.TunnelBuildMessage;
|
||||
import net.i2p.data.i2np.TunnelBuildReplyMessage;
|
||||
import net.i2p.router.Router;
|
||||
@@ -53,18 +55,18 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
|
||||
public void testBuildMessage() {
|
||||
RouterContext ctx = new RouterContext(null);
|
||||
x_testBuildMessage(ctx, 1);
|
||||
x_testBuildMessage(ctx, 2);
|
||||
x_testBuildMessage(ctx, 3);
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
x_testBuildMessage(ctx, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param testType 1=ElG; 2=ECIES; 3=ECIES short outbound; 4=ECIES short inbound
|
||||
* @param testType outbound: 1=ElG; 2=ECIES; 3=ECIES short; inbound: 4-6
|
||||
*/
|
||||
private void x_testBuildMessage(RouterContext ctx, int testType) {
|
||||
Log log = ctx.logManager().getLog(getClass());
|
||||
// set our keys to avoid NPE
|
||||
KeyPair kpr = ctx.keyGenerator().generatePKIKeys(testType == 1 ? EncType.ELGAMAL_2048 : EncType.ECIES_X25519);
|
||||
KeyPair kpr = ctx.keyGenerator().generatePKIKeys((testType == 1 || testType == 4) ? EncType.ELGAMAL_2048 : EncType.ECIES_X25519);
|
||||
PublicKey k1 = kpr.getPublic();
|
||||
PrivateKey k2 = kpr.getPrivate();
|
||||
Object[] kp = ctx.keyGenerator().generateSigningKeypair();
|
||||
@@ -82,16 +84,34 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
|
||||
// populate and encrypt the message
|
||||
TunnelBuildMessage msg;
|
||||
if (testType == 3)
|
||||
if (testType == 3) {
|
||||
msg = new ShortTunnelBuildMessage(ctx, TunnelBuildMessage.MAX_RECORD_COUNT);
|
||||
else
|
||||
} 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);
|
||||
}
|
||||
int end = cfg.getLength();
|
||||
if (testType > 3)
|
||||
end--;
|
||||
for (int i = 0; i < order.size(); i++) {
|
||||
int hop = order.get(i).intValue();
|
||||
PublicKey key = null;
|
||||
if (hop < _pubKeys.length)
|
||||
if (hop < end)
|
||||
key = _pubKeys[hop];
|
||||
BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
|
||||
// don't do this for ibgw in itbm
|
||||
if (testType != 6 || hop != 0)
|
||||
BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
|
||||
}
|
||||
BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);
|
||||
|
||||
@@ -100,14 +120,14 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
"\n" + cfg +
|
||||
"\n================================================================");
|
||||
|
||||
if (testType == 3) {
|
||||
// test read/write
|
||||
if (testType == 3 || testType == 6) {
|
||||
// test read/write for new messages
|
||||
byte[] data = msg.toByteArray();
|
||||
try {
|
||||
I2NPMessage msg2 = (new I2NPMessageHandler(ctx)).readMessage(data);
|
||||
msg = (ShortTunnelBuildMessage) msg2;
|
||||
msg = (TunnelBuildMessage) msg2;
|
||||
} catch (Exception e) {
|
||||
log.error("STBM out/in fail", e);
|
||||
log.error("TBM out/in fail", e);
|
||||
assertTrue(e.toString(), false);
|
||||
}
|
||||
}
|
||||
@@ -115,8 +135,10 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
// as necessary
|
||||
|
||||
BuildMessageProcessor proc = new BuildMessageProcessor(ctx);
|
||||
// skip cfg(0) which is the gateway (us)
|
||||
for (int i = 1; i < cfg.getLength(); i++) {
|
||||
// skip cfg(0) which is the gateway (us) for outbound
|
||||
// skip cfg(end) which is the endpoint (us) for inbound
|
||||
int start = testType > 3 ? 0 : 1;
|
||||
for (int i = start; i < end; i++) {
|
||||
// this not only decrypts the current hop's record, but encrypts the other records
|
||||
// with the reply key
|
||||
BuildRequestRecord req = proc.decrypt(msg, _peers[i], _privKeys[i]);
|
||||
@@ -132,9 +154,9 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
int ourSlot = -1;
|
||||
|
||||
EncryptedBuildRecord reply;
|
||||
if (testType == 1) {
|
||||
if (testType == 1 || testType == 4) {
|
||||
reply = BuildResponseRecord.create(ctx, 0, req.readReplyKey(), req.readReplyIV(), i);
|
||||
} else if (testType == 2) {
|
||||
} else if (testType == 2 || testType == 5) {
|
||||
reply = BuildResponseRecord.create(ctx, 0, req.getChaChaReplyKey(), req.getChaChaReplyAD(), EmptyProperties.INSTANCE);
|
||||
} else {
|
||||
reply = BuildResponseRecord.createShort(ctx, 0, req.getChaChaReplyKey(), req.getChaChaReplyAD(), EmptyProperties.INSTANCE);
|
||||
@@ -156,14 +178,14 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
if (testType == 1) {
|
||||
if (testType == 1 || testType == 4) {
|
||||
log.debug("Read slot " + ourSlot + " containing hop " + i + " @ " + _peers[i].toBase64()
|
||||
+ " receives on " + ourId + " sending to " + nextId
|
||||
+ " replyKey " + Base64.encode(req.readReplyKey().getData())
|
||||
+ " replyIV " + Base64.encode(req.readReplyIV())
|
||||
+ " on " + nextPeer.toBase64()
|
||||
+ " inGW? " + isInGW + " outEnd? " + isOutEnd + " time difference " + (now-time));
|
||||
} else if (testType == 2) {
|
||||
} else if (testType == 2 || testType == 5) {
|
||||
log.debug("Read slot " + ourSlot + " containing hop " + i + " @ " + _peers[i].toBase64()
|
||||
+ " receives on " + ourId + " sending to " + nextId
|
||||
+ " replyKey " + Base64.encode(req.readReplyKey().getData())
|
||||
@@ -218,7 +240,10 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
assertTrue(e.toString(), false);
|
||||
}
|
||||
} else {
|
||||
reply = new TunnelBuildReplyMessage(ctx);
|
||||
if (testType == 6)
|
||||
reply = new ShortTunnelBuildReplyMessage(ctx, TunnelBuildMessage.MAX_RECORD_COUNT);
|
||||
else
|
||||
reply = new TunnelBuildReplyMessage(ctx);
|
||||
for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++) {
|
||||
reply.setRecord(i, msg.getRecord(i));
|
||||
}
|
||||
@@ -257,15 +282,19 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig createConfig(I2PAppContext ctx, int testType) {
|
||||
return configOutbound(ctx, testType);
|
||||
boolean isInbound = testType > 3;
|
||||
if (isInbound)
|
||||
testType -= 3;
|
||||
return createConfig(ctx, testType, isInbound);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a 3-hop (4 entries in the config) outbound tunnel.
|
||||
* The first entry in the config is the gateway (us),
|
||||
* This creates a 3-hop (4 entries in the config) tunnel.
|
||||
* The first entry in the outbound config is the gateway (us),
|
||||
* and is mostly ignored.
|
||||
* Ditto last entry in inbound config.
|
||||
*/
|
||||
private TunnelCreatorConfig configOutbound(I2PAppContext ctx, int testType) {
|
||||
private TunnelCreatorConfig createConfig(I2PAppContext ctx, int testType, boolean isInbound) {
|
||||
_peers = new Hash[4];
|
||||
_pubKeys = new PublicKey[_peers.length];
|
||||
_privKeys = new PrivateKey[_peers.length];
|
||||
@@ -279,7 +308,7 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
_privKeys[i] = kp.getPrivate();
|
||||
}
|
||||
|
||||
TunnelCreatorConfig cfg = new TCConfig(null, _peers.length, false);
|
||||
TunnelCreatorConfig cfg = new TCConfig(null, _peers.length, isInbound);
|
||||
long now = ctx.clock().now();
|
||||
// peers[] is ordered gateway first (unlike in production code)
|
||||
for (int i = 0; i < _peers.length; i++) {
|
||||
@@ -306,9 +335,9 @@ public class BuildMessageTestStandalone extends TestCase {
|
||||
RouterContext ctx = r.getContext();
|
||||
ctx.initAll();
|
||||
try {
|
||||
test.x_testBuildMessage(ctx, 1);
|
||||
test.x_testBuildMessage(ctx, 2);
|
||||
test.x_testBuildMessage(ctx, 3);
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
test.x_testBuildMessage(ctx, i);
|
||||
}
|
||||
} finally {
|
||||
ctx.logManager().flush();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user