package net.i2p.router.tunnel; import net.i2p.I2PAppContext; import net.i2p.data.Hash; import net.i2p.util.Log; /** * Take a received tunnel message, verify that it isn't a * duplicate, and translate it into what the next hop will * want. The hop processor works the same on all peers - * inbound and outbound participants, outbound endpoints, * and inbound gateways (with a small modification per * InboundGatewayProcessor). * */ class HopProcessor { protected final I2PAppContext _context; private final Log _log; protected final HopConfig _config; private final IVValidator _validator; /** helpful flag for debugging */ //static final boolean USE_ENCRYPTION = true; /** * as of i2p 0.6, the tunnel crypto changed to encrypt the IV both before * and after using it at each hop so as to prevent a certain type of replay/confirmation * attack. * * See: http://osdir.com/ml/network.i2p/2005-07/msg00031.html */ //static final boolean USE_DOUBLE_IV_ENCRYPTION = true; static final int IV_LENGTH = 16; /** * @deprecated used only by unit tests */ @Deprecated HopProcessor(I2PAppContext ctx, HopConfig config) { this(ctx, config, createValidator()); } public HopProcessor(I2PAppContext ctx, HopConfig config, IVValidator validator) { _context = ctx; _log = ctx.logManager().getLog(HopProcessor.class); _config = config; _validator = validator; } /** * @deprecated used only by unit test constructor */ @Deprecated private static IVValidator createValidator() { // yeah, we'll use an O(1) validator later (e.g. bloom filter) return new HashSetIVValidator(); } /** * Process the data for the current hop, overwriting the original data with * what should be sent to the next peer. This also validates the previous * peer and the IV, making sure its not a repeat and not a loop. * * @param orig IV+data of the message * @param offset index into orig where the IV begins * @param length how long after the offset does the message go for? * @param prev previous hop in the tunnel, or null if we are the gateway * @return true if the message was updated and valid, false if it was not. */ public boolean process(byte orig[], int offset, int length, Hash prev) { // prev is null on gateways if (prev != null) { if (_config.getReceiveFrom() == null) { _config.setReceiveFrom(prev); } else if (!_config.getReceiveFrom().equals(prev)) { // shouldn't happen now that we have good dup ID detection in BuildHandler if (_log.shouldLog(Log.WARN)) _log.warn("Attempted mid-tunnel injection from " + prev + ", expected " + _config.getReceiveFrom()); return false; } } boolean okIV = _validator.receiveIV(orig, offset, orig, offset + IV_LENGTH); if (!okIV) { if (_log.shouldLog(Log.WARN)) _log.warn("Invalid IV, dropping at hop " + _config); return false; } if (_log.shouldLog(Log.DEBUG)) { //_log.debug("IV received: " + Base64.encode(iv)); //_log.debug("Before:" + Base64.encode(orig, IV_LENGTH, orig.length - IV_LENGTH)); } //if (USE_ENCRYPTION) { //if (USE_DOUBLE_IV_ENCRYPTION) updateIV(orig, offset); encrypt(orig, offset, length); updateIV(orig, offset); //} //if (_log.shouldLog(Log.DEBUG)) { //_log.debug("Data after processing: " + Base64.encode(orig, IV_LENGTH, orig.length - IV_LENGTH)); //_log.debug("IV sent: " + Base64.encode(orig, 0, IV_LENGTH)); //} return true; } private final void encrypt(byte data[], int offset, int length) { for (int off = offset + IV_LENGTH; off < length; off += IV_LENGTH) { //DataHelper.xor(data, off - IV_LENGTH, data, off, data, off, IV_LENGTH); for (int j = 0; j < IV_LENGTH; j++) { data[off + j] ^= data[(off - IV_LENGTH) + j]; } _context.aes().encryptBlock(data, off, _config.getLayerKey(), data, off); } } private final void updateIV(byte orig[], int offset) { _context.aes().encryptBlock(orig, offset, _config.getIVKey(), orig, offset); } /** * @since 0.8.12 */ @Override public String toString() { return getClass().getSimpleName() + " for " + _config; } }