This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-jdkim.git
commit e170c392948a28e18e29156fbab51b93555fcba2 Author: Benoit TELLIER <[email protected]> AuthorDate: Sun Nov 1 19:59:57 2026 +0000 [FIX] Body canonicalizers: ignore the high-order bits of write(int) RelaxedBodyCanonicalizer and SimpleBodyCanonicalizer compared the whole int with CR, LF, SP and HTAB, whereas OutputStream#write(int) says its 24 high-order bits are ignored: writing b | 0x100 through the streaming API (newBodyHasher, getOutputStream, verify(BodyHasher)) passed 0x120 on as data and kept trailing CRLFs, so the body hash differed from the one of verify(InputStream). write(int) now masks its argument with 0xFF first, in both canonicalizers and, for consistency, in LimitedOutputStream, CompoundOutputStream and DigestOutputStream. --- .../org/apache/james/jdkim/canon/CompoundOutputStream.java | 2 ++ .../java/org/apache/james/jdkim/canon/DigestOutputStream.java | 2 ++ .../java/org/apache/james/jdkim/canon/LimitedOutputStream.java | 2 ++ .../org/apache/james/jdkim/canon/RelaxedBodyCanonicalizer.java | 2 ++ .../org/apache/james/jdkim/canon/SimpleBodyCanonicalizer.java | 2 ++ .../apache/james/jdkim/canon/RelaxedBodyCanonicalizerTest.java | 10 ++++++++++ .../apache/james/jdkim/canon/SimpleBodyCanonicalizerTest.java | 10 ++++++++++ 7 files changed, 30 insertions(+) diff --git a/main/src/main/java/org/apache/james/jdkim/canon/CompoundOutputStream.java b/main/src/main/java/org/apache/james/jdkim/canon/CompoundOutputStream.java index 5c02384..8ae653a 100644 --- a/main/src/main/java/org/apache/james/jdkim/canon/CompoundOutputStream.java +++ b/main/src/main/java/org/apache/james/jdkim/canon/CompoundOutputStream.java @@ -48,6 +48,8 @@ public class CompoundOutputStream extends OutputStream { } public void write(int b) throws IOException { + // OutputStream#write(int): the 24 high-order bits are ignored + b &= 0xFF; for (OutputStream outputStream : outputStreams) outputStream.write(b); } diff --git a/main/src/main/java/org/apache/james/jdkim/canon/DigestOutputStream.java b/main/src/main/java/org/apache/james/jdkim/canon/DigestOutputStream.java index 3aec990..2c783b4 100644 --- a/main/src/main/java/org/apache/james/jdkim/canon/DigestOutputStream.java +++ b/main/src/main/java/org/apache/james/jdkim/canon/DigestOutputStream.java @@ -42,6 +42,8 @@ public class DigestOutputStream extends FilterOutputStream { } public void write(int arg0) throws IOException { + // OutputStream#write(int): the 24 high-order bits are ignored + arg0 &= 0xFF; md.update((byte) arg0); if (out != null) out.write(arg0); diff --git a/main/src/main/java/org/apache/james/jdkim/canon/LimitedOutputStream.java b/main/src/main/java/org/apache/james/jdkim/canon/LimitedOutputStream.java index 9e0a581..d6c8d44 100644 --- a/main/src/main/java/org/apache/james/jdkim/canon/LimitedOutputStream.java +++ b/main/src/main/java/org/apache/james/jdkim/canon/LimitedOutputStream.java @@ -55,6 +55,8 @@ public class LimitedOutputStream extends FilterOutputStream { } public void write(int b) throws IOException { + // OutputStream#write(int): the 24 high-order bits are ignored + b &= 0xFF; if (computedBytes < limit) { out.write(b); computedBytes++; diff --git a/main/src/main/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizer.java b/main/src/main/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizer.java index 86d8f95..2031d6b 100644 --- a/main/src/main/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizer.java +++ b/main/src/main/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizer.java @@ -63,6 +63,8 @@ public class RelaxedBodyCanonicalizer extends FilterOutputStream { } public void write(int b) throws IOException { + // OutputStream#write(int): the 24 high-order bits are ignored + b &= 0xFF; if (pendingCR) { pendingCR = false; if (b == '\n') { diff --git a/main/src/main/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizer.java b/main/src/main/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizer.java index 6e5f8d6..d2271e8 100644 --- a/main/src/main/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizer.java +++ b/main/src/main/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizer.java @@ -76,6 +76,8 @@ public class SimpleBodyCanonicalizer extends FilterOutputStream { } public void write(int b) throws IOException { + // OutputStream#write(int): the 24 high-order bits are ignored + b &= 0xFF; if (DEEP_DEBUG) System.out.println("B:(" + lastWasCR + "|" + countCRLF + ") [" + (char) b + "]"); if (lastWasCR) { diff --git a/main/src/test/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizerTest.java b/main/src/test/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizerTest.java index 4fb7658..41346f1 100644 --- a/main/src/test/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizerTest.java +++ b/main/src/test/java/org/apache/james/jdkim/canon/RelaxedBodyCanonicalizerTest.java @@ -91,4 +91,14 @@ public class RelaxedBodyCanonicalizerTest extends AbstractOutputStreamTestCase { os.close(); extensiveChunker(body, bytes.toByteArray()); } + + public void testWriteIntIgnoresHighBits() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + RelaxedBodyCanonicalizer os = new RelaxedBodyCanonicalizer(bos); + for (byte b : "a b\r\n".getBytes()) { + os.write(b | 0x100); // OutputStream#write(int): the 24 high-order bits are ignored + } + os.close(); + assertArrayEquals("a b\r\n".getBytes(), bos.toByteArray()); + } } diff --git a/main/src/test/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizerTest.java b/main/src/test/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizerTest.java index 23bfed1..48f2ce3 100644 --- a/main/src/test/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizerTest.java +++ b/main/src/test/java/org/apache/james/jdkim/canon/SimpleBodyCanonicalizerTest.java @@ -155,4 +155,14 @@ public class SimpleBodyCanonicalizerTest extends AbstractOutputStreamTestCase { extensiveChunker("\r\n\r\r".getBytes(), "\r\n\r\r\r\n".getBytes()); extensiveChunker("a\r\n\r\nb\r\r\n\r\n".getBytes(), "a\r\n\r\nb\r\r\n".getBytes()); } + + public void testWriteIntIgnoresHighBits() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + SimpleBodyCanonicalizer os = new SimpleBodyCanonicalizer(bos); + for (byte b : "x\r\n\r\n\r\n".getBytes()) { + os.write(b | 0x100); // OutputStream#write(int): the 24 high-order bits are ignored + } + os.close(); + assertArrayEquals("x\r\n".getBytes(), bos.toByteArray()); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
