From 3ce669575fcfcd40c8446fdaa7951c806d94bf1c Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 6 Mar 2022 07:31:16 -0500 Subject: [PATCH] SSU2: Fixes part 2 Add method to put additional blocks in data messages Send and handle termination blocks --- .../transport/udp/EstablishmentManager.java | 9 +++++ .../transport/udp/InboundEstablishState2.java | 23 ++++++++++--- .../udp/OutboundEstablishState2.java | 18 ++++++++-- .../router/transport/udp/PacketBuilder2.java | 34 ++++++++++++++++++- .../i2p/router/transport/udp/PeerState2.java | 5 +-- .../router/transport/udp/UDPTransport.java | 14 +++++--- 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 7fba35213..ae59271ef 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -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) { diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java index d0687eee6..746d1e94e 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -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; } diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java index ea2ecaf48..48e05f34b 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java @@ -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(); } /** diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java index 4977c9d7d..189302bbb 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java @@ -133,6 +133,15 @@ class PacketBuilder2 { * */ public UDPPacket buildPacket(List 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 fragments, List 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 blocks = new ArrayList(fragments.size() + 2); + int bcnt = fragments.size() + 2; + if (otherBlocks != null) + bcnt += otherBlocks.size(); + List blocks = new ArrayList(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 diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState2.java b/router/java/src/net/i2p/router/transport/udp/PeerState2.java index c863aa7b5..aba46fd9d 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java @@ -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) { 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 771c3f85d..da1742394 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -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);