- Use session key for relay request/response if available (ticket #1206)
   - Remove packetAuthTime stats
   - Misc. cleanups and logging
This commit is contained in:
zzz
2014-02-17 12:56:08 +00:00
parent 18cbf3d253
commit 6a3e5ec620
9 changed files with 126 additions and 52 deletions

View File

@ -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:

View File

@ -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 = "";

View File

@ -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));
}
/**

View File

@ -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];

View File

@ -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);
}
}

View File

@ -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))

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}