This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new a20c89bfab Revert the replay protection. The deisgn had fundamental 
issues.
a20c89bfab is described below

commit a20c89bfab1b57cff603f5b5335a8a6f9b2e9c2a
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Jun 17 15:13:40 2026 +0100

    Revert the replay protection. The deisgn had fundamental issues.
    
    A new approach will be required.
---
 .../group/interceptors/EncryptInterceptor.java     | 136 +---------
 .../interceptors/EncryptInterceptorMBean.java      |  14 -
 .../group/interceptors/LocalStrings.properties     |   2 -
 .../apache/catalina/tribes/util/CyclicTracker.java | 121 ---------
 .../catalina/tribes/util/LocalStrings.properties   |   1 -
 .../group/interceptors/TestEncryptInterceptor.java | 282 ++++-----------------
 .../catalina/tribes/util/TestCyclicTracker.java    | 129 ----------
 webapps/docs/changelog.xml                         |   3 -
 webapps/docs/config/cluster-interceptor.xml        |  14 +-
 9 files changed, 70 insertions(+), 632 deletions(-)

diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java 
b/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
index 1a678e509b..1708c4a9a9 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
+++ b/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
@@ -22,10 +22,7 @@ import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicLong;
 
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
@@ -41,7 +38,6 @@ import org.apache.catalina.tribes.Member;
 import org.apache.catalina.tribes.group.ChannelInterceptorBase;
 import org.apache.catalina.tribes.group.InterceptorPayload;
 import org.apache.catalina.tribes.io.XByteBuffer;
-import org.apache.catalina.tribes.util.CyclicTracker;
 import org.apache.catalina.tribes.util.StringManager;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
@@ -66,7 +62,6 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
     private String encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM;
     private byte[] encryptionKeyBytes;
     private String encryptionKeyString;
-    private int replayWindowSize = 1024;
 
 
     private BaseEncryptionManager encryptionManager;
@@ -84,7 +79,7 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
         if (Channel.SND_TX_SEQ == (svc & Channel.SND_TX_SEQ)) {
             try {
                 encryptionManager = 
createEncryptionManager(getEncryptionAlgorithm(), getEncryptionKeyInternal(),
-                        getProviderName(), getReplayWindowSize());
+                        getProviderName());
             } catch (GeneralSecurityException gse) {
                 throw new 
ChannelException(sm.getString("encryptInterceptor.init.failed"), gse);
             }
@@ -118,12 +113,9 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
             throws ChannelException {
         try {
             byte[] data = msg.getMessage().getBytes();
-            byte[] message = new byte[data.length + 8];
-            
XByteBuffer.toBytes(encryptionManager.getAndIncrementMessageNumber(), message, 
0);
-            System.arraycopy(data, 0, message, 8, data.length);
 
             // See #encrypt(byte[]) for an explanation of the return value
-            byte[][] bytes = encryptionManager.encrypt(message);
+            byte[][] bytes = encryptionManager.encrypt(data);
 
             XByteBuffer xbb = msg.getMessage();
 
@@ -146,19 +138,12 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
             byte[] data = msg.getMessage().getBytes();
 
             data = encryptionManager.decrypt(data);
-            if (data.length < 8) {
-                throw new 
GeneralSecurityException(sm.getString("encryptInterceptor.decrypt.error.short-message"));
-            }
-            if 
(!encryptionManager.checkIncomingMessageNumber(msg.getAddress(), 
XByteBuffer.toLong(data, 0))) {
-                log.error(sm.getString("encryptInterceptor.decrypt.replay"));
-                return;
-            }
 
             XByteBuffer xbb = msg.getMessage();
 
             // Completely replace the message with the decrypted one
             xbb.clear();
-            xbb.append(data, 8, data.length - 8);
+            xbb.append(data, 0, data.length);
 
             super.messageReceived(msg);
         } catch (GeneralSecurityException gse) {
@@ -166,14 +151,6 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
         }
     }
 
