Search in sources :

Example 1 with BuildRequestRecord

use of net.i2p.data.i2np.BuildRequestRecord in project i2p.i2p by i2p.

the class BuildMessageGenerator method createRecord.

/**
 * return null if it is unable to find a router's public key (etc)
 */
/**
 **
 *    public TunnelBuildMessage createInbound(RouterContext ctx, TunnelCreatorConfig cfg) {
 *        return create(ctx, cfg, null, -1);
 *    }
 ***
 */
/**
 * return null if it is unable to find a router's public key (etc)
 */
/**
 **
 *    public TunnelBuildMessage createOutbound(RouterContext ctx, TunnelCreatorConfig cfg, Hash replyRouter, long replyTunnel) {
 *        return create(ctx, cfg, replyRouter, replyTunnel);
 *    }
 ***
 */
/**
 **
 *    private TunnelBuildMessage create(RouterContext ctx, TunnelCreatorConfig cfg, Hash replyRouter, long replyTunnel) {
 *        TunnelBuildMessage msg = new TunnelBuildMessage(ctx);
 *        List order = new ArrayList(ORDER.length);
 *        for (int i = 0; i < ORDER.length; i++) order.add(ORDER[i]);
 *        Collections.shuffle(order, ctx.random());
 *        for (int i = 0; i < ORDER.length; i++) {
 *            int hop = ((Integer)order.get(i)).intValue();
 *            Hash peer = cfg.getPeer(hop);
 *            RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(peer);
 *            if (ri == null)
 *                return null;
 *            createRecord(i, hop, msg, cfg, replyRouter, replyTunnel, ctx, ri.getIdentity().getPublicKey());
 *        }
 *        layeredEncrypt(ctx, msg, cfg, order);
 *        return msg;
 *    }
 ***
 */
/**
 * Place the asymmetrically encrypted record in the specified record slot,
 * containing the hop's configuration (as well as the reply info, if it is an outbound endpoint)
 *
 * @param msg out parameter
 * @param peerKey Encrypt using this key.
 *                If null, replyRouter and replyTunnel are ignored,
 *                and the entire record is filled with random data
 * @throws IllegalArgumentException if hop bigger than config
 */
public static void createRecord(int recordNum, int hop, TunnelBuildMessage msg, TunnelCreatorConfig cfg, Hash replyRouter, long replyTunnel, I2PAppContext ctx, PublicKey peerKey) {
    // Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
    EncryptedBuildRecord erec;
    if (peerKey != null) {
        BuildRequestRecord req = null;
        if (// outbound endpoint
        (!cfg.isInbound()) && (hop + 1 == cfg.getLength()))
            req = createUnencryptedRecord(ctx, cfg, hop, replyRouter, replyTunnel);
        else
            req = createUnencryptedRecord(ctx, cfg, hop, null, -1);
        if (req == null)
            throw new IllegalArgumentException("hop bigger than config");
        Hash peer = cfg.getPeer(hop);
        // if (log.shouldLog(Log.DEBUG))
        // log.debug("Record " + recordNum + "/" + hop + "/" + peer.toBase64()
        // + ": unencrypted = " + Base64.encode(req.getData().getData()));
        erec = req.encryptRecord(ctx, peerKey, peer);
    // if (log.shouldLog(Log.DEBUG))
    // log.debug("Record " + recordNum + "/" + hop + ": encrypted   = " + Base64.encode(encrypted));
    } else {
        // if (log.shouldLog(Log.DEBUG))
        // log.debug("Record " + recordNum + "/" + hop + "/ is blank/random");
        byte[] encrypted = new byte[TunnelBuildMessage.RECORD_SIZE];
        ctx.random().nextBytes(encrypted);
        erec = new EncryptedBuildRecord(encrypted);
    }
    msg.setRecord(recordNum, erec);
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) Hash(net.i2p.data.Hash) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord)

Example 2 with BuildRequestRecord

use of net.i2p.data.i2np.BuildRequestRecord in project i2p.i2p by i2p.

the class BuildMessageProcessor method decrypt.

/**
 * Decrypt the record targetting us, encrypting all of the other records with the included
 * reply key and IV.  The original, encrypted record targetting us is removed from the request
 * message (so that the reply can be placed in that position after going through the decrypted
 * request record).
 *
 * Note that this layer-decrypts the build records in-place.
 * Do not call this more than once for a given message.
 *
 * @return the current hop's decrypted record or null on failure
 */
