Search in sources :

Example 31 with SessionKey

use of net.i2p.data.SessionKey in project i2p.i2p by i2p.

the class UDPTransport method startup.

private synchronized void startup() {
    _fragments.shutdown();
    if (_pusher != null)
        _pusher.shutdown();
    if (_handler != null)
        _handler.shutdown();
    for (UDPEndpoint endpoint : _endpoints) {
        endpoint.shutdown();
        // should we remove?
        _endpoints.remove(endpoint);
    }
    if (_establisher != null)
        _establisher.shutdown();
    if (_refiller != null)
        _refiller.shutdown();
    _inboundFragments.shutdown();
    // if (_flooder != null)
    // _flooder.shutdown();
    _introManager.reset();
    UDPPacket.clearCache();
    if (_log.shouldLog(Log.WARN))
        _log.warn("Starting SSU transport listening");
    _introKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
    System.arraycopy(_context.routerHash().getData(), 0, _introKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
    // bind host
    // This is not exposed in the UI and in practice is always null.
    // We use PROP_EXTERNAL_HOST instead. See below.
    String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
    if (bindTo == null) {
        // If we are configured with a fixed IP address,
        // AND it's one of our local interfaces,
        // bind only to that.
        String fixedHost = _context.getProperty(PROP_EXTERNAL_HOST);
        if (fixedHost != null && fixedHost.length() > 0) {
            // Generate a comma-separated list of valid IP addresses
            // that we can bind to,
            // from the comma-separated PROP_EXTERNAL_HOST config.
            // The config may contain IPs or hostnames; expand each
            // hostname to one or more (v4 or v6) IPs.
            TransportUtil.IPv6Config cfg = getIPv6Config();
            Set<String> myAddrs;
            if (cfg == IPV6_DISABLED)
                myAddrs = Addresses.getAddresses(false, false);
            else
                myAddrs = Addresses.getAddresses(false, true);
            StringBuilder buf = new StringBuilder();
            String[] bta = DataHelper.split(fixedHost, "[,; \r\n\t]");
            for (int i = 0; i < bta.length; i++) {
                String bt = bta[i];
                if (bt.length() <= 0)
                    continue;
                try {
                    InetAddress[] all = InetAddress.getAllByName(bt);
                    for (int j = 0; j < all.length; j++) {
                        InetAddress ia = all[j];
                        if (cfg == IPV6_ONLY && (ia instanceof Inet4Address)) {
                            if (_log.shouldWarn())
                                _log.warn("Configured for IPv6 only, not binding to configured IPv4 host " + bt);
                            continue;
                        }
                        String testAddr = ia.getHostAddress();
                        if (myAddrs.contains(testAddr)) {
                            if (buf.length() > 0)
                                buf.append(',');
                            buf.append(testAddr);
                        } else {
                            if (_log.shouldWarn())
                                _log.warn("Not a local address, not binding to configured IP " + testAddr);
                        }
                    }
                } catch (UnknownHostException uhe) {
                    if (_log.shouldWarn())
                        _log.warn("Not binding to configured host " + bt + " - " + uhe);
                }
            }
            if (buf.length() > 0) {
                bindTo = buf.toString();
                if (_log.shouldWarn() && !fixedHost.equals(bindTo))
                    _log.warn("Expanded external host config \"" + fixedHost + "\" to \"" + bindTo + '"');
            }
        }
    }
    // construct a set of addresses
    Set<InetAddress> bindToAddrs = new HashSet<InetAddress>(4);
    if (bindTo != null) {
        // Generate a set IP addresses
        // that we can bind to,
        // from the comma-separated PROP_BIND_INTERFACE config,
        // or as generated from the PROP_EXTERNAL_HOST config.
        // In theory, the config may contain IPs and/or hostnames.
        // However, in practice, it's only IPs, because any hostnames
        // in PROP_EXTERNAL_HOST were expanded above to one or more (v4 or v6) IPs.
        // PROP_BIND_INTERFACE is not exposed in the UI and is never set.
        String[] bta = DataHelper.split(bindTo, "[,; \r\n\t]");
        for (int i = 0; i < bta.length; i++) {
            String bt = bta[i];
            if (bt.length() <= 0)
                continue;
            try {
                bindToAddrs.add(InetAddress.getByName(bt));
            } catch (UnknownHostException uhe) {
                _log.error("Invalid SSU bind interface specified [" + bt + "]", uhe);
            // setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
            // return;
            // fall thru...
            }
        }
    }
    // Requested bind port
    // This may be -1 or may not be honored if busy,
    // Priority: Configured internal, then already used, then configured external
    // we will check below after starting up the endpoint.
    int port;
    int oldIPort = _context.getProperty(PROP_INTERNAL_PORT, -1);
    int oldBindPort = getListenPort(false);
    int oldEPort = _context.getProperty(PROP_EXTERNAL_PORT, -1);
    if (oldIPort > 0)
        port = oldIPort;
    else if (oldBindPort > 0)
        port = oldBindPort;
    else
        port = oldEPort;
    if (!bindToAddrs.isEmpty() && _log.shouldLog(Log.WARN))
        _log.warn("Binding only to " + bindToAddrs);
    if (_log.shouldLog(Log.INFO))
        _log.info("Binding to the port: " + port);
    if (_endpoints.isEmpty()) {
        // _endpoints will always be empty since we removed them above
        if (bindToAddrs.isEmpty()) {
            UDPEndpoint endpoint = new UDPEndpoint(_context, this, port, null);
            _endpoints.add(endpoint);
            setMTU(null);
        } else {
            for (InetAddress bindToAddr : bindToAddrs) {
                UDPEndpoint endpoint = new UDPEndpoint(_context, this, port, bindToAddr);
                _endpoints.add(endpoint);
                setMTU(bindToAddr);
            }
        }
    } else {
        // unused for now
        for (UDPEndpoint endpoint : _endpoints) {
            if (endpoint.isIPv4()) {
                // hack, first IPv4 endpoint, FIXME
                // todo, set bind address too
                endpoint.setListenPort(port);
                break;
            }
        }
    }
    if (_establisher == null)
        _establisher = new EstablishmentManager(_context, this);
    if (_handler == null)
        _handler = new PacketHandler(_context, this, _establisher, _inboundFragments, _testManager, _introManager);
    // See comments in DummyThrottle.java
    if (USE_PRIORITY && _refiller == null)
        _refiller = new OutboundRefiller(_context, _fragments, _outboundMessages);
    // if (SHOULD_FLOOD_PEERS && _flooder == null)
    // _flooder = new UDPFlooder(_context, this);
    // Startup the endpoint with the requested port, check the actual port, and
    // take action if it failed or was different than requested or it needs to be saved
    int newPort = -1;
    for (UDPEndpoint endpoint : _endpoints) {
        try {
            endpoint.startup();
            // hack, first IPv4 endpoint, FIXME
            if (newPort < 0 && endpoint.isIPv4()) {
                newPort = endpoint.getListenPort();
            }
            if (_log.shouldLog(Log.WARN))
                _log.warn("Started " + endpoint);
        } catch (SocketException se) {
            _endpoints.remove(endpoint);
            _log.error("Failed to start " + endpoint, se);
        }
    }
    if (_endpoints.isEmpty()) {
        _log.log(Log.CRIT, "Unable to open UDP port");
        setReachabilityStatus(Status.HOSED);
        return;
    }
    if (newPort > 0 && (newPort != port || newPort != oldIPort || newPort != oldEPort)) {
        // attempt to use it as our external port - this will be overridden by
        // externalAddressReceived(...)
        Map<String, String> changes = new HashMap<String, String>();
        changes.put(PROP_INTERNAL_PORT, Integer.toString(newPort));
        changes.put(PROP_EXTERNAL_PORT, Integer.toString(newPort));
        _context.router().saveConfig(changes, null);
    }
    _handler.startup();
    _fragments.startup();
    _inboundFragments.startup();
    _pusher = new PacketPusher(_context, _fragments, _endpoints);
    _pusher.startup();
    // must be after pusher
    _establisher.startup();
    if (USE_PRIORITY)
        _refiller.startup();
    // if (SHOULD_FLOOD_PEERS)
    // _flooder.startup();
    _expireEvent.setIsAlive(true);
    _reachabilityStatus = Status.UNKNOWN;
    // this queues it for 3-6 minutes in the future...
    _testEvent.setIsAlive(true);
    // lets requeue it for Real Soon
    _testEvent.reschedule(10 * 1000);
    // TransportManager.startListening() calls router.rebuildRouterInfo()
    if (newPort > 0 && bindToAddrs.isEmpty()) {
        for (InetAddress ia : getSavedLocalAddresses()) {
            // since there's no v6 testing?
            if (ia.getAddress().length == 16) {
                // FIXME we need to check and time out after an hour of no inbound ipv6,
                // change to firewalled maybe? but we don't have any test to restore
                // a v6 address after it's removed.
                _lastInboundIPv6 = _context.clock().now();
                if (!isIPv6Firewalled())
                    setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
            } else {
                if (!isIPv4Firewalled())
                    setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
            }
            rebuildExternalAddress(ia.getHostAddress(), newPort, false);
        }
    } else if (newPort > 0 && !bindToAddrs.isEmpty()) {
        for (InetAddress ia : bindToAddrs) {
            if (ia.getAddress().length == 16) {
                _lastInboundIPv6 = _context.clock().now();
                if (!isIPv6Firewalled())
                    setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
            } else {
                if (!isIPv4Firewalled())
                    setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
            }
            rebuildExternalAddress(ia.getHostAddress(), newPort, false);
        }
    // TODO
    // If we are bound only to v4 addresses,
    // force _haveIPv6Address to false, or else we get 'no endpoint' errors
    // If we are bound only to v6 addresses,
    // override getIPv6Config() ?
    }
    if (isIPv4Firewalled()) {
        if (_lastInboundIPv6 > 0)
            setReachabilityStatus(Status.IPV4_FIREWALLED_IPV6_UNKNOWN);
        else
            setReachabilityStatus(Status.REJECT_UNSOLICITED);
    }
    rebuildExternalAddress(false);
}
Also used : SocketException(java.net.SocketException) Inet4Address(java.net.Inet4Address) IPv6Config(net.i2p.router.transport.TransportUtil.IPv6Config) UnknownHostException(java.net.UnknownHostException) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) TransportUtil(net.i2p.router.transport.TransportUtil) SessionKey(net.i2p.data.SessionKey) InetAddress(java.net.InetAddress) ConcurrentHashSet(net.i2p.util.ConcurrentHashSet) HashSet(java.util.HashSet)

