SSU2: Handle retransmitted token request

and resend retry.
Previously failed to decrypt it.
This commit is contained in:
zzz
2022-12-11 15:11:12 -05:00
parent 415e31e560
commit 4a4ca0cdf0
3 changed files with 64 additions and 36 deletions

View File

@@ -771,10 +771,10 @@ class EstablishmentManager {
}
} else {
try {
state.receiveSessionRequestAfterRetry(packet);
state.receiveSessionOrTokenRequestAfterRetry(packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Corrupt Session Request after Retry from: " + state, gse);
_log.warn("Corrupt Session/Token Request after Retry from: " + state, gse);
return;
}
}
@@ -1323,7 +1323,7 @@ class EstablishmentManager {
/**
* This handles both initial send and retransmission of Session Created,
* and, for SSU2, send of Retry.
* Retry is never retransmnitted.
* Retry is never retransmitted except in response to a retransmitted Token Request.
*
* This may be called more than once.
*
@@ -1366,6 +1366,7 @@ class EstablishmentManager {
case IB_STATE_TOKEN_REQUEST_RECEIVED:
case IB_STATE_REQUEST_BAD_TOKEN_RECEIVED:
case IB_STATE_RETRY_SENT: // got a retransmitted token request
if (_log.shouldDebug())
_log.debug("Send retry to: " + state);
pkt = _builder2.buildRetryPacket(state2, 0);
@@ -2294,7 +2295,9 @@ class EstablishmentManager {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Removing completely confirmed inbound state");
break;
} else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME) {
} else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME ||
(istate == IB_STATE_RETRY_SENT && // limit time to get sess. req after retry
cur.getLifetime() >= 5 * InboundEstablishState.RETRANSMIT_DELAY)) {
// took too long
iter.remove();
inboundState = cur;
@@ -2351,13 +2354,8 @@ class EstablishmentManager {
sendDestroy(inboundState);
processExpired(inboundState);
} else if (inboundState.getNextSendTime() <= now) {
if (istate == IB_STATE_RETRY_SENT) {
// Retry is never retransmitted
inboundState.fail();
processExpired(inboundState);
} else {
sendCreated(inboundState);
}
// resend created or retry
sendCreated(inboundState);
}
break;

View File

@@ -505,30 +505,39 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
/** note that we just sent a Retry packet */
public synchronized void retryPacketSent() {
// retry after clock skew
if (_currentState == InboundState.IB_STATE_FAILED ||
_currentState == InboundState.IB_STATE_RETRY_SENT)
if (_currentState == InboundState.IB_STATE_FAILED)
return;
if (_currentState != InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED &&
if (_currentState != InboundState.IB_STATE_RETRY_SENT &&
_currentState != InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED &&
_currentState != InboundState.IB_STATE_TOKEN_REQUEST_RECEIVED)
throw new IllegalStateException("Bad state for Retry Sent: " + _currentState);
_currentState = InboundState.IB_STATE_RETRY_SENT;
_lastSend = _context.clock().now();
// Won't really be retransmitted, they have 5 sec to respond or
// EstablishmentManager.handleInbound() will fail the connection
// Alice will retransmit at 1 and 3 seconds, so wait 5
// We're not going to wait for the 3rd retx at 7 seconds.
_nextSend = _lastSend + (5 * RETRANSMIT_DELAY);
if (_currentState == InboundState.IB_STATE_RETRY_SENT) {
// We received a retransmtted token request and resent the retry.
// Won't really be retransmitted, they have 5 sec to respond
// ensure we expire before retransmitting
_nextSend = _establishBegin + (5 * RETRANSMIT_DELAY);
if (_log.shouldWarn())
_log.warn("Retransmit retry on " + this);
} else {
_currentState = InboundState.IB_STATE_RETRY_SENT;
// Won't really be retransmitted, they have 5 sec to respond or
// EstablishmentManager.handleInbound() will fail the connection
// Alice will retransmit at 1 and 3 seconds, so wait 5
// We're not going to wait for the 3rd retx at 7 seconds.
_nextSend = _lastSend + (5 * RETRANSMIT_DELAY);
}
}
/**
* All exceptions thrown from here will be fatal. fail() will be called before throwing.
*/
public synchronized void receiveSessionRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException {
public synchronized void receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException {
try {
locked_receiveSessionRequestAfterRetry(packet);
locked_receiveSessionOrTokenRequestAfterRetry(packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldDebug())
_log.debug("Session request error after retry", gse);
_log.debug("Session/Token request error after retry", gse);
// fail inside synch rather than have Est. Mgr. do it to prevent races
fail();
throw gse;
@@ -538,11 +547,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
/**
* @since 0.9.56
*/
private void locked_receiveSessionRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException {
if (_currentState != InboundState.IB_STATE_RETRY_SENT)
throw new GeneralSecurityException("Bad state for Session Request after Retry: " + _currentState);
if (_log.shouldDebug())
_log.debug("Got session request after retry from: " + _aliceSocketAddress);
private void locked_receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException {
DatagramPacket pkt = packet.getPacket();
SocketAddress from = pkt.getSocketAddress();
if (!from.equals(_aliceSocketAddress))
@@ -556,6 +561,26 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
long sid = DataHelper.fromLong8(data, off + 16);
if (sid != _sendConnID)
throw new GeneralSecurityException("Conn ID mismatch: 1: " + _sendConnID + " 2: " + sid);
int type = data[off + TYPE_OFFSET] & 0xff;
if (_currentState != InboundState.IB_STATE_RETRY_SENT) {
// not fatal
if (_log.shouldWarn())
_log.warn("Got out-of-order or retx msg " + type + " on: " + this);
return;
}
if (type == TOKEN_REQUEST_FLAG_BYTE) {
// retransmitted token request
if (_log.shouldWarn())
_log.warn("Got retx token request on: " + this);
// Est. mgr will resend retry and call retryPacketSent()
long now = _context.clock().now();
// rate limit
_nextSend = Math.max(now, _lastSend + 750);
return;
}
if (_log.shouldDebug())
_log.debug("Got session request after retry on: " + this);
long token = DataHelper.fromLong8(data, off + 24);
if (token != _token) {
// most likely a retransmitted session request with the old invalid token

View File

@@ -897,14 +897,19 @@ class PacketHandler {
header.getType() != SSU2Util.SESSION_REQUEST_FLAG_BYTE ||
header.getVersion() != 2 ||
header.getNetID() != _networkID) {
if (_log.shouldWarn()) {
if (header == null) {
// packet too short, possibly token-request-after-retry? let's see...
header = SSU2Header.trialDecryptLongHeader(packet, k1, k2);
}
_log.warn("Failed decrypt Session Request after Retry: " + header + " on " + state);
// possibly token-request-after-retry? let's see...
header = SSU2Header.trialDecryptLongHeader(packet, k1, k2);
if (header == null ||
header.getType() != SSU2Util.TOKEN_REQUEST_FLAG_BYTE ||
header.getVersion() != 2 ||
header.getNetID() != _networkID) {
// i2pd short session request? TODO
if (_log.shouldWarn())
_log.warn("Failed decrypt Session/Token Request after Retry: " + header +
" len " + packet.getPacket().getLength() + " on " + state);
return false;
}
return false;
//yes, retransmitted token request
}
if (header.getSrcConnID() != state.getSendConnID()) {
if (_log.shouldWarn())
@@ -918,7 +923,7 @@ class PacketHandler {
_log.warn("Bad Dest Conn id " + header + " on " + state);
return false;
}
type = SSU2Util.SESSION_REQUEST_FLAG_BYTE;
type = header.getType();
} else {
// Session Confirmed or retransmitted Session Request or Token Request
header = SSU2Header.trialDecryptShortHeader(packet, k1, k2);