public BuildRequestRecord decrypt(TunnelBuildMessage msg, Hash ourHash, PrivateKey privKey) {
    BuildRequestRecord rv = null;
    int ourHop = -1;
    long beforeActualDecrypt = 0;
    long afterActualDecrypt = 0;
    byte[] ourHashData = ourHash.getData();
    long beforeLoop = System.currentTimeMillis();
    for (int i = 0; i < msg.getRecordCount(); i++) {
        EncryptedBuildRecord rec = msg.getRecord(i);
        int len = BuildRequestRecord.PEER_SIZE;
        boolean eq = DataHelper.eq(ourHashData, 0, rec.getData(), 0, len);
        if (eq) {
            beforeActualDecrypt = System.currentTimeMillis();
            try {
                rv = new BuildRequestRecord(ctx, privKey, rec);
                afterActualDecrypt = System.currentTimeMillis();
                // i2pd bug
                boolean isBad = SessionKey.INVALID_KEY.equals(rv.readReplyKey());
                if (isBad) {
                    if (log.shouldLog(Log.WARN))
                        log.warn(msg.getUniqueId() + ": Bad reply key: " + rv);
                    ctx.statManager().addRateData("tunnel.buildRequestBadReplyKey", 1);
                    return null;
                }
                // The spec says to feed the 32-byte AES-256 reply key into the Bloom filter.
                // But we were using the first 32 bytes of the encrypted reply.
                // Fixed in 0.9.24
                boolean isDup = _filter.add(rv.getData(), BuildRequestRecord.OFF_REPLY_KEY, 32);
                if (isDup) {
                    if (log.shouldLog(Log.WARN))
                        log.warn(msg.getUniqueId() + ": Dup record: " + rv);
                    ctx.statManager().addRateData("tunnel.buildRequestDup", 1);
                    return null;
                }
                if (log.shouldLog(Log.DEBUG))
                    log.debug(msg.getUniqueId() + ": Matching record: " + rv);
                ourHop = i;
                // TODO should we keep looking for a second match and fail if found?
                break;
            } catch (DataFormatException dfe) {
                if (log.shouldLog(Log.WARN))
                    log.warn(msg.getUniqueId() + ": Matching record decrypt failure", dfe);
                // out there with the same first 16 bytes, go around again
                continue;
            }
        }
    }
    if (rv == null) {
        // none of the records matched, b0rk
        if (log.shouldLog(Log.WARN))
            log.warn(msg.getUniqueId() + ": No matching record");
        return null;
    }
    long beforeEncrypt = System.currentTimeMillis();
    SessionKey replyKey = rv.readReplyKey();
    byte[] iv = rv.readReplyIV();
    for (int i = 0; i < msg.getRecordCount(); i++) {
        if (i != ourHop) {
            EncryptedBuildRecord data = msg.getRecord(i);
            // if (log.shouldLog(Log.DEBUG))
            // log.debug("Encrypting record " + i + "/? with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv));
            // encrypt in-place, corrupts SDS
            byte[] bytes = data.getData();
            ctx.aes().encrypt(bytes, 0, bytes, 0, replyKey, iv, 0, EncryptedBuildRecord.LENGTH);
        }
    }
    long afterEncrypt = System.currentTimeMillis();
    msg.setRecord(ourHop, null);
    if (afterEncrypt - beforeLoop > 1000) {
        if (log.shouldLog(Log.WARN))
            log.warn("Slow decryption, total=" + (afterEncrypt - beforeLoop) + " looping=" + (beforeEncrypt - beforeLoop) + " decrypt=" + (afterActualDecrypt - beforeActualDecrypt) + " encrypt=" + (afterEncrypt - beforeEncrypt));
    }
    return rv;
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) DataFormatException(net.i2p.data.DataFormatException) SessionKey(net.i2p.data.SessionKey) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord)

Example 3 with BuildRequestRecord

use of net.i2p.data.i2np.BuildRequestRecord in project i2p.i2p by i2p.

the class BuildMessageGenerator method createUnencryptedRecord.

/**
 *  Returns null if hop >= cfg.length
 */