-    @Override
-    public void memberDisappeared(Member member) {
-        if (encryptionManager != null) {
-            encryptionManager.memberDisappeared(member);
-        }
-        super.memberDisappeared(member);
-    }
-
     /**
      * Sets the encryption algorithm to be used for encrypting and decrypting 
channel messages. You must specify the
      * <code>algorithm/mode/padding</code>. Information on standard algorithm 
names may be found in the
@@ -297,36 +274,6 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
         return providerName;
     }
 
-    /**
-     * Returns the number of message sequence numbers remembered for replay 
detection.
-     *
-     * @return The replay window size
-     */
-    @Override
-    public int getReplayWindowSize() {
-        return replayWindowSize;
-    }
-
-    /**
-     * Sets the number of message sequence numbers remembered for replay 
detection.
-     *
-     * @param replayWindowSize The replay window size
-     */
-    @Override
-    public void setReplayWindowSize(int replayWindowSize) {
-        if (replayWindowSize < 1) {
-            throw new 
IllegalArgumentException(sm.getString("encryptInterceptor.replayWindow.tooSmall"));
-        }
-        this.replayWindowSize = replayWindowSize;
-    }
-
-    Long getRemovedMemberHeadValue(Member member) {
-        if (encryptionManager == null) {
-            return null;
-        }
-        return encryptionManager.getRemovedMemberHeadValue(member);
-    }
-
     // Copied from org.apache.tomcat.util.buf.HexUtils
     // @formatter:off
     private static final int[] DEC = {
@@ -373,8 +320,7 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
     }
 
     private static BaseEncryptionManager createEncryptionManager(String 
algorithm, byte[] encryptionKey,
-            String providerName, int replayWindowSize)
-            throws NoSuchAlgorithmException, NoSuchPaddingException, 
NoSuchProviderException {
+            String providerName) throws NoSuchAlgorithmException, 
NoSuchPaddingException, NoSuchProviderException {
         if (null == encryptionKey) {
             throw new 
IllegalStateException(sm.getString("encryptInterceptor.key.required"));
         }
@@ -413,7 +359,8 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
          */
         if ("NONE".equals(algorithmMode) || "ECB".equals(algorithmMode) || 
"PCBC".equals(algorithmMode) ||
                 "CTS".equals(algorithmMode) || "KW".equals(algorithmMode) || 
"KWP".equals(algorithmMode) ||
-                "CTR".equals(algorithmMode) || ("CBC".equals(algorithmMode) && 
"NOPADDING".equals(algorithmPadding)) ||
+                "CTR".equals(algorithmMode) ||
+                ("CBC".equals(algorithmMode) && 
"NOPADDING".equals(algorithmPadding)) ||
                 ("CFB".equals(algorithmMode) && 
"NOPADDING".equals(algorithmPadding)) ||
                 ("GCM".equals(algorithmMode) && 
"PKCS5PADDING".equals(algorithmPadding)) ||
                 ("OFB".equals(algorithmMode) && 
"NOPADDING".equals(algorithmPadding))) {
@@ -428,18 +375,17 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
 
         } else if (algorithmMode.startsWith("CFB") || 
algorithmMode.startsWith("OFB")) {
             // Using a non-default block size. Not supported as insecure 
and/or inefficient.
-            throw new 
IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported",
 algorithm));
+            throw new IllegalArgumentException(
+                    sm.getString("encryptInterceptor.algorithm.unsupported", 
algorithm));
 
         } else if ("GCM".equals(algorithmMode) && 
"NOPADDING".equals(algorithmPadding)) {
             // Needs a specialised encryption manager to handle the 
differences between GCM and other modes
-            return new GCMEncryptionManager(algorithm, new 
SecretKeySpec(encryptionKey, algorithmName), providerName,
-                    replayWindowSize);
+            return new GCMEncryptionManager(algorithm, new 
SecretKeySpec(encryptionKey, algorithmName), providerName);
         }
 
         // Use the default encryption manager
         try {
-            return new BaseEncryptionManager(algorithm, new 
SecretKeySpec(encryptionKey, algorithmName), providerName,
-                    replayWindowSize);
+            return new BaseEncryptionManager(algorithm, new 
SecretKeySpec(encryptionKey, algorithmName), providerName);
         } catch (NoSuchAlgorithmException | NoSuchPaddingException | 
NoSuchProviderException ex) {
             throw new 
IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported",
 algorithm), ex);
         }
@@ -477,80 +423,24 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
          * SecureRandom is thread-safe, but sharing a single instance will 
likely be a bottleneck.
          */
         private final ConcurrentLinkedQueue<SecureRandom> randomPool;
-        private final AtomicLong messageNumberGenerator = new AtomicLong();
-        private final Map<Member,CyclicTracker> receivedMessageNumbersByMember 
= new ConcurrentHashMap<>();
-        private final Map<Member,Long> messageNumbersByRemovedMember = new 
ConcurrentHashMap<>();
-        private final CyclicTracker receivedMessageNumbersForUnknownSender;
-        private final int replayWindowSize;
 
-        BaseEncryptionManager(String algorithm, SecretKeySpec secretKey, 
String providerName, int replayWindowSize)
+        BaseEncryptionManager(String algorithm, SecretKeySpec secretKey, 
String providerName)
                 throws NoSuchAlgorithmException, NoSuchPaddingException, 