Example 32 with SessionKey

use of net.i2p.data.SessionKey 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 33 with SessionKey

use of net.i2p.data.SessionKey in project i2p.i2p by i2p.

the class BuildReplyHandler method decryptRecord.

/**
 * Decrypt the record (removing the layers of reply encyption) and read out the status
 *
 * Note that this layer-decrypts the build records in-place.
 * Do not call this more than once for a given message.
 *
 * @return the status 0-255, or -1 on decrypt failure
 */
private int decryptRecord(TunnelBuildReplyMessage reply, TunnelCreatorConfig cfg, int recordNum, int hop) {
    if (BuildMessageGenerator.isBlank(cfg, hop)) {
        if (log.shouldLog(Log.DEBUG))
            log.debug(reply.getUniqueId() + ": Record " + recordNum + "/" + hop + " is fake, so consider it valid...");
        return 0;
    }
    EncryptedBuildRecord rec = reply.getRecord(recordNum);
    byte[] data = rec.getData();
    int start = cfg.getLength() - 1;
    if (cfg.isInbound())
        // the last hop in an inbound tunnel response doesn't actually encrypt
        start--;
    // do we need to adjust this for the endpoint?
    for (int j = start; j >= hop; j--) {
        HopConfig hopConfig = cfg.getConfig(j);
        SessionKey replyKey = hopConfig.getReplyKey();
        byte[] replyIV = hopConfig.getReplyIV();
        if (log.shouldLog(Log.DEBUG)) {
            log.debug(reply.getUniqueId() + ": Decrypting record " + recordNum + "/" + hop + "/" + j + " with replyKey " + replyKey.toBase64() + "/" + Base64.encode(replyIV) + ": " + cfg);
            log.debug(reply.getUniqueId() + ": before decrypt: " + Base64.encode(data));
            log.debug(reply.getUniqueId() + ": Full reply rec: sz=" + data.length + " data=" + Base64.encode(data, 0, TunnelBuildReplyMessage.RECORD_SIZE));
        }
        ctx.aes().decrypt(data, 0, data, 0, replyKey, replyIV, 0, TunnelBuildReplyMessage.RECORD_SIZE);
        if (log.shouldLog(Log.DEBUG))
            log.debug(reply.getUniqueId() + ": after decrypt: " + Base64.encode(data));
    }
    // ok, all of the layered encryption is stripped, so lets verify it
    // (formatted per BuildResponseRecord.create)
    // don't cache the result
    // Hash h = ctx.sha().calculateHash(data, off + Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE-Hash.HASH_LENGTH);
    byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
    ctx.sha().calculateHash(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE - Hash.HASH_LENGTH, h, 0);
    boolean ok = DataHelper.eq(h, 0, data, 0, Hash.HASH_LENGTH);
    if (!ok) {
        if (log.shouldLog(Log.DEBUG))
            log.debug(reply.getUniqueId() + ": Failed verification on " + recordNum + "/" + hop + ": " + Base64.encode(h) + " calculated, " + Base64.encode(data, 0, Hash.HASH_LENGTH) + " expected\n" + "Record: " + Base64.encode(data, Hash.HASH_LENGTH, TunnelBuildReplyMessage.RECORD_SIZE - Hash.HASH_LENGTH));
        SimpleByteCache.release(h);
        return -1;
    } else {
        SimpleByteCache.release(h);
        int rv = data[TunnelBuildReplyMessage.RECORD_SIZE - 1] & 0xff;
        if (log.shouldLog(Log.DEBUG))
            log.debug(reply.getUniqueId() + ": Verified: " + rv + " for record " + recordNum + "/" + hop);
        return rv;
    }
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) SessionKey(net.i2p.data.SessionKey)

