SSU2: Refactor tokens

Store OB tokens by IP/port
Centralize token generation and expiration in Establishment Manager
Add methods to flush OB tokens on IP/port change
Log tweaks
Javadoc fix
This commit is contained in:
zzz
2022-03-23 06:46:01 -04:00
parent 4f8ad3b6cb
commit 0154a87cbf
7 changed files with 81 additions and 53 deletions

View File

@@ -51,7 +51,7 @@ class EstablishmentManager {
// SSU 2
private final PacketBuilder2 _builder2;
private final boolean _enableSSU2;
private final Map<Hash, Token> _outboundTokens;
private final Map<RemoteHostId, Token> _outboundTokens;
private final Map<RemoteHostId, Token> _inboundTokens;
/** map of RemoteHostId to InboundEstablishState */
@@ -168,7 +168,7 @@ class EstablishmentManager {
_outboundByHash = new ConcurrentHashMap<Hash, OutboundEstablishState>();
if (_enableSSU2) {
_inboundTokens = new LHMCache<RemoteHostId, Token>(MAX_TOKENS);
_outboundTokens = new LHMCache<Hash, Token>(MAX_TOKENS);
_outboundTokens = new LHMCache<RemoteHostId, Token>(MAX_TOKENS);
} else {
_inboundTokens = null;
_outboundTokens = null;
@@ -1800,7 +1800,7 @@ class EstablishmentManager {
* @param token nonzero
* @since 0.9.54
*/
public void addOutboundToken(Hash peer, long token, long expires) {
public void addOutboundToken(RemoteHostId peer, long token, long expires) {
if (expires < _context.clock().now())
return;
Token tok = new Token(token, expires);
@@ -1815,7 +1815,7 @@ class EstablishmentManager {
* @return 0 if none available
* @since 0.9.54
*/
public long getOutboundToken(Hash peer) {
public long getOutboundToken(RemoteHostId peer) {
Token tok;
synchronized(_outboundTokens) {
tok = _outboundTokens.remove(peer);
@@ -1828,17 +1828,54 @@ class EstablishmentManager {
}
/**
* Remember a token that can be used later for the peer to connect to us
* Remove our outbound tokens for this length
*
* @param token nonzero
* @since 0.9.54
*/
public void addInboundToken(RemoteHostId peer, long token) {
public void ipChanged(boolean isIPv6) {
if (!_enableSSU2)
return;
int len = isIPv6 ? 16 : 4;
// expire while we're at it
long now = _context.clock().now();
synchronized(_outboundTokens) {
for (Iterator<Map.Entry<RemoteHostId, Token>> iter = _outboundTokens.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<RemoteHostId, Token> e = iter.next();
if (e.getKey().getIP().length == len || e.getValue().expires < now)
iter.remove();
}
}
}
/**
* Remove all outbound tokens
*
* @since 0.9.54
*/
public void portChanged() {
if (!_enableSSU2)
return;
synchronized(_outboundTokens) {
_outboundTokens.clear();
}
}
/**
* Get a token that can be used later for the peer to connect to us
*
* @since 0.9.54
*/
public Token getInboundToken(RemoteHostId peer) {
long token;
do {
token = _context.random().nextLong();
} while (token == 0);
long expires = _context.clock().now() + IB_TOKEN_EXPIRATION;
Token tok = new Token(token, expires);
synchronized(_inboundTokens) {
_inboundTokens.put(peer, tok);
}
return tok;
}
/**
@@ -1862,7 +1899,7 @@ class EstablishmentManager {
return tok.expires >= _context.clock().now();
}
private static class Token {
public static class Token {
public final long token, expires;
public Token(long tok, long exp) {
token = tok; expires = exp;

View File

@@ -370,7 +370,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
public void gotToken(long token, long expires) {
if (_receivedConfirmedIdentity == null)
throw new IllegalStateException("RI must be first");
_transport.getEstablisher().addOutboundToken(_receivedConfirmedIdentity.calculateHash(), token, expires);
_transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
}
public void gotI2NP(I2NPMessage msg) {
@@ -429,14 +429,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
public long getSendConnID() { return _sendConnID; }
public long getRcvConnID() { return _rcvConnID; }
public long getToken() { return _token; }
public long getNextToken() {
// generate on the fly, this will only be called once
long token;
do {
token = _context.random().nextLong();
} while (token == 0);
_transport.getEstablisher().addInboundToken(_remoteHostId, token);
return token;
public EstablishmentManager.Token getNextToken() {
return _transport.getEstablisher().getInboundToken(_remoteHostId);
}
public HandshakeState getHandshakeState() { return _handshakeState; }
public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; }
@@ -720,6 +714,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
StringBuilder buf = new StringBuilder(128);
buf.append("IES2 ");
buf.append(Addresses.toString(_aliceIP, _alicePort));
buf.append(" Rcv ID: ").append(_rcvConnID);
buf.append(" Send ID: ").append(_sendConnID);
buf.append(" RelayTag: ").append(_sentRelayTag);
buf.append(' ').append(_currentState);
return buf.toString();

View File

@@ -127,7 +127,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
} while (_sendConnID == rcid);
_rcvConnID = rcid;
_token = _transport.getEstablisher().getOutboundToken(_remotePeer.calculateHash());
_token = _transport.getEstablisher().getOutboundToken(_remoteHostId);
_routerAddress = ra;
if (_token != 0)
createNewState(ra);
@@ -247,7 +247,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
}
public void gotToken(long token, long expires) {
_transport.getEstablisher().addOutboundToken(_remotePeer.calculateHash(), token, expires);
_transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
}
public void gotI2NP(I2NPMessage msg) {
@@ -299,14 +299,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
public long getSendConnID() { return _sendConnID; }
public long getRcvConnID() { return _rcvConnID; }
public long getToken() { return _token; }
public long getNextToken() {
// generate on the fly, this will only be called once
long token;
do {
token = _context.random().nextLong();
} while (token == 0);
_transport.getEstablisher().addInboundToken(_remoteHostId, token);
return token;
public EstablishmentManager.Token getNextToken() {
return _transport.getEstablisher().getInboundToken(_remoteHostId);
}
public HandshakeState getHandshakeState() { return _handshakeState; }
public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; }
@@ -567,6 +561,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
@Override
public String toString() {
return "OES2 " + _remoteHostId + ' ' + _currentState;
return "OES2 " + _remoteHostId +
" Rcv ID: " + _rcvConnID +
" Send ID: " + _sendConnID +
' ' + _currentState;
}
}

View File

@@ -73,9 +73,6 @@ class PacketBuilder2 {
/** 80 */
public static final int MIN_IPV6_DATA_PACKET_OVERHEAD = IPV6_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE + MAC_LEN;
/// FIXME
private static final int MAX_IDENTITY_FRAGMENT_SIZE = 1280 - (MIN_DATA_PACKET_OVERHEAD + KEY_LEN + MAC_LEN);
private static final int ABSOLUTE_MAX_ACK_RANGES = 512;
/* Higher than all other OutNetMessage priorities, but still droppable,
@@ -788,7 +785,8 @@ class PacketBuilder2 {
* @param packet containing only 32 byte header
*/
private void encryptSessionCreated(UDPPacket packet, HandshakeState state,
byte[] hdrKey1, byte[] hdrKey2, long relayTag, long token, byte[] ip, int port) {
byte[] hdrKey1, byte[] hdrKey2, long relayTag,
EstablishmentManager.Token token, byte[] ip, int port) {
DatagramPacket pkt = packet.getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
@@ -805,8 +803,8 @@ class PacketBuilder2 {
len += block.getTotalLength();
blocks.add(block);
}
if (token > 0) {
block = new SSU2Payload.NewTokenBlock(token, _context.clock().now() + EstablishmentManager.IB_TOKEN_EXPIRATION);
if (token != null) {
block = new SSU2Payload.NewTokenBlock(token.token, token.expires);
len += block.getTotalLength();
blocks.add(block);
}
@@ -938,7 +936,7 @@ class PacketBuilder2 {
*/
private void encryptSessionConfirmed(UDPPacket packet, HandshakeState state, int mtu,
boolean isIPv6, byte[] hdrKey1, byte[] hdrKey2,
SSU2Payload.RIBlock riblock, long token) {
SSU2Payload.RIBlock riblock, EstablishmentManager.Token token) {
DatagramPacket pkt = packet.getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
@@ -949,8 +947,8 @@ class PacketBuilder2 {
int len = riblock.getTotalLength();
blocks.add(riblock);
// only if room
if (token > 0 && mtu - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + len + MAC_LEN) >= 15) {
Block block = new SSU2Payload.NewTokenBlock(token, _context.clock().now() + EstablishmentManager.IB_TOKEN_EXPIRATION);
if (token != null && mtu - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + len + MAC_LEN) >= 15) {
Block block = new SSU2Payload.NewTokenBlock(token.token, token.expires);
len += block.getTotalLength();
blocks.add(block);
}

View File

@@ -289,8 +289,8 @@ class PacketHandler {
if (state.getNextMACKey() != null)
isValid = validate(packet, state.getNextMACKey());
if (!isValid) {
if (_log.shouldLog(Log.INFO))
_log.info("Failed validation with existing con, trying as new con: " + packet);
if (_log.shouldDebug())
_log.debug("Failed validation with existing con, trying as new con: " + packet);
isValid = validate(packet, _transport.getIntroKey());
if (isValid) {
@@ -472,8 +472,8 @@ class PacketHandler {
if (state.getMACKey() != null) {
isValid = validate(packet, state.getMACKey());
if (isValid) {
if (_log.shouldLog(Log.INFO))
_log.info("Valid introduction packet received for inbound con: " + packet);
if (_log.shouldDebug())
_log.debug("Valid introduction packet received for inbound con: " + packet);
packet.decrypt(state.getCipherKey());
handlePacket(reader, packet, null, null, null, AuthType.SESSION);
@@ -513,8 +513,8 @@ class PacketHandler {
isValid = validate(packet, state.getMACKey());
if (isValid) {
// this should be the Session Confirmed packet
if (_log.shouldLog(Log.INFO))
_log.info("Valid introduction packet received for outbound established con: " + packet);
if (_log.shouldDebug())
_log.debug("Valid introduction packet received for outbound established con: " + packet);
packet.decrypt(state.getCipherKey());
handlePacket(reader, packet, null, state, null, AuthType.SESSION);
return;
@@ -524,8 +524,8 @@ class PacketHandler {
// keys not yet exchanged, lets try it with the peer's intro key
isValid = validate(packet, state.getIntroKey());
if (isValid) {
if (_log.shouldLog(Log.INFO))
_log.info("Valid packet received for " + state + " with Bob's intro key: " + packet);
if (_log.shouldDebug())
_log.debug("Valid packet received for " + state + " with Bob's intro key: " + packet);
packet.decrypt(state.getIntroKey());
// the only packet we should be getting with Bob's intro key is Session Created
handlePacket(reader, packet, null, state, null, AuthType.BOBINTRO);
@@ -721,8 +721,8 @@ class PacketHandler {
_log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
break;
}
if (_log.shouldLog(Log.INFO))
_log.info("Received relay request packet: " + reader + " from " + from);
if (_log.shouldDebug())
_log.debug("Received relay request packet: " + reader + " from " + from);
_introManager.receiveRelayRequest(from, reader);
break;
case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
@@ -731,8 +731,8 @@ class PacketHandler {
_log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
break;
}
if (_log.shouldLog(Log.INFO))
_log.info("Received relay intro packet: " + reader + " from " + from);
if (_log.shouldDebug())
_log.debug("Received relay intro packet: " + reader + " from " + from);
_introManager.receiveRelayIntro(from, reader);
break;
case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
@@ -741,8 +741,8 @@ class PacketHandler {
_log.warn("Dropping type " + type + " auth " + auth + ": " + packet);
break;
}
if (_log.shouldLog(Log.INFO))
_log.info("Received relay response packet: " + reader + " from " + from);
if (_log.shouldDebug())
_log.debug("Received relay response packet: " + reader + " from " + from);
_establisher.receiveRelayResponse(from, reader);
break;
case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:

View File

@@ -427,7 +427,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
}
public void gotToken(long token, long expires) {
_transport.getEstablisher().addOutboundToken(_remotePeer, token, expires);
_transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
}
public void gotI2NP(I2NPMessage msg) {

View File

@@ -184,7 +184,7 @@ class PeerTestManager {
/**
* The next few methods are for when we are Alice
*
* @param bobIP IPv4 only
* @param bob IPv4 only
*/
public synchronized void runTest(PeerState bob) {
if (_currentTest != null) {