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); } }