private static BuildRequestRecord createUnencryptedRecord(I2PAppContext ctx, TunnelCreatorConfig cfg, int hop, Hash replyRouter, long replyTunnel) {
    // Log log = ctx.logManager().getLog(BuildMessageGenerator.class);
    if (hop < cfg.getLength()) {
        // ok, now lets fill in some data
        HopConfig hopConfig = cfg.getConfig(hop);
        Hash peer = cfg.getPeer(hop);
        long recvTunnelId = -1;
        if (cfg.isInbound() || (hop > 0))
            recvTunnelId = hopConfig.getReceiveTunnel().getTunnelId();
        else
            recvTunnelId = 0;
        long nextTunnelId = -1;
        Hash nextPeer = null;
        if (hop + 1 < cfg.getLength()) {
            nextTunnelId = cfg.getConfig(hop + 1).getReceiveTunnel().getTunnelId();
            nextPeer = cfg.getPeer(hop + 1);
        } else {
            if ((replyTunnel >= 0) && (replyRouter != null)) {
                nextTunnelId = replyTunnel;
                nextPeer = replyRouter;
            } else {
                // inbound endpoint (aka creator)
                nextTunnelId = 0;
                // self
                nextPeer = peer;
            }
        }
        SessionKey layerKey = hopConfig.getLayerKey();
        SessionKey ivKey = hopConfig.getIVKey();
        SessionKey replyKey = hopConfig.getReplyKey();
        byte[] iv = hopConfig.getReplyIV();
        if (iv == null) {
            iv = new byte[BuildRequestRecord.IV_SIZE];
            ctx.random().nextBytes(iv);
            hopConfig.setReplyIV(iv);
        }
        boolean isInGW = (cfg.isInbound() && (hop == 0));
        boolean isOutEnd = (!cfg.isInbound() && (hop + 1 >= cfg.getLength()));
        long nextMsgId = -1;
        if (isOutEnd || (cfg.isInbound() && (hop + 2 >= cfg.getLength()))) {
            nextMsgId = cfg.getReplyMessageId();
        } else {
            // dont care about these intermediary hops
            nextMsgId = ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE);
        }
        // if (log.shouldLog(Log.DEBUG))
        // log.debug("Hop " + hop + " has the next message ID of " + nextMsgId + " for " + cfg
        // + " with replyKey " + replyKey.toBase64() + " and replyIV " + Base64.encode(iv));
        BuildRequestRecord rec = new BuildRequestRecord(ctx, recvTunnelId, peer, nextTunnelId, nextPeer, nextMsgId, layerKey, ivKey, replyKey, iv, isInGW, isOutEnd);
        return rec;
    } else {
        return null;
    }
}
Also used : SessionKey(net.i2p.data.SessionKey) Hash(net.i2p.data.Hash) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord)

Example 4 with BuildRequestRecord

use of net.i2p.data.i2np.BuildRequestRecord in project i2p.i2p by i2p.

the class BuildHandler method handleRequest.

/**
 *  Decrypt the request, lookup the RI locally,
 *  and call handleReq() if found or queue a lookup job.
 *
 *  @return handle time or -1 if it wasn't completely handled
 */
private long handleRequest(BuildMessageState state) {
    long timeSinceReceived = _context.clock().now() - state.recvTime;
    // if (_log.shouldLog(Log.DEBUG))
    // _log.debug(state.msg.getUniqueId() + ": handling request after " + timeSinceReceived);
    Hash from = state.fromHash;
    if (from == null && state.from != null)
        from = state.from.calculateHash();
    if (timeSinceReceived > (BuildRequestor.REQUEST_TIMEOUT * 3)) {
        // don't even bother, since we are so overloaded locally
        _context.throttle().setTunnelStatus(_x("Dropping tunnel requests: Overloaded"));
        if (_log.shouldLog(Log.WARN))
            _log.warn("Not even trying to handle/decrypt the request " + state.msg.getUniqueId() + ", since we received it a long time ago: " + timeSinceReceived);
        _context.statManager().addRateData("tunnel.dropLoadDelay", timeSinceReceived);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return -1;
    }
    // ok, this is not our own tunnel, so we need to do some heavy lifting
    // this not only decrypts the current hop's record, but encrypts the other records
    // with the enclosed reply key
    long beforeDecrypt = System.currentTimeMillis();
    BuildRequestRecord req = _processor.decrypt(state.msg, _context.routerHash(), _context.keyManager().getPrivateKey());
    long decryptTime = System.currentTimeMillis() - beforeDecrypt;
    _context.statManager().addRateData("tunnel.decryptRequestTime", decryptTime);
    if (decryptTime > 500 && _log.shouldLog(Log.WARN))
        _log.warn("Took too long to decrypt the request: " + decryptTime + " for message " + state.msg.getUniqueId() + " received " + (timeSinceReceived + decryptTime) + " ago");
    if (req == null) {
        // no records matched, or the decryption failed.  bah
        if (_log.shouldLog(Log.WARN)) {
            _log.warn("The request " + state.msg.getUniqueId() + " could not be decrypted from: " + from);
        }
        _context.statManager().addRateData("tunnel.dropDecryptFail", 1);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return -1;
    }
    long beforeLookup = System.currentTimeMillis();
    Hash nextPeer = req.readNextIdentity();
    long readPeerTime = System.currentTimeMillis() - beforeLookup;
    RouterInfo nextPeerInfo = _context.netDb().lookupRouterInfoLocally(nextPeer);
    long lookupTime = System.currentTimeMillis() - beforeLookup;
    if (lookupTime > 500 && _log.shouldLog(Log.WARN))
        _log.warn("Took too long to lookup the request: " + lookupTime + "/" + readPeerTime + " for " + req);
    if (nextPeerInfo == null) {
        // limit concurrent next-hop lookups to prevent job queue overload attacks
        int numTunnels = _context.tunnelManager().getParticipatingCount();
        int limit = Math.max(MIN_LOOKUP_LIMIT, Math.min(MAX_LOOKUP_LIMIT, numTunnels * PERCENT_LOOKUP_LIMIT / 100));
        int current;
        // leaky counter, since it isn't reliable
        if (_context.random().nextInt(16) > 0)
            current = _currentLookups.incrementAndGet();
        else
            current = 1;
        if (current <= limit) {
            // don't let it go negative
            if (current <= 0)
                _currentLookups.set(1);
            if (_log.shouldLog(Log.DEBUG))
                _log.debug("Request " + req + " handled, lookup next peer " + nextPeer + " lookups: " + current + '/' + limit);
            _context.netDb().lookupRouterInfo(nextPeer, new HandleReq(_context, state, req, nextPeer), new TimeoutReq(_context, state, req, nextPeer), NEXT_HOP_LOOKUP_TIMEOUT);
        } else {
            _currentLookups.decrementAndGet();
            if (_log.shouldLog(Log.WARN))
                _log.warn("Drop next hop lookup, limit " + limit + ": " + req);
            _context.statManager().addRateData("tunnel.dropLookupThrottle", 1);
            if (from != null)
                _context.commSystem().mayDisconnect(from);
        }
        return -1;
    } else {
        long beforeHandle = System.currentTimeMillis();
        handleReq(nextPeerInfo, state, req, nextPeer);
        long handleTime = System.currentTimeMillis() - beforeHandle;
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("Request " + req + " handled and we know the next peer " + nextPeer + " after " + handleTime + "/" + decryptTime + "/" + lookupTime + "/" + timeSinceReceived);
        return handleTime;
    }
}
Also used : RouterInfo(net.i2p.data.router.RouterInfo) Hash(net.i2p.data.Hash) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord)