NoSuchProviderException {
             this.algorithm = algorithm;
             this.providerName = providerName;
             this.secretKey = secretKey;
-            this.replayWindowSize = replayWindowSize;
 
             cipherPool = new ConcurrentLinkedQueue<>();
             Cipher cipher = createCipher();
             blockSize = cipher.getBlockSize();
             cipherPool.offer(cipher);
             randomPool = new ConcurrentLinkedQueue<>();
-            receivedMessageNumbersForUnknownSender = new 
CyclicTracker(replayWindowSize);
         }
 
         public void shutdown() {
             // Individual Cipher and SecureRandom objects need no explicit 
tear down
             cipherPool.clear();
             randomPool.clear();
-            receivedMessageNumbersByMember.clear();
-            messageNumbersByRemovedMember.clear();
-        }
-
-        public long getAndIncrementMessageNumber() {
-            return messageNumberGenerator.getAndIncrement();
-        }
-
-        public boolean checkIncomingMessageNumber(Member sender, long 
messageNumber) {
-            if (sender == null) {
-                return 
receivedMessageNumbersForUnknownSender.track(messageNumber);
-            }
-            return receivedMessageNumbersByMember.computeIfAbsent(sender, 
this::createTrackerForMember)
-                    .track(messageNumber);
-        }
-
-        public void memberDisappeared(Member member) {
-            CyclicTracker tracker = 
receivedMessageNumbersByMember.remove(member);
-            if (tracker != null) {
-                /*
-                 * There is a security trade off here.
-                 *
-                 * Entries are only removed from this Map if the Member 
reappears. That means there is a potential DoS
-                 * risks due to the growth of this Map. That is considered 
unlikely as only Members with the encryption
-                 * key will be added to this Map and the size of the Map.Entry 
is minimal.
-                 *
-                 * If entries are removed from this Map based either on Map 
size or time, that exposes the risk of a
-                 * replay attack using any message the Member may have 
previously sent.
-                 *
-                 * The replay attack is viewed as the higher risk, hence there 
are no limits on the size of this Map.
-                 */
-                messageNumbersByRemovedMember.put(member, 
Long.valueOf(tracker.getHeadValue()));
-            }
-        }
-
-        public Long getRemovedMemberHeadValue(Member member) {
-            return messageNumbersByRemovedMember.get(member);
-        }
-
-        private CyclicTracker createTrackerForMember(Member member) {
-            CyclicTracker tracker = new CyclicTracker(replayWindowSize);
-            Long headValue = messageNumbersByRemovedMember.remove(member);
-            if (headValue == null) {
-                // This is a new member. First valid message will be 0. 
Therefore set last message to -1.
-                tracker.track(-1);
-            } else {
-                tracker.track(headValue.longValue());
-            }
-            return tracker;
         }
 
         private String getAlgorithm() {
@@ -721,9 +611,9 @@ public class EncryptInterceptor extends 
ChannelInterceptorBase implements Encryp
      * number of bits supported 128-bit provide the best security.
      */
     private static class GCMEncryptionManager extends BaseEncryptionManager {
-        GCMEncryptionManager(String algorithm, SecretKeySpec secretKey, String 
providerName, int replayWindowSize)
+        GCMEncryptionManager(String algorithm, SecretKeySpec secretKey, String 
providerName)
                 throws NoSuchAlgorithmException, NoSuchPaddingException, 
NoSuchProviderException {
-            super(algorithm, secretKey, providerName, replayWindowSize);
+            super(algorithm, secretKey, providerName);
         }
 
         @Override
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
 
b/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
index e6fd2fb4ef..7d10a1f4f1 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptorMBean.java
@@ -76,18 +76,4 @@ public interface EncryptInterceptorMBean {
      * @return the JCA provider name, or {@code null} for default
      */
     String getProviderName();
-
-    /**
-     * Returns the number of message sequence numbers remembered for replay 
detection.
-     *
-     * @return the replay window size
-     */
-    int getReplayWindowSize();
-
-    /**
-     * Sets the number of message sequence numbers remembered for replay 
detection.
-     *
-     * @param replayWindowSize the replay window size
-     */
-    void setReplayWindowSize(int replayWindowSize);
 }
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties 
b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
index 2bbb511f1d..6030d789f5 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
@@ -24,11 +24,9 @@ encryptInterceptor.algorithm.switch=The EncryptInterceptor 
is using the algorith
 encryptInterceptor.algorithm.unsupported=EncryptInterceptor does not support 
algorithm [{0}]
 encryptInterceptor.decrypt.error.short-message=Failed to decrypt message: 
premature end-of-message
 encryptInterceptor.decrypt.failed=Failed to decrypt message
-encryptInterceptor.decrypt.replay=Failed to decrypt message: replay attack 
detected
 encryptInterceptor.encrypt.failed=Failed to encrypt message
 encryptInterceptor.init.failed=Failed to initialize EncryptInterceptor
 encryptInterceptor.key.required=Encryption key is required
-encryptInterceptor.replayWindow.tooSmall=replayWindowSize must be greater than 
zero
 encryptInterceptor.tcpFailureDetector.ordering=EncryptInterceptor must be 
upstream of TcpFailureDetector. Please re-order EncryptInterceptor to be listed 
before TcpFailureDetector in your channel interceptor pipeline.
 
 fragmentationInterceptor.fragments.missing=Fragments are missing.
diff --git a/java/org/apache/catalina/tribes/util/CyclicTracker.java 
b/java/org/apache/catalina/tribes/util/CyclicTracker.java
deleted file mode 100644
index 509df9e580..0000000000
--- a/java/org/apache/catalina/tribes/util/CyclicTracker.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.catalina.tribes.util;
-
-/**
- * Tracks recently observed integers in a fixed-size cyclic window.
- * <p>
- * Values are ordered using the natural {@code long} sequence with overflow 
handling so {@code Long.MAX_VALUE} is
- * followed by {@code Long.MIN_VALUE}.
- */
-public class CyclicTracker {
-
-    private static final StringManager sm = 
StringManager.getManager(CyclicTracker.class);
-
-    private final boolean[] seen;
-
-    private boolean initialized = false;
-    private long headValue;
-    private int headIndex;
-
-
-    public CyclicTracker(int size) {
-        if (size < 1) {
-            throw new 
IllegalArgumentException(sm.getString("cyclicTracker.size.tooSmall"));
-        }
-        seen = new boolean[size];
-    }
-
-
-    /**
-     * Tracks the provided value.
-     *
-     * @param value The value to track
-     *
-     * @return {@code true} if the value had not previously been seen and is 
within the current tracking window,
-     *             otherwise {@code false}
-     */
-    public synchronized boolean track(long value) {
-        if (!initialized) {
-            initialized = true;
-            headValue = value;
-            headIndex = 0;
-            seen[0] = true;
-            return true;
-        }
-
-        long behind = headValue - value;
-        if (behind >= 0) {
-            if (behind >= seen.length) {
-                return false;
-            }
-            int index = toIndex(headIndex - (int) behind);
-            if (seen[index]) {
-                return false;
-            }
-            seen[index] = true;
-            return true;
-        }
-
-        advance(distance(headValue, value));
-
-        headValue = value;
-        seen[headIndex] = true;
-        return true;
-    }
-
-    /**
-     * Returns the current head value.
-     *
-     * @return The current head value
-     */
-    public synchronized long getHeadValue() {
-        if (!initialized) {
-            throw new IllegalStateException("No value has been tracked");
-        }
-        return headValue;
-    }
-
-
-    private void advance(long delta) {
-        if (Long.compareUnsigned(delta, seen.length) >= 0) {
-            java.util.Arrays.fill(seen, false);
-            headIndex = toIndex(headIndex + (int) (delta % seen.length));
-            return;
-        }
-
-        for (int i = 1; i <= (int) delta; i++) {
-            int index = toIndex(headIndex + i);
-            seen[index] = false;
-        }
-        headIndex = toIndex(headIndex + (int) delta);
-    }
-
-
-    private int toIndex(int value) {
-        int result = value % seen.length;
-        if (result < 0) {
-            result += seen.length;
-        }
-        return result;
-    }
-
-
-    private long distance(long from, long to) {
-        return to - from;
-    }
-}
diff --git a/java/org/apache/catalina/tribes/util/LocalStrings.properties 
b/java/org/apache/catalina/tribes/util/LocalStrings.properties
index a9acf46578..d26bc13628 100644
--- a/java/org/apache/catalina/tribes/util/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/util/LocalStrings.properties
@@ -21,7 +21,6 @@ arrays.length.outOfBounds=not enough data elements in the 
key, length is out of
 arrays.malformed.arrays=byte arrays must be represented as {1,3,4,5,6}
 arrays.srcoffset.outOfBounds=srcoffset is out of bounds.
 
-cyclicTracker.size.tooSmall=size must be greater than zero
 executorFactory.not.running=Executor not running, can't force a command into 
the queues
 executorFactory.queue.full=Queue capacity is full.
 
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
index 2c03dc4c38..e30e84321f 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestEncryptInterceptor.java
@@ -37,13 +37,13 @@ import org.apache.catalina.tribes.Channel;
 import org.apache.catalina.tribes.ChannelException;
 import org.apache.catalina.tribes.io.ChannelData;
 import org.apache.catalina.tribes.io.XByteBuffer;
-import org.apache.catalina.tribes.membership.MemberImpl;
 
 /**
  * Tests the EncryptInterceptor.
- * <p>
- * Many of the tests in this class use strings as input and output, even 
though the interceptor actually operates on
- * byte arrays. This is done for readability for the tests and their outputs.
+ *
+ * Many of the tests in this class use strings as input and output, even
+ * though the interceptor actually operates on byte arrays. This is done
+ * for readability for the tests and their outputs.
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class TestEncryptInterceptor extends EncryptionInterceptorBaseTest {
@@ -55,7 +55,9 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "The quick brown fox jumps over the lazy dog.";
 
-        Assert.assertEquals("Basic roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Basic roundtrip failed",
+                     testInput,
+                     roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -65,15 +67,25 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "The quick brown fox jumps over the lazy dog.";
 
-        Assert.assertEquals("Basic roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Basic roundtrip failed",
+                     testInput,
+                     roundTrip(testInput, src, dest));
 
-        Assert.assertEquals("Second roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Second roundtrip failed",
+                testInput,
+                roundTrip(testInput, src, dest));
 
-        Assert.assertEquals("Third roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Third roundtrip failed",
+                testInput,
+                roundTrip(testInput, src, dest));
 
-        Assert.assertEquals("Fourth roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Fourth roundtrip failed",
+                testInput,
+                roundTrip(testInput, src, dest));
 
-        Assert.assertEquals("Fifth roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Fifth roundtrip failed",
+                testInput,
+                roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -83,7 +95,9 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "x";
 
-        Assert.assertEquals("Tiny payload roundtrip failed", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Tiny payload roundtrip failed",
+                     testInput,
+                     roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -91,9 +105,11 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
         src.start(Channel.SND_TX_SEQ);
         dest.start(Channel.SND_TX_SEQ);
 
-        byte[] bytes = new byte[1024 * 1024];
+        byte[] bytes = new byte[1024*1024];
 
-        Assert.assertArrayEquals("Huge payload roundtrip failed", bytes, 
roundTrip(bytes, src, dest));
+        Assert.assertArrayEquals("Huge payload roundtrip failed",
+                          bytes,
+                          roundTrip(bytes, src, dest));
     }
 
     @Test
@@ -105,7 +121,9 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "The quick brown fox jumps over the lazy dog.";
 
-        Assert.assertEquals("Failed to set custom provider name", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Failed to set custom provider name",
+                     testInput,
+                     roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -120,7 +138,9 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "The quick brown fox jumps over the lazy dog.";
 
-        Assert.assertEquals("Failed to set custom provider name", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Failed to set custom provider name",
+                     testInput,
+                     roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -135,7 +155,9 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
 
         String testInput = "The quick brown fox jumps over the lazy dog.";
 
-        Assert.assertEquals("Failed to set custom provider name", testInput, 
roundTrip(testInput, src, dest));
+        Assert.assertEquals("Failed to set custom provider name",
+                     testInput,
+                     roundTrip(testInput, src, dest));
     }
 
     @Test
@@ -149,7 +171,7 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
         msg.setMessage(new XByteBuffer(testInput.getBytes("UTF-8"), false));
         src.sendMessage(null, msg, null);
 
-        byte[] bytes = ((ValueCaptureInterceptor) src.getNext()).getValue();
+        byte[] bytes = ((ValueCaptureInterceptor)src.getNext()).getValue();
 
         try (FileOutputStream out = new FileOutputStream(MESSAGE_FILE)) {
             out.write(bytes);
@@ -183,213 +205,21 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
         msg.setMessage(new XByteBuffer(testInput.getBytes("UTF-8"), false));
         src.sendMessage(null, msg, null);
 
-        byte[] cipherText1 = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-
-        msg.setMessage(new XByteBuffer(testInput.getBytes("UTF-8"), false));
-        src.sendMessage(null, msg, null);
-
-        byte[] cipherText2 = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-
-        MatcherAssert.assertThat("Two identical cleartexts encrypt to the same 
ciphertext", cipherText1,
-                IsNot.not(IsEqual.equalTo(cipherText2)));
-    }
-
-    @Test
-    public void testRejectReplay() throws Exception {
-        src.setNext(new ValueCaptureInterceptor());
-        dest.setPrevious(new ValuesCaptureInterceptor());
-        src.start(Channel.SND_TX_SEQ);
-        dest.start(Channel.SND_TX_SEQ);
-        MemberImpl sender = createMember("127.0.0.1", 10001, 1);
-
-        String testInput = "The quick brown fox jumps over the lazy dog.";
+        byte[] cipherText1 = 
((ValueCaptureInterceptor)src.getNext()).getValue();
 
-        ChannelData msg = new ChannelData(false);
         msg.setMessage(new XByteBuffer(testInput.getBytes("UTF-8"), false));
         src.sendMessage(null, msg, null);
 
-        byte[] encrypted = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-
-        ChannelData incoming = new ChannelData(false);
-        XByteBuffer xbb = new XByteBuffer(encrypted.length, false);
-        xbb.append(encrypted, 0, encrypted.length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(sender);
-        dest.messageReceived(incoming);
-
-        incoming = new ChannelData(false);
-        xbb = new XByteBuffer(encrypted.length, false);
-        xbb.append(encrypted, 0, encrypted.length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(sender);
-        dest.messageReceived(incoming);
-
-        Collection<byte[]> messages = ((ValuesCaptureInterceptor) 
dest.getPrevious()).getValues();
-        Assert.assertEquals(1, messages.size());
-        Assert.assertArrayEquals(testInput.getBytes("UTF-8"), 
messages.iterator().next());
-    }
-
-    @Test
-    public void testReplayWindowRejectsOldMessage() throws Exception {
-        src.setNext(new ValueCaptureInterceptor());
-        dest.setPrevious(new ValuesCaptureInterceptor());
-        dest.setReplayWindowSize(2);
-        src.start(Channel.SND_TX_SEQ);
-        dest.start(Channel.SND_TX_SEQ);
-        MemberImpl sender = createMember("127.0.0.1", 10001, 1);
-
-        byte[][] encrypted = new byte[3][];
-        for (int i = 0; i < encrypted.length; i++) {
-            ChannelData msg = new ChannelData(false);
-            msg.setMessage(new XByteBuffer(Long.toString(i).getBytes("UTF-8"), 
false));
-            src.sendMessage(null, msg, null);
-            encrypted[i] = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-            ChannelData incoming = new ChannelData(false);
-            XByteBuffer xbb = new XByteBuffer(encrypted[i].length, false);
-            xbb.append(encrypted[i], 0, encrypted[i].length);
-            incoming.setMessage(xbb);
-            incoming.setAddress(sender);
-            dest.messageReceived(incoming);
-        }
-
-        ChannelData replay = new ChannelData(false);
-        XByteBuffer xbb = new XByteBuffer(encrypted[0].length, false);
-        xbb.append(encrypted[0], 0, encrypted[0].length);
-        replay.setMessage(xbb);
-        replay.setAddress(sender);
-        dest.messageReceived(replay);
-
-        Collection<byte[]> messages = ((ValuesCaptureInterceptor) 
dest.getPrevious()).getValues();
-        Assert.assertEquals(3, messages.size());
-    }
-
-    @Test
-    public void testAcceptSameMessageNumberFromDifferentMembers() throws 
Exception {
-        EncryptInterceptor src1 = new EncryptInterceptor();
-        src1.setEncryptionKey(encryptionKey128);
-        src1.setNext(new ValueCaptureInterceptor());
-        src1.start(Channel.SND_TX_SEQ);
-
-        EncryptInterceptor src2 = new EncryptInterceptor();
-        src2.setEncryptionKey(encryptionKey128);
-        src2.setNext(new ValueCaptureInterceptor());
-        src2.start(Channel.SND_TX_SEQ);
+        byte[] cipherText2 = 
((ValueCaptureInterceptor)src.getNext()).getValue();
 
-        dest.setPrevious(new ValuesCaptureInterceptor());
-        dest.start(Channel.SND_TX_SEQ);
-
-        ChannelData msg = new ChannelData(false);
-        msg.setMessage(new XByteBuffer("msg-1".getBytes("UTF-8"), false));
-        src1.sendMessage(null, msg, null);
-        byte[] encrypted1 = ((ValueCaptureInterceptor) 
src1.getNext()).getValue();
-
-        msg = new ChannelData(false);
-        msg.setMessage(new XByteBuffer("msg-2".getBytes("UTF-8"), false));
-        src2.sendMessage(null, msg, null);
-        byte[] encrypted2 = ((ValueCaptureInterceptor) 
src2.getNext()).getValue();
-
-        ChannelData incoming = new ChannelData(false);
-        XByteBuffer xbb = new XByteBuffer(encrypted1.length, false);
-        xbb.append(encrypted1, 0, encrypted1.length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(createMember("127.0.0.1", 10001, 1));
-        dest.messageReceived(incoming);
-
-        incoming = new ChannelData(false);
-        xbb = new XByteBuffer(encrypted2.length, false);
-        xbb.append(encrypted2, 0, encrypted2.length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(createMember("127.0.0.1", 10002, 2));
-        dest.messageReceived(incoming);
-
-        Collection<byte[]> messages = ((ValuesCaptureInterceptor) 
dest.getPrevious()).getValues();
-        Assert.assertEquals(2, messages.size());
-    }
-
-    @Test
-    public void testMemberDisappearedStoresHeadValue() throws Exception {
-        src.setNext(new ValueCaptureInterceptor());
-        dest.setPrevious(new ValuesCaptureInterceptor());
-        dest.setReplayWindowSize(4);
-        src.start(Channel.SND_TX_SEQ);
-        dest.start(Channel.SND_TX_SEQ);
-        MemberImpl sender = createMember("127.0.0.1", 10001, 1);
-
-        for (int i = 0; i < 3; i++) {
-            ChannelData msg = new ChannelData(false);
-            msg.setMessage(new XByteBuffer(Long.toString(i).getBytes("UTF-8"), 
false));
-            src.sendMessage(null, msg, null);
-            byte[] encrypted = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-
-            ChannelData incoming = new ChannelData(false);
-            XByteBuffer xbb = new XByteBuffer(encrypted.length, false);
-            xbb.append(encrypted, 0, encrypted.length);
-            incoming.setMessage(xbb);
-            incoming.setAddress(sender);
-            dest.messageReceived(incoming);
-        }
-
-        dest.memberDisappeared(sender);
-
-        Assert.assertEquals(Long.valueOf(2), 
dest.getRemovedMemberHeadValue(sender));
-    }
-
-    @Test
-    public void testRemovedMemberHeadValueInitializesReplacementTracker() 
throws Exception {
-        src.setNext(new ValueCaptureInterceptor());
-        dest.setPrevious(new ValuesCaptureInterceptor());
-        dest.setReplayWindowSize(4);
-        src.start(Channel.SND_TX_SEQ);
-        dest.start(Channel.SND_TX_SEQ);
-        MemberImpl sender = createMember("127.0.0.1", 10001, 1);
-
-        byte[][] encrypted = new byte[4][];
-        for (int i = 0; i < 3; i++) {
-            ChannelData msg = new ChannelData(false);
-            msg.setMessage(new XByteBuffer(Long.toString(i).getBytes("UTF-8"), 
false));
-            src.sendMessage(null, msg, null);
-            encrypted[i] = ((ValueCaptureInterceptor) 
src.getNext()).getValue();
-
-            ChannelData incoming = new ChannelData(false);
-            XByteBuffer xbb = new XByteBuffer(encrypted[i].length, false);
-            xbb.append(encrypted[i], 0, encrypted[i].length);
-            incoming.setMessage(xbb);
-            incoming.setAddress(sender);
-            dest.messageReceived(incoming);
-        }
-
-        dest.memberDisappeared(sender);
-        Assert.assertEquals(Long.valueOf(2), 
dest.getRemovedMemberHeadValue(sender));
-
-        ChannelData msg = new ChannelData(false);
-        msg.setMessage(new XByteBuffer("3".getBytes("UTF-8"), false));
-        src.sendMessage(null, msg, null);
-        encrypted[3] = ((ValueCaptureInterceptor) src.getNext()).getValue();
-
-        ChannelData incoming = new ChannelData(false);
-        XByteBuffer xbb = new XByteBuffer(encrypted[3].length, false);
-        xbb.append(encrypted[3], 0, encrypted[3].length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(sender);
-        dest.messageReceived(incoming);
-
-        Assert.assertNull(dest.getRemovedMemberHeadValue(sender));
-
-        incoming = new ChannelData(false);
-        xbb = new XByteBuffer(encrypted[2].length, false);
-        xbb.append(encrypted[2], 0, encrypted[2].length);
-        incoming.setMessage(xbb);
-        incoming.setAddress(sender);
-        dest.messageReceived(incoming);
-
-        Collection<byte[]> messages = ((ValuesCaptureInterceptor) 
dest.getPrevious()).getValues();
-        Assert.assertEquals(4, messages.size());
+        MatcherAssert.assertThat("Two identical cleartexts encrypt to the same 
ciphertext",
+                cipherText1, IsNot.not(IsEqual.equalTo(cipherText2)));
     }
 
     @Test
     public void testPickup() throws Exception {
         File file = new File(MESSAGE_FILE);
-        if (!file.exists()) {
+        if(!file.exists()) {
             System.err.println("File message.bin does not exist. Skipping 
test.");
             return;
         }
@@ -412,7 +242,8 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
     }
 
     /*
-     * This test isn't guaranteed to catch any multithreaded issues, but it 
gives a good exercise.
+     * This test isn't guaranteed to catch any multithreaded issues, but it
+     * gives a good exercise.
      */
     @Test
     public void testMultithreaded() throws Exception {
@@ -435,7 +266,7 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
                     xbb.append(bytes, 0, bytes.length);
                     msg.setMessage(xbb);
 
-                    for (int i = 0; i < messagesPerThread; ++i) {
+                    for(int i=0; i<messagesPerThread; ++i) {
                         src.sendMessage(null, msg, null);
                     }
                 } catch (ChannelException e) {
@@ -445,25 +276,26 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
         };
 
         Thread[] threads = new Thread[numThreads];
-        for (int i = 0; i < numThreads; ++i) {
+        for(int i=0; i<numThreads; ++i) {
             threads[i] = new Thread(job);
             threads[i].setName("Message-Thread-" + i);
         }
 
-        for (int i = 0; i < numThreads; ++i) {
+        for(int i=0; i<numThreads; ++i) {
             threads[i].start();
         }
 
-        for (int i = 0; i < numThreads; ++i) {
+        for(int i=0; i<numThreads; ++i) {
             threads[i].join();
         }
 
         // Check all received messages to make sure they are not corrupted
-        Collection<byte[]> messages = ((ValuesCaptureInterceptor) 
dest.getPrevious()).getValues();
+        Collection<byte[]> messages = 
((ValuesCaptureInterceptor)dest.getPrevious()).getValues();
 
-        Assert.assertEquals("Did not receive all expected messages", 
numThreads * messagesPerThread, messages.size());
+        Assert.assertEquals("Did not receive all expected messages",
+                numThreads * messagesPerThread, messages.size());
 
-        for (byte[] message : messages) {
+        for(byte[] message : messages) {
             Assert.assertArrayEquals("Message is corrupted", message, bytes);
         }
     }
@@ -484,10 +316,4 @@ public class TestEncryptInterceptor extends 
EncryptionInterceptorBaseTest {
             Assert.fail("EncryptionInterceptor should throw 
ChannelConfigException, not " + t.getClass().getName());
         }
     }
-
-    private MemberImpl createMember(String host, int port, int uniqueIdSeed) 
throws Exception {
-        MemberImpl member = new MemberImpl(host, port, 0);
-        member.setUniqueId(new byte[] { (byte) uniqueIdSeed, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0 });
-        return member;
-    }
 }
diff --git a/test/org/apache/catalina/tribes/util/TestCyclicTracker.java 
b/test/org/apache/catalina/tribes/util/TestCyclicTracker.java
deleted file mode 100644
index c7eeaab5d2..0000000000
--- a/test/org/apache/catalina/tribes/util/TestCyclicTracker.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.catalina.tribes.util;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestCyclicTracker {
-
-    @Test
-    public void testRejectsDuplicateWithinWindow() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertTrue(tracker.track(11));
-        Assert.assertFalse(tracker.track(10));
-        Assert.assertFalse(tracker.track(11));
-    }
-
-
-    @Test
-    public void testAcceptsSkippedValueWithinWindow() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertTrue(tracker.track(12));
-        Assert.assertTrue(tracker.track(11));
-        Assert.assertFalse(tracker.track(11));
-    }
-
-
-    @Test
-    public void testRejectsValuesThatAreTooOld() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertTrue(tracker.track(11));
-        Assert.assertTrue(tracker.track(12));
-        Assert.assertTrue(tracker.track(13));
-        Assert.assertFalse(tracker.track(9));
-    }
-
-
-    @Test
-    public void testAdvancePastWindowClearsOlderEntries() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertTrue(tracker.track(20));
-        Assert.assertTrue(tracker.track(17));
-        Assert.assertFalse(tracker.track(16));
-        Assert.assertFalse(tracker.track(10));
-    }
-
-
-    @Test
-    public void testAcceptsValuesFarAhead() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertTrue(tracker.track(Long.MIN_VALUE));
-        Assert.assertFalse(tracker.track(Long.MIN_VALUE));
-    }
-
-
-    @Test
-    public void testHandlesLongOverflow01() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(Long.MAX_VALUE - 1));
-        Assert.assertTrue(tracker.track(Long.MAX_VALUE));
-        Assert.assertTrue(tracker.track(Long.MIN_VALUE));
-        Assert.assertTrue(tracker.track(Long.MIN_VALUE + 1));
-
-        Assert.assertFalse(tracker.track(Long.MAX_VALUE));
-        Assert.assertFalse(tracker.track(Long.MAX_VALUE - 2));
-    }
-
-
-    @Test
-    public void testHandlesLongOverflow02() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(Long.MAX_VALUE - 1));
-        Assert.assertTrue(tracker.track(Long.MIN_VALUE + 2));
-        Assert.assertTrue(tracker.track(Long.MAX_VALUE));
-        Assert.assertFalse(tracker.track(Long.MAX_VALUE - 2));
-    }
-
-
-    @Test
-    public void testGetHeadValue() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        Assert.assertTrue(tracker.track(10));
-        Assert.assertEquals(10, tracker.getHeadValue());
-        Assert.assertTrue(tracker.track(12));
-        Assert.assertEquals(12, tracker.getHeadValue());
-    }
-
-
-    @Test(expected = IllegalStateException.class)
-    public void testGetHeadValueNotInitialized() {
-        CyclicTracker tracker = new CyclicTracker(4);
-
-        tracker.getHeadValue();
-    }
-
-
-    @SuppressWarnings("unused")
-    @Test(expected = IllegalArgumentException.class)
-    public void testRejectsZeroSize() {
-        new CyclicTracker(0);
-    }
-}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ccb85ae2c8..3d2a50a815 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -424,9 +424,6 @@
         Fix concurrency issues generating MD5 digests in the
         <code>CloudMembershipProvider</code> implementations. (markt)
       </fix>
