Files
i2p.i2p/router/java/src/net/i2p/router/tunnel/BuildReplyHandler.java
jrandom a12ede096a 2006-01-17 jrandom
* First pass of the new tunnel creation crypto, specified in the new
      router/doc/tunnel-alt-creation.html (referenced in the current
      router/doc/tunnel-alt.html).  It isn't actually used anywhere yet, other
      than in the test code, but the code verifies the technical viability, so
      further scrutiny would be warranted.
2006-01-17 22:56:15 +00:00

74 lines
3.3 KiB
Java

package net.i2p.router.tunnel;
import java.util.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.data.i2np.*;
import net.i2p.util.Log;
/**
* Decrypt the layers of a tunnel build reply message, determining whether the individual
* hops agreed to participate in the tunnel, or if not, why not.
*
*/
public class BuildReplyHandler {
public BuildReplyHandler() {}
/**
* Decrypt the tunnel build reply records. This overwrites the contents of the reply
*
* @return status for the records (in record order), or null if the replies were not valid. Fake records
* always have 0 as their value
*/
public int[] decrypt(I2PAppContext ctx, TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, List recordOrder) {
int rv[] = new int[TunnelBuildReplyMessage.RECORD_COUNT];
for (int i = 0; i < rv.length; i++) {
int hop = ((Integer)recordOrder.get(i)).intValue();
int ok = decryptRecord(ctx, reply, cfg, i, hop);
if (ok == -1) return null;
rv[i] = ok;
}
return rv;
}
/**
* Decrypt the record (removing the layers of reply encyption) and read out the status
*
* @return -1 on decrypt failure
*/
private int decryptRecord(I2PAppContext ctx, TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) {
Log log = ctx.logManager().getLog(getClass());
if (hop >= cfg.getLength()) {
if (log.shouldLog(Log.DEBUG))
log.debug("Record " + recordNum + "/" + hop + " is fake, so consider it valid...");
return 0;
}
ByteArray rec = reply.getRecord(recordNum);
int off = rec.getOffset();
for (int j = cfg.getLength() - 1; j >= hop; j--) {
HopConfig hopConfig = cfg.getConfig(j);
SessionKey replyKey = hopConfig.getReplyKey();
byte replyIV[] = hopConfig.getReplyIV().getData();
int replyIVOff = hopConfig.getReplyIV().getOffset();
if (log.shouldLog(Log.DEBUG))
log.debug("Decrypting record " + recordNum + "/" + hop + " with replyKey " + replyKey.toBase64() + "/" + Base64.encode(replyIV, replyIVOff, 16));
ctx.aes().decrypt(rec.getData(), off, rec.getData(), off, replyKey, replyIV, replyIVOff, TunnelBuildReplyMessage.RECORD_SIZE);
}
// ok, all of the layered encryption is stripped, so lets verify it
// (formatted per BuildResponseRecord.create)
Hash h = ctx.sha().calculateHash(rec.getData(), off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH);
if (!DataHelper.eq(h.getData(), 0, rec.getData(), off, Hash.HASH_LENGTH)) {
if (log.shouldLog(Log.DEBUG))
log.debug("Failed verification on " + recordNum + "/" + hop + ": " + h.toBase64() + " calculated, " +
Base64.encode(rec.getData(), off, Hash.HASH_LENGTH) + " expected\n" +
"Record: " + Base64.encode(rec.getData()));
return -1;
} else {
int rv = (int)DataHelper.fromLong(rec.getData(), off + TunnelBuildReplyMessage.RECORD_SIZE - 1, 1);
if (log.shouldLog(Log.DEBUG))
log.debug("Verified: " + rv + " for record " + recordNum + "/" + hop);
return rv;
}
}
}