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;