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

    [FIX] SignatureRecord: reject a t= tag that is not 1*12DIGIT
    
    validate() did not look at t=, so an empty or signed timestamp passed
    and getSignatureTimestamp() later threw NumberFormatException, which
    the verifier reported as an unexpected exception without the record.
    The template still accepts an empty t=, meaning "fill in the signing
    time".
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java  |  4 ++++
 .../apache/james/jdkim/tagvalue/SignatureRecordTemplate.java  |  5 +++++
 .../java/org/apache/james/jdkim/SignatureRecordImplTest.java  | 11 +++++++++++
 .../org/apache/james/jdkim/SignatureRecordTemplateTest.java   | 11 +++++++++++
 4 files changed, 31 insertions(+)

diff --git 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
index bd10540..cfaa3a8 100644
--- 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
+++ 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java
@@ -81,6 +81,10 @@ public class SignatureRecordImpl extends TagValue implements 
SignatureRecord {
                 ("." + getValue("d")).toLowerCase()))
             throw new IllegalStateException("Identity (i=) domain mismatch: 
expected [optional]@[optional.]domain-from-d-attribute");
 
+        // RFC 6376 section 3.5: sig-t-tag = %x74 [FWS] "=" [FWS] 1*12DIGIT
+        if (getValue("t") != null && 
!timestampPattern.matcher(getValue("t")).matches())
+            throw new IllegalStateException("Invalid signature timestamp (t=): 
" + getValue("t"));
+
         // when "x=" exists and signature expired then return PERMFAIL
         // (signature expired)
         if (getValue("x") != null) {
diff --git 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
index b18fd29..34bb92e 100644
--- 
a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
+++ 
b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java
@@ -89,6 +89,11 @@ public class SignatureRecordTemplate extends TagValue 
implements SignatureRecord
                 ("." + getValue("d")).toLowerCase()))
             throw new IllegalStateException("Identity (i=) domain mismatch: 
expected [optional]@[optional.]domain-from-d-attribute");
 
+        // An empty t= asks the signer to fill in the signing time.
+        if (getValue("t") != null && getValue("t").length() > 0
+                && !timestampPattern.matcher(getValue("t")).matches())
+            throw new IllegalStateException("Invalid signature timestamp (t=): 
" + getValue("t"));
+
         // when "x=" exists and signature expired then return PERMFAIL
         // (signature expired)
         if (getValue("x") != null) {
diff --git 
a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java 
b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
index c6f0e9f..d41515f 100644
--- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java
@@ -178,4 +178,15 @@ public class SignatureRecordImplTest {
         assertThatThrownBy(() -> new SignatureRecordImpl(base + 
"x=9999999999999"))
                 .isInstanceOf(IllegalStateException.class);
     }
+
+    @Test
+    public void nonNumericTimestampShouldBeASyntaxError() {
+        String base = "v=1; a=rsa-sha256; d=example.com; s=s; h=from; bh=AA==; 
b=AA==; ";
+
+        assertThatThrownBy(() -> new SignatureRecordImpl(base + "t="))
+                .isInstanceOf(IllegalStateException.class);
+        assertThatThrownBy(() -> new SignatureRecordImpl(base + "t=-"))
+                .isInstanceOf(IllegalStateException.class);
+        assertThat(new SignatureRecordImpl(base + 
"t=1234567890").getSignatureTimestamp()).isEqualTo(1234567890L);
+    }
 }
diff --git 
a/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java 
b/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java
index 266154e..5adf7a7 100644
--- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java
+++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java
@@ -165,4 +165,15 @@ public class SignatureRecordTemplateTest {
         assertThatThrownBy(() -> new SignatureRecordTemplate("v=1; 
a=rsa-sha256; d=example.com; s=s; h=from; x=abc"))
                 .isInstanceOf(IllegalStateException.class);
     }
+
+    @Test
+    public void nonNumericTimestampShouldBeASyntaxError() {
+        assertThatThrownBy(() -> new SignatureRecordTemplate("v=1; 
a=rsa-sha256; d=example.com; s=s; h=from; t=-"))
+                .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    public void emptyTimestampShouldBeAccepted() {
+        new SignatureRecordTemplate("v=1; a=rsa-sha256; d=example.com; s=s; 
h=from; t=");
+    }
 }


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

Reply via email to