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 aa1885c73d3fa27403db1ed2a77bce8bef40880d
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Oct 30 08:43:10 2026 +0100

    [FIX] Relaxed header canonicalization: only delete WSP around the value
    
    String#trim() removes every character up to U+0020, so control
    characters at either end of a signed header value were dropped before
    hashing: a NUL could be added to a signed From and the relaxed
    signature still verified. RFC 6376 section 3.4.2 only deletes WSP;
    since WSP runs are already reduced to one SP, one leading and one
    trailing SP are now removed.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../java/org/apache/james/jdkim/DKIMCommon.java    |  3 ++-
 .../test/java/org/apache/james/jdkim/DKIMTest.java | 26 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java 
b/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
index 830342a..72a1386 100644
--- a/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
+++ b/main/src/main/java/org/apache/james/jdkim/DKIMCommon.java
@@ -59,7 +59,8 @@ public abstract class DKIMCommon {
             String headerValue = fv.substring(fv.indexOf(':') + 1);
             headerValue = headerValue.replaceAll("\r\n[\t ]", " ");
             headerValue = headerValue.replaceAll("[\t ]+", " ");
-            headerValue = headerValue.trim();
+            // RFC 6376 section 3.4.2 only deletes WSP: String#trim would also 
drop control characters
+            headerValue = headerValue.replaceAll("^ | $", "");
             signature.update(octets(headerValue));
             if (DEEP_DEBUG)
                 System.out.println("#" + headerValue + "#");
diff --git a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java 
b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
index bbf1933..ffd45c5 100644
--- a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
@@ -263,4 +263,30 @@ public class DKIMTest {
         assertThat(message.getFields("missing")).isEmpty();
     }
 
+    @Test
+    public void 
controlCharacterAddedToARelaxedHeaderValueShouldBreakTheSignature() throws 
Exception {
+        DKIMSigner signer = new DKIMSigner("v=1; a=rsa-sha256; 
c=relaxed/simple; d=messiah.edu; h=from; s=selector2;", TestKeys.privateKey);
+        byte[] mail = "From: 
[email protected]\r\n\r\nbody\r\n".getBytes(StandardCharsets.US_ASCII);
+        String field = signer.sign(new ByteArrayInputStream(mail));
+
+        for (String tampered : Arrays.asList("From: [email protected]\u0001", 
"From: \[email protected]", "From:\u0016 [email protected]")) {
+            InputStream in = new SequenceInputStream(new 
ByteArrayInputStream((field + "\r\n").getBytes(StandardCharsets.US_ASCII)),
+                    new ByteArrayInputStream((tampered + 
"\r\n\r\nbody\r\n").getBytes(StandardCharsets.US_ASCII)));
+
+            assertThatThrownBy(() -> 
verifier.verify(in)).as(tampered).isInstanceOf(PermFailException.class);
+        }
+    }
+
+    @Test
+    public void 
relaxedHeaderCanonicalizationShouldIgnoreWhitespaceAroundTheValue() throws 
Exception {
+        DKIMSigner signer = new DKIMSigner("v=1; a=rsa-sha256; 
c=relaxed/simple; d=messiah.edu; h=from; s=selector2;", TestKeys.privateKey);
+        byte[] mail = "From: 
[email protected]\r\n\r\nbody\r\n".getBytes(StandardCharsets.US_ASCII);
+        byte[] reformatted = "From:\t [email protected] 
\t\r\n\r\nbody\r\n".getBytes(StandardCharsets.US_ASCII);
+        String field = signer.sign(new ByteArrayInputStream(mail));
+
+        InputStream in = new SequenceInputStream(
+                new ByteArrayInputStream((field + 
"\r\n").getBytes(StandardCharsets.US_ASCII)), new 
ByteArrayInputStream(reformatted));
+
+        assertThat(verifier.verify(in)).hasSize(1);
+    }
 }


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

Reply via email to