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

oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new f4473e19bf07 CAMEL-23766: camel-crypto - use constant-time comparison 
for HMAC verification in HMACAccumulator
f4473e19bf07 is described below

commit f4473e19bf07cc56f283733a7eae161234ae1364
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Jul 2 11:54:31 2026 +0200

    CAMEL-23766: camel-crypto - use constant-time comparison for HMAC 
verification in HMACAccumulator
    
    HMACAccumulator.validate() compared the expected and actual MAC 
byte-by-byte with an early-exit loop, leaking how many leading bytes matched 
through timing. Use java.security.MessageDigest.isEqual() for a constant-time 
comparison, the standard practice for MAC/signature verification. Added a test 
for the mismatch case.
    
    Closes #24375
---
 .../org/apache/camel/converter/crypto/HMACAccumulator.java | 14 +++++++-------
 .../apache/camel/converter/crypto/HMACAccumulatorTest.java | 13 +++++++++++++
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/HMACAccumulator.java
 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/HMACAccumulator.java
index c8423626a113..a1482c3bb0ed 100644
--- 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/HMACAccumulator.java
+++ 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/HMACAccumulator.java
@@ -19,6 +19,7 @@ package org.apache.camel.converter.crypto;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.Key;
+import java.security.MessageDigest;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
@@ -98,13 +99,12 @@ public class HMACAccumulator {
     public void validate() {
         byte[] actual = getCalculatedMac();
         byte[] expected = getAppendedMac();
-        for (int x = 0; x < actual.length; x++) {
-            if (expected[x] != actual[x]) {
-                throw new IllegalStateException(
-                        "Expected mac did not match actual mac\nexpected:"
-                                                + 
byteArrayToHexString(expected) + "\n     actual:"
-                                                + 
byteArrayToHexString(actual));
-            }
+        // Use a constant-time comparison to avoid leaking MAC-match progress 
through timing (side-channel).
+        if (!MessageDigest.isEqual(expected, actual)) {
+            throw new IllegalStateException(
+                    "Expected mac did not match actual mac\nexpected:"
+                                            + byteArrayToHexString(expected) + 
"\n     actual:"
+                                            + byteArrayToHexString(actual));
         }
     }
 
diff --git 
a/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/HMACAccumulatorTest.java
 
b/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/HMACAccumulatorTest.java
index c0a8dec3995a..8a13500236b2 100644
--- 
a/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/HMACAccumulatorTest.java
+++ 
b/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/HMACAccumulatorTest.java
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 public class HMACAccumulatorTest {
     private byte[] payload = {
@@ -70,6 +71,18 @@ public class HMACAccumulatorTest {
         validate(builder);
     }
 
+    @Test
+    void testValidateFailsWhenAppendedMacDoesNotMatch() throws Exception {
+        int buffersize = 256;
+        byte[] buffer = initializeBuffer(buffersize);
+        // Corrupt the first byte of the appended MAC so it no longer matches 
the calculated MAC
+        buffer[payload.length] ^= 0x01;
+
+        HMACAccumulator builder = new HMACAccumulator(key, "HmacSHA1", null, 
buffersize);
+        builder.decryptUpdate(buffer, 40);
+        assertThrows(IllegalStateException.class, builder::validate);
+    }
+
     @Test
     void testDecryptionWhereMacOverlaps() throws Exception {
         int buffersize = 32;

Reply via email to