[ 
https://issues.apache.org/jira/browse/CAMEL-25179?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

shashank updated CAMEL-25179:
-----------------------------
    Priority: Major  (was: Minor)

> camel-crypto - CryptoDataFormat with the default HMAC cannot unmarshal a 
> message larger than its buffer size (4096 bytes): 
> ArrayIndexOutOfBoundsException in HMACAccumulator.CircularBuffer
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25179
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25179
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core
>            Reporter: shashank
>            Priority: Major
>
> {{CryptoDataFormat}} appends an HMAC by default ({{shouldAppendHMAC=true}}). 
> On unmarshal, {{HMACAccumulator.decryptUpdate}} holds back the last 
> {{maclength}} bytes in a {{CircularBuffer}} of {{bufferSize + maclength}} 
> bytes. Both {{write}} and {{read}} of that buffer get the wrap-around at the 
> end of the array wrong:
> {code:java}
> public void write(byte[] data, int pos, int len) {
>     if (available >= len) {
>         if (write + len > buffer.length) {
>             int overlap = write + len % buffer.length;        // is write + 
> (len % length)
>             System.arraycopy(data, 0, buffer, write, len - overlap);   // 
> length is -write
>             System.arraycopy(data, len - overlap, buffer, 0, overlap);
>         } else { ... }
> ...
> public int read(byte[] dest, int position, int len) {
>     ...
>             if (read > write) {
>                 int x = buffer.length - read;
>                 System.arraycopy(buffer, read, dest, position, buffer.length 
> - read);  // copies length - read bytes, not len
>                 System.arraycopy(buffer, 0, dest, position + x, overlap);
>             } else {
>                 System.arraycopy(buffer, read, dest, position, len);          
>          // no wrap-around at all
>             }
> {code}
> Every {{write}} that has to wrap throws (the first copy gets a negative 
> length), and a {{read}} throws or copies the wrong bytes as soon as the read 
> position has passed the write position. {{CipherInputStream}} hands the 
> decrypted data out in chunks of about 512 bytes (GCM: in chunks of 
> {{bufferSize}}), so the buffer wraps as soon as a message is larger than 
> {{bufferSize}}: unmarshal of any message of 4096 bytes or more fails with the 
> default settings:
> {noformat}
> java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -4088 is negative
>     at 
> org.apache.camel.converter.crypto.HMACAccumulator$CircularBuffer.write(HMACAccumulator.java:138)
>     at 
> org.apache.camel.converter.crypto.HMACAccumulator.decryptUpdate(HMACAccumulator.java:71)
>     at 
> org.apache.camel.converter.crypto.CryptoDataFormat.unmarshal(CryptoDataFormat.java:197)
> {noformat}
> The data format documentation example ({{new CryptoDataFormat("DES", key)}}, 
> marshal then unmarshal) fails for a 5000 byte body. The existing tests only 
> use a 49 byte payload, and {{HMACAccumulatorTest}} never wraps the buffer. 
> The workarounds are {{shouldAppendHMAC=false}} (no integrity check) or a 
> {{bufferSize}} larger than every message.
> h3. Reproduction
> {code:java}
> CryptoDataFormat crypto = new CryptoDataFormat("DES", 
> KeyGenerator.getInstance("DES").generateKey());
> from("direct:basic").marshal(crypto).unmarshal(crypto).to("mock:unencrypted");
> template.sendBody("direct:basic", new byte[5000]);   // fails; 100 bytes works
> {code}
> Unit tests round-trip 100, 4096, 5000 and 100000 bytes through the data 
> format with the default options, and exercise the two {{CircularBuffer}} 
> cases and a 1000 byte input fed to {{HMACAccumulator}} in 24 byte chunks: on 
> main every case from 4096 bytes up fails, the 100 byte control passes 
> (checked again on main {{c3632125413b}}, 2026-09-30). The buffer code comes 
> from the original contribution (CAMEL-2482, 2010) and is the same in 3.0.0, 
> 4.0.0, 4.14.0, 4.18.0 and 4.22.0; later changes to the class (CAMEL-19194, 
> CAMEL-23766, CAMEL-24440) did not touch it.
> A small formal model (Lean 4) of the two methods shows that every {{write}} 
> which has to wrap throws, that a non-wrapping {{read}} with {{read > write}} 
> throws when the destination only has room for {{len}} bytes (the 
> {{decryptUpdate}} case), and that the fixed methods implement a FIFO queue; 
> together with a model of {{decryptUpdate}} this gives: for every way the 
> input is split into chunks of at most {{bufferSize}} bytes, the bytes passed 
> on are the input without its last {{maclength}} bytes, and the bytes kept 
> back for the check are exactly those last {{maclength}} bytes.
> h3. Proposed fix
> Split both copies at the end of the array:
> {code:java}
> public void write(byte[] data, int pos, int len) {
>     if (available >= len) {
>         int first = Math.min(len, buffer.length - write);
>         System.arraycopy(data, pos, buffer, write, first);
>         System.arraycopy(data, pos + first, buffer, 0, len - first);
>         write = (write + len) % buffer.length;
>         available -= len;
>     }
> }
> public int read(byte[] dest, int position, int len) {
>     if (dest.length - position >= len) {
>         if (buffer.length - available >= len) {
>             int first = Math.min(len, buffer.length - read);
>             System.arraycopy(buffer, read, dest, position, first);
>             System.arraycopy(buffer, 0, dest, position + first, len - first);
>             read = (read + len) % buffer.length;
>             available += len;
>             return len;
>         }
>     }
>     return 0;
> }
> {code}
> The format of the data is unchanged (marshal is not affected), so messages 
> written by earlier versions can be read. With the fix the new tests pass, and 
> so does the whole camel-crypto suite (84 tests).
> This is a failure, not a weakness in the MAC check: the MAC is still compared 
> over exactly the bytes that are passed on.
> Duplicate check (2026-09-30): JIRA "HMACAccumulator", "CircularBuffer", 
> "CryptoDataFormat" with "large" or "ArrayIndexOutOfBoundsException": only 
> CAMEL-19194, CAMEL-23766 and CAMEL-24440, none about the buffer. No open or 
> closed pull request touches the circular buffer.
> _Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to