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]
