2006-01-17 22:56:15 +00:00
|
|
|
package net.i2p.router.tunnel;
|
|
|
|
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
|
|
|
|
2013-01-04 00:33:03 +00:00
|
|
|
import junit.framework.TestCase;
|
|
|
|
|
2006-01-17 22:56:15 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
2008-07-16 13:42:54 +00:00
|
|
|
import net.i2p.data.Base64;
|
|
|
|
import net.i2p.data.ByteArray;
|
|
|
|
import net.i2p.data.Hash;
|
|
|
|
import net.i2p.data.PrivateKey;
|
|
|
|
import net.i2p.data.PublicKey;
|
|
|
|
import net.i2p.data.TunnelId;
|
|
|
|
import net.i2p.data.i2np.BuildRequestRecord;
|
|
|
|
import net.i2p.data.i2np.BuildResponseRecord;
|
|
|
|
import net.i2p.data.i2np.TunnelBuildMessage;
|
|
|
|
import net.i2p.data.i2np.TunnelBuildReplyMessage;
|
2006-01-17 22:56:15 +00:00
|
|
|
import net.i2p.util.Log;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple test to create an encrypted TunnelBuildMessage, decrypt its layers (as it would be
|
|
|
|
* during transmission), inject replies, then handle the TunnelBuildReplyMessage (unwrapping
|
|
|
|
* the reply encryption and reading the replies).
|
2013-01-05 21:52:33 +00:00
|
|
|
*
|
|
|
|
* ===
|
|
|
|
* Update 1/5/2013 :
|
|
|
|
* This test is renamed so it does not match the JUnit wildcard.
|
|
|
|
* There is something wrong with the decryption check; it doesn't look like the test takes
|
|
|
|
* into consideration the re-encryption of the records in the TunnelBuildMessage.
|
|
|
|
* Most probably the test will have to be re-written from scratch.
|
|
|
|
* --zab
|
2006-01-17 22:56:15 +00:00
|
|
|
*/
|
2013-01-05 21:52:33 +00:00
|
|
|
public class BuildMessageTestStandalone extends TestCase {
|
2006-01-17 22:56:15 +00:00
|
|
|
private Hash _peers[];
|
|
|
|
private PrivateKey _privKeys[];
|
|
|
|
private PublicKey _pubKeys[];
|
|
|
|
private Hash _replyRouter;
|
|
|
|
private long _replyTunnel;
|
|
|
|
|
2013-01-04 00:33:03 +00:00
|
|
|
public void testBuildMessage() {
|
2006-01-17 22:56:15 +00:00
|
|
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
|
|
|
Log log = ctx.logManager().getLog(getClass());
|
|
|
|
|
2013-01-05 21:52:33 +00:00
|
|
|
List<Integer> order = pickOrder();
|
2006-01-17 22:56:15 +00:00
|
|
|
|
|
|
|
TunnelCreatorConfig cfg = createConfig(ctx);
|
|
|
|
_replyRouter = new Hash();
|
|
|
|
byte h[] = new byte[Hash.HASH_LENGTH];
|
|
|
|
Arrays.fill(h, (byte)0xFF);
|
|
|
|
_replyRouter.setData(h);
|
|
|
|
_replyTunnel = 42;
|
|
|
|
|
|
|
|
// populate and encrypt the message
|
|
|
|
TunnelBuildMessage msg = new TunnelBuildMessage(ctx);
|
2012-11-05 21:31:40 +00:00
|
|
|
for (int i = 0; i < order.size(); i++) {
|
2006-01-17 22:56:15 +00:00
|
|
|
int hop = ((Integer)order.get(i)).intValue();
|
|
|
|
PublicKey key = null;
|
|
|
|
if (hop < _pubKeys.length)
|
|
|
|
key = _pubKeys[hop];
|
2012-11-05 21:31:40 +00:00
|
|
|
BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
|
2006-01-17 22:56:15 +00:00
|
|
|
}
|
2012-11-05 21:31:40 +00:00
|
|
|
BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);
|
2006-01-17 22:56:15 +00:00
|
|
|
|
|
|
|
log.debug("\n================================================================" +
|
|
|
|
"\nMessage fully encrypted" +
|
|
|
|
"\n================================================================");
|
|
|
|
|
|
|
|
// now msg is fully encrypted, so lets go through the hops, decrypting and replying
|
|
|
|
// as necessary
|
|
|
|
|
2006-02-15 05:33:17 +00:00
|
|
|
BuildMessageProcessor proc = new BuildMessageProcessor(ctx);
|
2006-01-17 22:56:15 +00:00
|
|
|
for (int i = 0; i < cfg.getLength(); i++) {
|
|
|
|
// this not only decrypts the current hop's record, but encrypts the other records
|
|
|
|
// with the reply key
|
|
|
|
BuildRequestRecord req = proc.decrypt(ctx, msg, _peers[i], _privKeys[i]);
|
2013-01-04 00:33:03 +00:00
|
|
|
// If false, no records matched the _peers[i], or the decryption failed
|
|
|
|
assertTrue("foo @ " + i, req != null);
|
2006-01-17 22:56:15 +00:00
|
|
|
long ourId = req.readReceiveTunnelId();
|
|
|
|
byte replyIV[] = req.readReplyIV();
|
|
|
|
long nextId = req.readNextTunnelId();
|
|
|
|
Hash nextPeer = req.readNextIdentity();
|
|
|
|
boolean isInGW = req.readIsInboundGateway();
|
|
|
|
boolean isOutEnd = req.readIsOutboundEndpoint();
|
|
|
|
long time = req.readRequestTime();
|
|
|
|
long now = (ctx.clock().now() / (60l*60l*1000l)) * (60*60*1000);
|
|
|
|
int ourSlot = -1;
|
2012-11-05 21:31:40 +00:00
|
|
|
|
|
|
|
byte reply[] = BuildResponseRecord.create(ctx, 0, req.readReplyKey(), req.readReplyIV(), -1);
|
|
|
|
for (int j = 0; j < TunnelBuildMessage.MAX_RECORD_COUNT; j++) {
|
2006-01-17 22:56:15 +00:00
|
|
|
if (msg.getRecord(j) == null) {
|
|
|
|
ourSlot = j;
|
|
|
|
msg.setRecord(j, new ByteArray(reply));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.debug("Read slot " + ourSlot + " containing hop " + i + " @ " + _peers[i].toBase64()
|
|
|
|
+ " receives on " + ourId
|
|
|
|
+ " w/ replyIV " + Base64.encode(replyIV) + " sending to " + nextId
|
|
|
|
+ " on " + nextPeer.toBase64()
|
|
|
|
+ " inGW? " + isInGW + " outEnd? " + isOutEnd + " time difference " + (now-time));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.debug("\n================================================================" +
|
|
|
|
"\nAll hops traversed and replies gathered" +
|
|
|
|
"\n================================================================");
|
|
|
|
|
|
|
|
// now all of the replies are populated, toss 'em into a reply message and handle it
|
|
|
|
TunnelBuildReplyMessage reply = new TunnelBuildReplyMessage(ctx);
|
2012-11-05 21:31:40 +00:00
|
|
|
for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++)
|
2006-01-17 22:56:15 +00:00
|
|
|
reply.setRecord(i, msg.getRecord(i));
|
2012-11-05 21:31:40 +00:00
|
|
|
|
|
|
|
int statuses[] = BuildReplyHandler.decrypt(ctx, reply, cfg, order);
|
2006-01-17 22:56:15 +00:00
|
|
|
if (statuses == null) throw new RuntimeException("bar");
|
|
|
|
boolean allAgree = true;
|
|
|
|
for (int i = 0; i < cfg.getLength(); i++) {
|
|
|
|
Hash peer = cfg.getPeer(i);
|
|
|
|
int record = ((Integer)order.get(i)).intValue();
|
|
|
|
if (statuses[record] != 0)
|
|
|
|
allAgree = false;
|
|
|
|
//else
|
|
|
|
// penalize peer according to the rejection cause
|
|
|
|
}
|
|
|
|
|
|
|
|
log.debug("\n================================================================" +
|
|
|
|
"\nAll peers agree? " + allAgree +
|
|
|
|
"\n================================================================");
|
|
|
|
}
|
|
|
|
|
2013-01-05 21:52:33 +00:00
|
|
|
private static final List<Integer> pickOrder() {
|
2006-01-17 22:56:15 +00:00
|
|
|
// pseudorandom, yet consistent (so we can be repeatable)
|
2013-01-05 21:52:33 +00:00
|
|
|
List<Integer> rv = new ArrayList<Integer>(8);
|
2006-01-17 22:56:15 +00:00
|
|
|
rv.add(new Integer(2));
|
|
|
|
rv.add(new Integer(4));
|
|
|
|
rv.add(new Integer(6));
|
|
|
|
rv.add(new Integer(0));
|
|
|
|
rv.add(new Integer(1));
|
|
|
|
rv.add(new Integer(3));
|
|
|
|
rv.add(new Integer(5));
|
|
|
|
rv.add(new Integer(7));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
private TunnelCreatorConfig createConfig(I2PAppContext ctx) {
|
|
|
|
return configOutbound(ctx);
|
|
|
|
}
|
|
|
|
private TunnelCreatorConfig configOutbound(I2PAppContext ctx) {
|
|
|
|
_peers = new Hash[4];
|
|
|
|
_pubKeys = new PublicKey[_peers.length];
|
|
|
|
_privKeys = new PrivateKey[_peers.length];
|
|
|
|
for (int i = 0; i < _peers.length; i++) {
|
|
|
|
byte buf[] = new byte[Hash.HASH_LENGTH];
|
|
|
|
Arrays.fill(buf, (byte)i); // consistent for repeatability
|
|
|
|
Hash h = new Hash(buf);
|
|
|
|
_peers[i] = h;
|
|
|
|
Object kp[] = ctx.keyGenerator().generatePKIKeypair();
|
|
|
|
_pubKeys[i] = (PublicKey)kp[0];
|
|
|
|
_privKeys[i] = (PrivateKey)kp[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
TunnelCreatorConfig cfg = new TunnelCreatorConfig(null, _peers.length, false);
|
|
|
|
long now = ctx.clock().now();
|
|
|
|
// peers[] is ordered endpoint first, but cfg.getPeer() is ordered gateway first
|
|
|
|
for (int i = 0; i < _peers.length; i++) {
|
|
|
|
cfg.setPeer(i, _peers[i]);
|
|
|
|
HopConfig hop = cfg.getConfig(i);
|
|
|
|
hop.setExpiration(now+10*60*1000);
|
|
|
|
hop.setIVKey(ctx.keyGenerator().generateSessionKey());
|
|
|
|
hop.setLayerKey(ctx.keyGenerator().generateSessionKey());
|
|
|
|
hop.setReplyKey(ctx.keyGenerator().generateSessionKey());
|
|
|
|
byte iv[] = new byte[BuildRequestRecord.IV_SIZE];
|
|
|
|
Arrays.fill(iv, (byte)i); // consistent for repeatability
|
|
|
|
hop.setReplyIV(new ByteArray(iv));
|
|
|
|
hop.setReceiveTunnelId(new TunnelId(i+1));
|
|
|
|
}
|
|
|
|
return cfg;
|
|
|
|
}
|
|
|
|
}
|