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