diff --git a/history.txt b/history.txt index b8a81b093a..7dc8d4de4d 100644 --- a/history.txt +++ b/history.txt @@ -3,6 +3,9 @@ - Replace BC MD5 with JVM version, refactor I2PHMAC to use MessageDigest instead of BC Digest (ticket #1189) - Use JVM HmacSHA256 instead of I2PHMAC for Syndie since it is standard + * SSU: + - Use session key for relay request/response if available (ticket #1206) + - Remove packetAuthTime stats 2014-02-14 zzz * I2CP: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 4e28a2c8d2..c10128fe62 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 6; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java index 01ae042caa..4117edb14f 100644 --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java @@ -411,13 +411,34 @@ class IntroductionManager { // TODO throttle based on alice identity and/or intro tag? _context.statManager().addRateData("udp.receiveRelayRequest", 1, 0); - byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; - reader.getRelayRequestReader().readAliceIntroKey(key, 0); - SessionKey aliceIntroKey = new SessionKey(key); + // send that peer an introduction for alice _transport.send(_builder.buildRelayIntro(alice, charlie, reader.getRelayRequestReader())); + // send alice back charlie's info - _transport.send(_builder.buildRelayResponse(alice, charlie, reader.getRelayRequestReader().readNonce(), aliceIntroKey)); + // lookup session so we can use session key if available + SessionKey cipherKey = null; + SessionKey macKey = null; + PeerState aliceState = _transport.getPeerState(alice); + if (aliceState != null) { + // established session (since 0.9.12) + cipherKey = aliceState.getCurrentCipherKey(); + macKey = aliceState.getCurrentMACKey(); + } + if (cipherKey == null || macKey == null) { + // no session, use intro key (was only way before 0.9.12) + byte key[] = new byte[SessionKey.KEYSIZE_BYTES]; + reader.getRelayRequestReader().readAliceIntroKey(key, 0); + cipherKey = new SessionKey(key); + macKey = cipherKey; + if (_log.shouldLog(Log.INFO)) + _log.info("Sending relay response (w/ intro key) to " + alice); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("Sending relay response (in-session) to " + alice); + } + _transport.send(_builder.buildRelayResponse(alice, charlie, reader.getRelayRequestReader().readNonce(), + cipherKey, macKey)); } /** diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java index 9d14a4a23d..d81674bb0b 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java @@ -96,6 +96,7 @@ class OutboundEstablishState { /** * @param claimedAddress an IP/port based RemoteHostId, or null if unknown * @param remoteHostId non-null, == claimedAddress if direct, or a hash-based one if indirect + * @param introKey Bob's introduction key, as published in the netdb * @param addr non-null */ public OutboundEstablishState(RouterContext ctx, RemoteHostId claimedAddress, @@ -163,6 +164,10 @@ class OutboundEstablishState { } public RouterIdentity getRemoteIdentity() { return _remotePeer; } + + /** + * Bob's introduction key, as published in the netdb + */ public SessionKey getIntroKey() { return _introKey; } /** caller must synch - only call once */ @@ -327,8 +332,8 @@ class OutboundEstablishState { */ private void decryptSignature() { if (_receivedEncryptedSignature == null) throw new NullPointerException("encrypted signature is null! this=" + this.toString()); - else if (_sessionKey == null) throw new NullPointerException("SessionKey is null!"); - else if (_receivedIV == null) throw new NullPointerException("IV is null!"); + if (_sessionKey == null) throw new NullPointerException("SessionKey is null!"); + if (_receivedIV == null) throw new NullPointerException("IV is null!"); _context.aes().decrypt(_receivedEncryptedSignature, 0, _receivedEncryptedSignature, 0, _sessionKey, _receivedIV, _receivedEncryptedSignature.length); byte signatureBytes[] = new byte[Signature.SIGNATURE_BYTES]; diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 647c207ae2..c70177b9db 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -957,7 +957,8 @@ class PacketBuilder { * * @return ready to send packet, or null if there was a problem */ - public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) { + public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, + SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) { UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); DatagramPacket pkt = packet.getPacket(); byte data[] = pkt.getData(); @@ -1031,7 +1032,9 @@ class PacketBuilder { * * @return ready to send packet, or null if there was a problem */ - public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey) { + public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, + SessionKey aliceIntroKey, long nonce, + SessionKey bobCipherKey, SessionKey bobMACKey) { UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); DatagramPacket pkt = packet.getPacket(); byte data[] = pkt.getData(); @@ -1100,7 +1103,34 @@ class PacketBuilder { // TODO implement some sort of introducer banlist continue; } - rv.add(buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true)); + + // lookup session so we can use session key if available + SessionKey cipherKey = null; + SessionKey macKey = null; + // first look up by ikey, it is equal to router hash for now + PeerState bobState = transport.getPeerState(Hash.create(ikey)); + if (bobState == null) { + RemoteHostId rhid = new RemoteHostId(iaddr.getAddress(), iport); + bobState = transport.getPeerState(rhid); + } + if (bobState != null) { + // established session (since 0.9.12) + cipherKey = bobState.getCurrentCipherKey(); + macKey = bobState.getCurrentMACKey(); + } + if (cipherKey == null || macKey == null) { + // no session, use intro key (was only way before 0.9.12) + cipherKey = new SessionKey(ikey); + macKey = cipherKey; + if (_log.shouldLog(Log.INFO)) + _log.info("Sending relay request (w/ intro key) to " + iaddr + ":" + iport); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("Sending relay request (in-session) to " + iaddr + ":" + iport); + } + + rv.add(buildRelayRequest(iaddr, iport, cipherKey, macKey, tag, + ourIntroKey, state.getIntroNonce())); } return rv; } @@ -1110,8 +1140,9 @@ class PacketBuilder { * send a RelayRequest over IPv6 * */ - private UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], - long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) { + private UDPPacket buildRelayRequest(InetAddress introHost, int introPort, + SessionKey cipherKey, SessionKey macKey, + long introTag, SessionKey ourIntroKey, long introNonce) { UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE); DatagramPacket pkt = packet.getPacket(); byte data[] = pkt.getData(); @@ -1121,9 +1152,6 @@ class PacketBuilder { byte ourIP[] = getOurExplicitIP(); int ourPort = getOurExplicitPort(); - if (_log.shouldLog(Log.INFO)) - _log.info("Sending intro relay request to " + introHost + ":" + introPort); // + " regarding " + state.getRemoteIdentity().calculateHash().toBase64()); - // now for the body DataHelper.toLong(data, off, 4, introTag); off += 4; @@ -1160,8 +1188,7 @@ class PacketBuilder { off = pad1(data, off); off = pad2(data, off); pkt.setLength(off); - if (encrypt) - authenticate(packet, new SessionKey(introKey), new SessionKey(introKey)); + authenticate(packet, cipherKey, macKey); setTo(packet, introHost, introPort); packet.setMessageType(TYPE_RREQ); return packet; @@ -1214,7 +1241,8 @@ class PacketBuilder { */ private static final byte PEER_RELAY_RESPONSE_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE << 4); - UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey aliceIntroKey) { + UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, + SessionKey cipherKey, SessionKey macKey) { InetAddress aliceAddr = null; try { aliceAddr = InetAddress.getByAddress(alice.getIP()); @@ -1228,7 +1256,7 @@ class PacketBuilder { int off = HEADER_SIZE; if (_log.shouldLog(Log.INFO)) - _log.info("Sending relay response to " + alice + " for " + charlie + " with alice's intro key " + aliceIntroKey); + _log.info("Sending relay response to " + alice + " for " + charlie + " with key " + cipherKey); // now for the body byte charlieIP[] = charlie.getRemoteIP(); @@ -1255,7 +1283,7 @@ class PacketBuilder { off = pad1(data, off); off = pad2(data, off); pkt.setLength(off); - authenticate(packet, aliceIntroKey, aliceIntroKey); + authenticate(packet, cipherKey, macKey); setTo(packet, aliceAddr, alice.getPort()); packet.setMessageType(TYPE_RESP); return packet; @@ -1416,7 +1444,7 @@ class PacketBuilder { * @param iv IV to deliver */ private void authenticate(UDPPacket packet, SessionKey cipherKey, SessionKey macKey, byte[] iv) { - long before = System.currentTimeMillis(); + //long before = System.currentTimeMillis(); DatagramPacket pkt = packet.getPacket(); int off = pkt.getOffset(); int hmacOff = off; @@ -1454,9 +1482,10 @@ class PacketBuilder { System.arraycopy(ba, 0, data, hmacOff, UDPPacket.MAC_SIZE); SimpleByteCache.release(ba); System.arraycopy(iv, 0, data, hmacOff + UDPPacket.MAC_SIZE, UDPPacket.IV_SIZE); - long timeToAuth = System.currentTimeMillis() - before; - _context.statManager().addRateData("udp.packetAuthTime", timeToAuth, timeToAuth); - if (timeToAuth > 100) - _context.statManager().addRateData("udp.packetAuthTimeSlow", timeToAuth, timeToAuth); + // avg. 0.06 ms on a 2005-era PC + //long timeToAuth = System.currentTimeMillis() - before; + //_context.statManager().addRateData("udp.packetAuthTime", timeToAuth, timeToAuth); + //if (timeToAuth > 100) + // _context.statManager().addRateData("udp.packetAuthTimeSlow", timeToAuth, timeToAuth); } } diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java index ba80cb35a8..6b454dd5c1 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -266,8 +266,9 @@ class PeerTestManager { if (!expired()) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending test to Bob: " + test); - _transport.send(_packetBuilder.buildPeerTestFromAlice(test.getBobIP(), test.getBobPort(), test.getBobCipherKey(), test.getBobMACKey(), //_bobIntroKey, - test.getNonce(), _transport.getIntroKey())); + _transport.send(_packetBuilder.buildPeerTestFromAlice(test.getBobIP(), test.getBobPort(), + test.getBobCipherKey(), test.getBobMACKey(), //_bobIntroKey, + test.getNonce(), _transport.getIntroKey())); } else { _currentTest = null; } @@ -279,8 +280,9 @@ class PeerTestManager { if (!expired()) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending test to Charlie: " + test); - _transport.send(_packetBuilder.buildPeerTestFromAlice(test.getCharlieIP(), test.getCharliePort(), test.getCharlieIntroKey(), - test.getNonce(), _transport.getIntroKey())); + _transport.send(_packetBuilder.buildPeerTestFromAlice(test.getCharlieIP(), test.getCharliePort(), + test.getCharlieIntroKey(), + test.getNonce(), _transport.getIntroKey())); } else { _currentTest = null; } @@ -695,10 +697,13 @@ class PeerTestManager { _context.simpleScheduler().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME); } - UDPPacket packet = _packetBuilder.buildPeerTestToBob(bobIP, from.getPort(), aliceIP, alicePort, aliceIntroKey, nonce, state.getBobCipherKey(), state.getBobMACKey()); + UDPPacket packet = _packetBuilder.buildPeerTestToBob(bobIP, from.getPort(), aliceIP, alicePort, + aliceIntroKey, nonce, + state.getBobCipherKey(), state.getBobMACKey()); _transport.send(packet); - packet = _packetBuilder.buildPeerTestToAlice(aliceIP, alicePort, aliceIntroKey, _transport.getIntroKey(), nonce); + packet = _packetBuilder.buildPeerTestToAlice(aliceIP, alicePort, aliceIntroKey, + _transport.getIntroKey(), nonce); _transport.send(packet); } catch (UnknownHostException uhe) { if (_log.shouldLog(Log.WARN)) diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index a3cdf57a00..252490cfc6 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -7,8 +7,10 @@ import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import net.i2p.I2PAppContext; +import net.i2p.data.Base64; import net.i2p.data.DataHelper; import net.i2p.data.SessionKey; +import net.i2p.router.RouterContext; import net.i2p.router.util.CDQEntry; import net.i2p.util.Addresses; import net.i2p.util.Log; @@ -206,24 +208,32 @@ class UDPPacket implements CDQEntry { off += 2; eq = _context.hmac().verify(macKey, _validateBuf, 0, off, _data, _packet.getOffset(), MAC_SIZE); - /* - Hash hmac = _context.hmac().calculate(macKey, buf.getData(), 0, off); - if (_log.shouldLog(Log.DEBUG)) { - StringBuilder str = new StringBuilder(128); - str.append(_packet.getLength()).append(" byte packet received, payload length "); - str.append(payloadLength); - str.append("\nIV: ").append(Base64.encode(buf.getData(), payloadLength, IV_SIZE)); - str.append("\nIV2: ").append(Base64.encode(_data, MAC_SIZE, IV_SIZE)); - str.append("\nlen: ").append(DataHelper.fromLong(buf.getData(), payloadLength + IV_SIZE, 2)); - str.append("\nMAC key: ").append(macKey.toBase64()); - str.append("\ncalc HMAC: ").append(Base64.encode(hmac.getData())); - str.append("\nread HMAC: ").append(Base64.encode(_data, _packet.getOffset(), MAC_SIZE)); - str.append("\nraw: ").append(Base64.encode(_data, _packet.getOffset(), _packet.getLength())); - _log.debug(str.toString()); + if (!eq) { + // this is relatively frequent, as you can get old keys in PacketHandler. + Log log = _context.logManager().getLog(UDPPacket.class); + if (log.shouldLog(Log.INFO)) { + byte[] calc = new byte[32]; + _context.hmac().calculate(macKey, _validateBuf, 0, off, calc, 0); + StringBuilder str = new StringBuilder(512); + str.append("Bad HMAC:\n\t"); + str.append(_packet.getLength()).append(" byte pkt, "); + str.append(payloadLength).append(" byte payload"); + str.append("\n\tFrom: ").append(getRemoteHost().toString()); + str.append("\n\tIV: ").append(Base64.encode(_validateBuf, payloadLength, IV_SIZE)); + str.append("\n\tIV2: ").append(Base64.encode(_data, MAC_SIZE, IV_SIZE)); + str.append("\n\tGiven Len: ").append(DataHelper.fromLong(_validateBuf, payloadLength + IV_SIZE, 2)); + str.append("\n\tCalc HMAC: ").append(Base64.encode(calc, 0, MAC_SIZE)); + str.append("\n\tRead HMAC: ").append(Base64.encode(_data, _packet.getOffset(), MAC_SIZE)); + str.append("\n\tUsing key: ").append(macKey.toBase64()); + if (DataHelper.eq(macKey.getData(), 0, ((RouterContext)_context).routerHash().getData(), 0, 32)) + str.append(" (Intro)"); + else + str.append(" (Session)"); + str.append("\n\tRaw: ").append(Base64.encode(_data, _packet.getOffset(), _packet.getLength())); + log.info(str.toString(), new Exception()); + } } - eq = DataHelper.eq(hmac.getData(), 0, _data, _packet.getOffset(), MAC_SIZE); - */ } else { //if (_log.shouldLog(Log.WARN)) // _log.warn("Payload length is " + payloadLength); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java index 6a82fcebd5..affaf09df8 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java @@ -51,7 +51,7 @@ class UDPPacketReader { initialize(packet.getPacket().getData(), off, len); } - public void initialize(byte message[], int payloadOffset, int payloadLength) { + private void initialize(byte message[], int payloadOffset, int payloadLength) { _message = message; _payloadBeginOffset = payloadOffset; _payloadLength = payloadLength; @@ -106,7 +106,8 @@ class UDPPacketReader { @Override public String toString() { - switch (readPayloadType()) { + int type = readPayloadType(); + switch (type) { case UDPPacket.PAYLOAD_TYPE_DATA: return _dataReader.toString(); case UDPPacket.PAYLOAD_TYPE_SESSION_CONFIRMED: @@ -126,7 +127,7 @@ class UDPPacketReader { case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY: return "Session destroyed packet"; default: - return "Other packet type..."; + return "Unknown packet type " + type; } } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 239060bfff..3a2f001c2e 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -261,8 +261,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _context.statManager().createRateStat("udp.dropPeerDroplist", "How many peers currently have their packets dropped outright when a new peer is added to the list?", "udp", RATES); _context.statManager().createRateStat("udp.dropPeerConsecutiveFailures", "How many consecutive failed sends to a peer did we attempt before giving up and reestablishing a new session (lifetime is inactivity perood)", "udp", RATES); // following are for PacketBuider - _context.statManager().createRateStat("udp.packetAuthTime", "How long it takes to encrypt and MAC a packet for sending", "udp", RATES); - _context.statManager().createRateStat("udp.packetAuthTimeSlow", "How long it takes to encrypt and MAC a packet for sending (when its slow)", "udp", RATES); + //_context.statManager().createRateStat("udp.packetAuthTime", "How long it takes to encrypt and MAC a packet for sending", "udp", RATES); + //_context.statManager().createRateStat("udp.packetAuthTimeSlow", "How long it takes to encrypt and MAC a packet for sending (when its slow)", "udp", RATES); _context.simpleScheduler().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4); }