Example 5 with BuildRequestRecord

use of net.i2p.data.i2np.BuildRequestRecord in project i2p.i2p by i2p.

the class BuildMessageTestStandalone method testBuildMessage.

public void testBuildMessage() {
    I2PAppContext ctx = I2PAppContext.getGlobalContext();
    Log log = ctx.logManager().getLog(getClass());
    List<Integer> order = pickOrder();
    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);
    for (int i = 0; i < order.size(); i++) {
        int hop = order.get(i).intValue();
        PublicKey key = null;
        if (hop < _pubKeys.length)
            key = _pubKeys[hop];
        BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
    }
    BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);
    log.debug("\n================================================================" + "\nMessage fully encrypted" + "\n================================================================");
    // now msg is fully encrypted, so lets go through the hops, decrypting and replying
    // as necessary
    BuildMessageProcessor proc = new BuildMessageProcessor(ctx);
    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(msg, _peers[i], _privKeys[i]);
        // If false, no records matched the _peers[i], or the decryption failed
        assertTrue("foo @ " + i, req != null);
        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;
        EncryptedBuildRecord reply = BuildResponseRecord.create(ctx, 0, req.readReplyKey(), req.readReplyIV(), -1);
        for (int j = 0; j < TunnelBuildMessage.MAX_RECORD_COUNT; j++) {
            if (msg.getRecord(j) == null) {
                ourSlot = j;
                msg.setRecord(j, 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);
    for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++) reply.setRecord(i, msg.getRecord(i));
    int[] statuses = (new BuildReplyHandler(ctx)).decrypt(reply, cfg, order);
    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 = 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================================================================");
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) I2PAppContext(net.i2p.I2PAppContext) Log(net.i2p.util.Log) PublicKey(net.i2p.data.PublicKey) TunnelBuildMessage(net.i2p.data.i2np.TunnelBuildMessage) Hash(net.i2p.data.Hash) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord) TunnelBuildReplyMessage(net.i2p.data.i2np.TunnelBuildReplyMessage)

Aggregations

BuildRequestRecord (net.i2p.data.i2np.BuildRequestRecord)5 Hash (net.i2p.data.Hash)4 EncryptedBuildRecord (net.i2p.data.i2np.EncryptedBuildRecord)3 SessionKey (net.i2p.data.SessionKey)2 I2PAppContext (net.i2p.I2PAppContext)1 DataFormatException (net.i2p.data.DataFormatException)1 PublicKey (net.i2p.data.PublicKey)1 TunnelBuildMessage (net.i2p.data.i2np.TunnelBuildMessage)1 TunnelBuildReplyMessage (net.i2p.data.i2np.TunnelBuildReplyMessage)1 RouterInfo (net.i2p.data.router.RouterInfo)1 Log (net.i2p.util.Log)1