Example 34 with SessionKey

use of net.i2p.data.SessionKey in project i2p.i2p by i2p.

the class InboundEstablishState method generateSessionKey.

/**
 *  Generates session key and mac key.
 */
public synchronized void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
    if (_sessionKey != null)
        return;
    _keyBuilder.setPeerPublicValue(_receivedX);
    _sessionKey = _keyBuilder.getSessionKey();
    ByteArray extra = _keyBuilder.getExtraBytes();
    _macKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
    System.arraycopy(extra.getData(), 0, _macKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Established inbound keys.  cipher: " + Base64.encode(_sessionKey.getData()) + " mac: " + Base64.encode(_macKey.getData()));
}
Also used : SessionKey(net.i2p.data.SessionKey) ByteArray(net.i2p.data.ByteArray)

Example 35 with SessionKey

use of net.i2p.data.SessionKey in project i2p.i2p by i2p.

the class OutboundEstablishState method generateSessionKey.

/**
 *  Generates session key and mac key.
 *  Caller must synch on this.
 */
private void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
    if (_sessionKey != null)
        return;
    if (_keyBuilder == null)
        throw new DHSessionKeyBuilder.InvalidPublicParameterException("Illegal state - never generated a key builder");
    _keyBuilder.setPeerPublicValue(_receivedY);
    _sessionKey = _keyBuilder.getSessionKey();
    ByteArray extra = _keyBuilder.getExtraBytes();
    _macKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
    System.arraycopy(extra.getData(), 0, _macKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Established outbound keys.  cipher: " + _sessionKey + " mac: " + _macKey);
}
Also used : SessionKey(net.i2p.data.SessionKey) ByteArray(net.i2p.data.ByteArray) DHSessionKeyBuilder(net.i2p.router.transport.crypto.DHSessionKeyBuilder)

Aggregations

SessionKey (net.i2p.data.SessionKey)69 SessionTag (net.i2p.data.SessionTag)15 PublicKey (net.i2p.data.PublicKey)14 I2PAppContext (net.i2p.I2PAppContext)13 HashSet (java.util.HashSet)11 Hash (net.i2p.data.Hash)11 SessionKeyManager (net.i2p.crypto.SessionKeyManager)10 PrivateKey (net.i2p.data.PrivateKey)10 InetAddress (java.net.InetAddress)9 DataFormatException (net.i2p.data.DataFormatException)9 UnknownHostException (java.net.UnknownHostException)7 TagSetHandle (net.i2p.crypto.TagSetHandle)5 Map (java.util.Map)4 GarlicMessage (net.i2p.data.i2np.GarlicMessage)4 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 HashMap (java.util.HashMap)3 Set (java.util.Set)3 EncryptedBuildRecord (net.i2p.data.i2np.EncryptedBuildRecord)3 BigInteger (java.math.BigInteger)2