SSU2: Fixes part 2

Add method to put additional blocks in data messages
Send and handle termination blocks
This commit is contained in:
zzz
2022-03-06 07:31:16 -05:00
parent 63aa64f8bb
commit 3ce669575f
6 changed files with 89 additions and 14 deletions

View File

@@ -775,6 +775,9 @@ class EstablishmentManager {
/**
* Got a SessionDestroy on an established conn
*
* SSU 1 or 2
*
* @since 0.8.1
*/
void receiveSessionDestroy(RemoteHostId from, PeerState state) {
@@ -785,6 +788,9 @@ class EstablishmentManager {
/**
* Got a SessionDestroy during outbound establish
*
* SSU 1 or 2
*
* @since 0.8.1
*/
void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state) {
@@ -800,6 +806,9 @@ class EstablishmentManager {
* TODO - PacketHandler won't look up inbound establishes
* As this packet was essentially unauthenticated (i.e. intro key, not session key)
* we just log it as it could be spoofed.
*
* SSU 1 or 2
*
* @since 0.8.1
*/
void receiveSessionDestroy(RemoteHostId from) {

View File

@@ -144,6 +144,10 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
_currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
}
if (_currentState == InboundState.IB_STATE_FAILED) {
// termination block received
throw new GeneralSecurityException("Termination block in Session/Token Request");
}
if (_timeReceived == 0)
throw new GeneralSecurityException("No DateTime block in Session/Token Request");
long skew = _establishBegin - _timeReceived;
@@ -308,7 +312,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
}
public void gotTermination(int reason, long count) {
throw new IllegalStateException("Termination in Handshake");
if (_log.shouldWarn())
_log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
// this sets the state to FAILED
fail();
_transport.getEstablisher().receiveSessionDestroy(_remoteHostId);
}
public void gotUnknown(int type, int len) {
@@ -418,6 +426,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
_log.debug("State after sess req: " + _handshakeState);
_timeReceived = 0;
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
packetReceived();
if (_currentState == InboundState.IB_STATE_FAILED) {
// termination block received
throw new GeneralSecurityException("Termination block in Session Request");
}
if (_timeReceived == 0)
throw new GeneralSecurityException("No DateTime block in Session Request");
long skew = _establishBegin - _timeReceived;
@@ -426,8 +439,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
_currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
_rtt = (int) ( _context.clock().now() - _lastSend );
packetReceived();
}
/**
@@ -463,6 +474,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
if (_log.shouldDebug())
_log.debug("State after sess conf: " + _handshakeState);
processPayload(data, off + SHORT_HEADER_SIZE, len - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + MAC_LEN), false);
packetReceived();
if (_currentState == InboundState.IB_STATE_FAILED) {
// termination block received
throw new GeneralSecurityException("Termination block in Session Confirmed");
}
_sessCrForReTX = null;
if (_receivedConfirmedIdentity == null)
@@ -471,7 +487,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
// createPeerState() called from gotRI()
_currentState = InboundState.IB_STATE_CONFIRMED_COMPLETELY;
packetReceived();
return _pstate;
}

View File

@@ -234,7 +234,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
}
public void gotTermination(int reason, long count) {
throw new IllegalStateException("Termination in Sess Created");
if (_log.shouldWarn())
_log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
// this sets the state to FAILED
fail();
_transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
}
public void gotUnknown(int type, int len) {
@@ -323,6 +327,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
_log.debug("Retry error", gse);
throw gse;
}
packetReceived();
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
// termination block received
return;
}
if (_timeReceived == 0)
throw new GeneralSecurityException("No DateTime block in Session/Token Request");
long skew = _establishBegin - _timeReceived;
@@ -330,7 +339,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
throw new GeneralSecurityException("Skew exceeded in Session/Token Request: " + skew);
createNewState(_routerAddress);
_currentState = OutboundState.OB_STATE_RETRY_RECEIVED;
packetReceived();
}
public synchronized void receiveSessionCreated(UDPPacket packet) throws GeneralSecurityException {
@@ -371,6 +379,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
_log.debug("State after sess cr: " + _handshakeState);
_timeReceived = 0;
processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true);
packetReceived();
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
// termination block received
return;
}
if (_timeReceived == 0)
throw new GeneralSecurityException("No DateTime block in Session/Token Request");
long skew = _establishBegin - _timeReceived;
@@ -384,7 +397,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
if (_requestSentCount == 1) {
_rtt = (int) (_context.clock().now() - _requestSentTime);
}
packetReceived();
}
/**

View File

@@ -133,6 +133,15 @@ class PacketBuilder2 {
*
*/
public UDPPacket buildPacket(List<Fragment> fragments, PeerState2 peer) {
return buildPacket(fragments, null, peer);
}
/*
* Multiple fragments and optional other blocks.
*
* @param otherBlocks may be null or empty
*/
public UDPPacket buildPacket(List<Fragment> fragments, List<Block> otherBlocks, PeerState2 peer) {
// calculate data size
int numFragments = fragments.size();
int dataSize = 0;
@@ -172,7 +181,10 @@ class PacketBuilder2 {
// ok, now for the body...
// +2 for acks and padding
List<Block> blocks = new ArrayList<Block>(fragments.size() + 2);
int bcnt = fragments.size() + 2;
if (otherBlocks != null)
bcnt += otherBlocks.size();
List<Block> blocks = new ArrayList<Block>(bcnt);
int sizeWritten = 0;
// add the acks
@@ -207,6 +219,17 @@ class PacketBuilder2 {
off += sz;
sizeWritten += sz;
}
// now the other blocks, if any
if (otherBlocks != null) {
for (Block block : otherBlocks) {
blocks.add(block);
int sz = block.getTotalLength();
off += sz;
sizeWritten += sz;
}
}
// FIXME
Block block = getPadding(sizeWritten, currentMTU);
if (block != null) {
@@ -280,6 +303,15 @@ class PacketBuilder2 {
public UDPPacket buildACK(PeerState2 peer) {
return buildPacket(Collections.emptyList(), peer);
}
/**
* Build a data packet with a termination block.
* This will also include acks and padding.
*/
public UDPPacket buildSessionDestroyPacket(int reason, PeerState2 peer) {
Block block = new SSU2Payload.TerminationBlock(reason, peer.getReceivedMessages().getHighestSet());
return buildPacket(Collections.emptyList(), Collections.singletonList(block), peer);
}
/**
* Build a new SessionRequest packet for the given peer, encrypting it

View File

@@ -350,8 +350,9 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
}
public void gotTermination(int reason, long count) {
if (_log.shouldDebug())
_log.debug("Got TERMINATION block, reason: " + reason + " count: " + count);
if (_log.shouldWarn())
_log.warn("Got TERMINATION block, reason: " + reason + " count: " + count);
_transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
}
public void gotUnknown(int type, int len) {

View File

@@ -2126,10 +2126,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.8.9
*/
void sendDestroy(PeerState peer) {
// peer must be fully established
if (peer.getCurrentCipherKey() == null)
return;
UDPPacket pkt = _packetBuilder.buildSessionDestroyPacket(peer);
UDPPacket pkt;
if (peer.getVersion() == 1) {
// peer must be fully established
if (peer.getCurrentCipherKey() == null)
return;
pkt = _packetBuilder.buildSessionDestroyPacket(peer);
} else {
// unspecified reason
pkt = _packetBuilder2.buildSessionDestroyPacket(0, (PeerState2) peer);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending destroy to : " + peer);
send(pkt);