-      <add>
-        Add replay protection to the <code>EncryptInterceptor</code>. (markt)
-      </add>
     </changelog>
   </subsection>
   <subsection name="WebSocket">
diff --git a/webapps/docs/config/cluster-interceptor.xml 
b/webapps/docs/config/cluster-interceptor.xml
index 42bc365697..df18599bfa 100644
--- a/webapps/docs/config/cluster-interceptor.xml
+++ b/webapps/docs/config/cluster-interceptor.xml
@@ -233,19 +233,11 @@
        <code>AES/GCM/NoPadding</code> is used.</p>
      </attribute>
      <attribute name="encryptionKey" required="true">
-       <p>The key to be used with the encryption algorithm.</p>
+       The key to be used with the encryption algorithm.
 
-       <p>The key should be specified as hex-encoded bytes of the appropriate
+       The key should be specified as hex-encoded bytes of the appropriate
        length for the algorithm (e.g. 16 bytes / 32 characters / 128 bits for
-       AES-128, 32 bytes / 64 characters / 256 bits for AES-256, etc.).</p>
-     </attribute>
-     <attribute name="replayWindowSize" required="false">
-       <p>The number of messages per sender remembered for replay 
detection.</p>
-
-       <p>Any message outside of this window will be rejected. Any message
-       inside this window will only be accpeted once and any duplicates
-       rejected. If not specified, the default of <code>1024</code> will be
-       used.</p>
+       AES-128, 32 bytes / 64 characters / 256 bits for AES-256, etc.).
      </attribute>
    </attributes>
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to