package net.i2p.router.tunnel;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.util.Log;
import net.i2p.util.SimpleByteCache;
/**
* Turn the preprocessed tunnel data into something that can be delivered to the
* first hop in the tunnel. The crypto used in this class is also used by the
* InboundEndpointProcessor, as its the same 'undo' function of the tunnel crypto.
*
*/
class OutboundGatewayProcessor {
private final I2PAppContext _context;
private final Log _log;
private final TunnelCreatorConfig _config;
//static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
public OutboundGatewayProcessor(I2PAppContext ctx, TunnelCreatorConfig cfg) {
_context = ctx;
_log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
_config = cfg;
}
/**
* Since we are the outbound gateway, pick a random IV and wrap the preprocessed
* data so that it will be exposed at the endpoint.
*
* @param orig original data with an extra 16 byte IV prepended.
* @param offset index into the array where the extra 16 bytes (IV) begins
* @param length how much of orig can we write to (must be a multiple of 16).
*/
public void process(byte orig[], int offset, int length) {
byte iv[] = SimpleByteCache.acquire(HopProcessor.IV_LENGTH);
//_context.random().nextBytes(iv);
//System.arraycopy(iv, 0, orig, offset, HopProcessor.IV_LENGTH);
System.arraycopy(orig, offset, iv, 0, HopProcessor.IV_LENGTH);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Orig random IV: " + Base64.encode(iv));
//_log.debug("data: " + Base64.encode(orig, iv.length, length - iv.length));
}
//if (USE_ENCRYPTION)
decrypt(_context, _config, iv, orig, offset, length);
if (_log.shouldLog(Log.DEBUG))
_log.debug("finished processing the preprocessed data");
SimpleByteCache.release(iv);
}
/**
* Iteratively undo the crypto that the various layers in the tunnel added. This is used
* by the outbound gateway (preemptively undoing the crypto peers will add).
*/
private void decrypt(I2PAppContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) {
Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
byte cur[] = SimpleByteCache.acquire(HopProcessor.IV_LENGTH);
for (int i = cfg.getLength()-1; i >= 1; i--) { // dont include hop 0, since that is the creator
decrypt(ctx, iv, orig, offset, length, cur, cfg.getConfig(i));
if (log.shouldLog(Log.DEBUG)) {
log.debug("IV at hop " + i + ": " + Base64.encode(orig, offset, HopProcessor.IV_LENGTH));
//log.debug("hop " + i + ": " + Base64.encode(orig, offset + HopProcessor.IV_LENGTH, length - HopProcessor.IV_LENGTH));
}
}
SimpleByteCache.release(cur);
}
/**
* Undo the crypto for a single hop. This is used
* by both the outbound gateway (preemptively undoing the crypto peers will add)
* and by the inbound endpoint.
*/
static void decrypt(I2PAppContext ctx, byte iv[], byte orig[], int offset, int length, byte cur[], HopConfig config) {
// update the IV for the previous (next?) hop
ctx.aes().decryptBlock(orig, offset, config.getIVKey(), orig, offset);
int numBlocks = (length - HopProcessor.IV_LENGTH) / HopProcessor.IV_LENGTH;
// prev == previous encrypted block (or IV for the first block)
byte prev[] = iv;
System.arraycopy(orig, offset, prev, 0, HopProcessor.IV_LENGTH);
//_log.debug("IV at curHop: " + Base64.encode(iv));
//decrypt the whole row
for (int i = 0; i < numBlocks; i++) {
int off = (i + 1) * HopProcessor.IV_LENGTH + offset;
System.arraycopy(orig, off, cur, 0, HopProcessor.IV_LENGTH);
ctx.aes().decryptBlock(orig, off, config.getLayerKey(), orig, off);
//DataHelper.xor(prev, 0, orig, off, orig, off, HopProcessor.IV_LENGTH);
for (int j = 0; j < HopProcessor.IV_LENGTH; j++) {
orig[off + j] ^= prev[j];
}
byte xf[] = prev;
prev = cur;
cur = xf;
}
//if (HopProcessor.USE_DOUBLE_IV_ENCRYPTION)
ctx.aes().decryptBlock(orig, offset, config.getIVKey(), orig, offset);
}
}