From 6de81d41d2bac38076a744dca23e8cba4c35a0b8 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 17 Apr 2014 18:52:40 +0000
Subject: [PATCH] SSU: SessionRequest replay prevention (ticket #1212) NTCP:
 Just use first 8 bytes of Hx^Hi for replay check

---
 .../i2p/router/transport/ntcp/NTCPTransport.java  |  4 ++--
 .../transport/udp/EstablishmentManager.java       | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
index 77b1332018..053ab176ec 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
@@ -178,7 +178,7 @@ public class NTCPTransport extends TransportImpl {
         _establishing = new ConcurrentHashSet<NTCPConnection>(16);
         _conLock = new Object();
         _conByIdent = new ConcurrentHashMap<Hash, NTCPConnection>(64);
-        _replayFilter = new DecayingHashSet(ctx, 10*60*1000, 32, "NTCP-Hx^HI");
+        _replayFilter = new DecayingHashSet(ctx, 10*60*1000, 8, "NTCP-Hx^HI");
 
         _finisher = new NTCPSendFinisher(ctx, this);
 
@@ -522,7 +522,7 @@ public class NTCPTransport extends TransportImpl {
      *  @since 0.9.12
      */
     boolean isHXHIValid(byte[] hxhi) {
-        return !_replayFilter.add(hxhi);
+        return !_replayFilter.add(hxhi, 0, 8);
     }
 
     private static final int MIN_CONCURRENT_READERS = 2;  // unless < 32MB
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 b471a3c971..41b0eeddd6 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -23,6 +23,8 @@ import net.i2p.router.RouterContext;
 import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
 import static net.i2p.router.transport.udp.InboundEstablishState.InboundState.*;
 import static net.i2p.router.transport.udp.OutboundEstablishState.OutboundState.*;
+import net.i2p.router.util.DecayingHashSet;
+import net.i2p.router.util.DecayingBloomFilter;
 import net.i2p.util.Addresses;
 import net.i2p.util.I2PThread;
 import net.i2p.util.Log;
@@ -82,6 +84,9 @@ class EstablishmentManager {
     private volatile boolean _alive;
     private final Object _activityLock;
     private int _activity;
+
+    /** "bloom filter" */
+    private final DecayingBloomFilter _replayFilter;
     
     /** max outbound in progress - max inbound is half of this */
     private final int DEFAULT_MAX_CONCURRENT_ESTABLISH;
@@ -132,6 +137,7 @@ class EstablishmentManager {
         _outboundByClaimedAddress = new ConcurrentHashMap<RemoteHostId, OutboundEstablishState>();
         _outboundByHash = new ConcurrentHashMap<Hash, OutboundEstablishState>();
         _activityLock = new Object();
+        _replayFilter = new DecayingHashSet(ctx, 10*60*1000, 8, "SSU-DH-X");
         DEFAULT_MAX_CONCURRENT_ESTABLISH = Math.max(DEFAULT_LOW_MAX_CONCURRENT_ESTABLISH,
                                                     Math.min(DEFAULT_HIGH_MAX_CONCURRENT_ESTABLISH,
                                                              ctx.bandwidthLimiter().getOutboundKBytesPerSecond() / 2));
@@ -159,6 +165,7 @@ class EstablishmentManager {
         _context.statManager().createRateStat("udp.rejectConcurrentSequence", "How many consecutive concurrency rejections have we had when we stop rejecting (period is how many concurrent packets we are on)", "udp", UDPTransport.RATES);
         //_context.statManager().createRateStat("udp.queueDropSize", "How many messages were queued up when it was considered full, causing a tail drop?", "udp", UDPTransport.RATES);
         //_context.statManager().createRateStat("udp.queueAllowTotalLifetime", "When a peer is retransmitting and we probabalistically allow a new message, what is the sum of the pending message lifetimes? (period is the new message's lifetime)?", "udp", UDPTransport.RATES);
+        _context.statManager().createRateStat("udp.dupDHX", "Session request replay", "udp", new long[] { 24*60*60*1000L } );
     }
     
     public synchronized void startup() {
@@ -450,6 +457,14 @@ class EstablishmentManager {
                                                   _transport.getExternalPort(fromIP.length == 16),
                                                   _transport.getDHBuilder());
                 state.receiveSessionRequest(reader.getSessionRequestReader());
+
+                if (_replayFilter.add(state.getReceivedX(), 0, 8)) {
+                    if (_log.shouldLog(Log.WARN))
+                        _log.warn("Duplicate X in session request from: " + from);
+                    _context.statManager().addRateData("udp.dupDHX", 1);
+                    return; // drop the packet
+                }
+
                 InboundEstablishState oldState = _inboundStates.putIfAbsent(from, state);
                 isNew = oldState == null;
                 if (!isNew)
-- 
GitLab