Hi, While differential-testing the ML-DSA implementations in the SUN provider (JEP 497) and BouncyCastle, I found the two providers disagree on how a structurally malformed ML-DSA signature is rejected. On byte-identical inputs, SUN throws SignatureException
Hi,
While differential-testing the ML-DSA implementations in the SUN
provider (JEP 497) and BouncyCastle, I found the two providers
disagree on how a structurally malformed ML-DSA signature is rejected.
On byte-identical inputs, SUN throws SignatureException ("Incorrect
signature length" for truncated/extended/empty inputs; "Invalid hints
encoding" for same-length inputs whose hint region fails FIPS 204
decoding), while BouncyCastle's Signature.verify() returns false. This
reproduces identically on JDK 24.0.2, 25.0.2, and 26.0.1, against BC
1.85 and the 1.86 beta — 21 divergent rows (7 structural mutations x 3
parameter sets). Well-formed-but-wrong signatures agree (false on
both).
Both behaviours are permitted by the Signature.verify javadoc, so I'm
not reporting a spec violation — but since verify() consumes
attacker-controlled input, the divergence is a portability and
robustness concern: code written against one provider's rejection path
misbehaves on the other (unhandled-exception surface in one direction,
silently unexercised exception handling in the other).
I've filed the same data with the BouncyCastle project
(https://github.com/bcgit/bc-java/issues/2367) suggesting they either
document or align their behaviour. On the JDK side, would a clarifying
note in the provider documentation (or an alignment decision either
way) be worth considering? Happy to share the harness — methodology
and raw results are at
https://github.com/Arpan0995/pqc-provider-differential — and to help
with test vectors that pin the agreed behaviour.
Best regards,
Arpan Sharma