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 23e4227e9a3a6cd06a8c68c512f76efb3c684fa5
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Nov 4 15:00:37 2026 +0100

    [FIX] DKIMSigner: do not fold next to the FWS a template's h= holds
    
    When the h= piece is too long for one line, foldedRepresentation splits
    the value on ":" and folds before the name that would pass column 78.
    A template may already fold the list (h=from:\r\n\tsubject:...): the
    names then start with "\r\n\t", their CRLF and tab were counted on the
    current line, and the "\r\n " added before such a name left a line
    holding a single SP, obsolete FWS the signer must not generate.
    
    The names are now stripped of their FWS before the list is re-folded,
    and the column restarts after a CRLF held by a tag written in one
    piece.
---
 .../org/apache/james/jdkim/tagvalue/TagValue.java  | 18 ++++++++++++++---
 .../test/java/org/apache/james/jdkim/DKIMTest.java | 23 ++++++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
index a16d4de..4e47f5e 100644
--- a/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
+++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/TagValue.java
@@ -426,15 +426,16 @@ public class TagValue {
             breakBeforeNextTag = false;
             if (column + piece.length() <= FOLDING_WIDTH || !"h".equals(tag)) {
                 res.append(piece);
-                column += piece.length();
+                column = columnAfter(column, piece);
                 continue;
             }
-            // SPEC: sig-h-tag = hdr-name *( [FWS] ":" [FWS] hdr-name ): fold 
after a ":"
+            // SPEC: sig-h-tag = hdr-name *( [FWS] ":" [FWS] hdr-name ): fold 
after a ":". The FWS the
+            // value may already hold is dropped, so that a fold never lands 
next to an existing one.
             res.append(" h=");
             column += 3;
             String[] names = value.split(":", -1);
             for (int i = 0; i < names.length; i++) {
-                String name = names[i] + (i < names.length - 1 ? ":" : ";");
+                String name = fwsPattern.matcher(names[i]).replaceAll("") + (i 
< names.length - 1 ? ":" : ";");
                 if (column + name.length() > FOLDING_WIDTH) {
                     res.append("\r\n ");
                     column = 1;
@@ -446,4 +447,15 @@ public class TagValue {
         return res.toString();
     }
 
+    /**
+     * @return the column after appending text at column: a CRLF the text 
holds starts a new line
+     */
+    private static int columnAfter(int column, String text) {
+        int crlf = text.lastIndexOf("\r\n");
+        if (crlf == -1) {
+            return column + text.length();
+        }
+        return text.length() - crlf - 2;
+    }
+
 }
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 725c698..12b9d36 100644
--- a/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/DKIMTest.java
@@ -763,4 +763,27 @@ public class DKIMTest {
         }
     }
 
+    @Test
+    public void 
signedFieldShouldNotHoldWhitespaceOnlyLinesWhenTemplateFoldsH() throws 
Exception {
+        String message = "From: [email protected]\r\nSubject: 
hi\r\n\r\nbody\r\n";
+        for (int repeats = 1; repeats < 20; repeats++) {
+            for (String c : new String[] {"simple/simple", "relaxed/relaxed"}) 
{
+                String template = "v=1; a=rsa-sha256; c=" + c + "; 
d=messiah.edu; s=selector2; h=from"
+                        + ":\r\n\tsubject".repeat(repeats) + ";";
+                String field = new DKIMSigner(template, TestKeys.privateKey)
+                        .sign(new 
ByteArrayInputStream(message.getBytes(StandardCharsets.US_ASCII)));
+
+                for (String line : field.split("\r\n", -1)) {
+                    assertThat(line.trim()).as("line in [%s]", 
field).isNotEmpty();
+                    assertThat(line.length()).as("line [%s]", 
line).isLessThanOrEqualTo(78);
+                }
+                verifier.resetResults();
+                String signed = field + "\r\n" + message;
+                assertThat(verifier.verify(new 
ByteArrayInputStream(signed.getBytes(StandardCharsets.US_ASCII))))
+                        .as("c=%s, %d names", c, repeats)
+                        .hasSize(1);
+            }
+        }
+    }
+
 }


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

Reply via email to