Hi, Following up with a new data point. Since posting I added a third independent ML-DSA verifier to the same byte-identical differential: the Amazon Corretto Crypto Provider (ACCP 2. 5. 0, built on AWS-LC), alongside BouncyCastle 1. 85 and the
Hi,
Following up with a new data point.
Since posting I added a third independent ML-DSA verifier to the same
byte-identical differential: the Amazon Corretto Crypto Provider
(ACCP 2.5.0, built on AWS-LC), alongside BouncyCastle 1.85 and the
SUN provider, on JDK 26.0.1. ACCP agrees with BouncyCastle. It returns
false on every structurally malformed signature and throws nothing.
So across three implementations from independent lineages (SUN, BC's
pure Java code, AWS-LC's C), SUN is the outlier on all 21 divergent
rows (7 structural mutations x 3 parameter sets). The same-length,
decodable-but-wrong cases still agree (false on all three).
mutation | BC | SUN | ACCP
truncate-1 / truncate-half | false | SignatureException | false
extend-1 / extend-block | false | SignatureException | false
flip-last-bit / ones-all | false | SignatureException | false
empty | false | SignatureException | false
flip-first-bit / zero-all | false | false | false
(same length, decodable, wrong)
To rule out the obvious artifact, each verify is pinned to its
provider: the run prints the provider actually servicing the Signature
and KeyFactory plus the concrete key class, and the three are distinct
native types (BCMLDSAPublicKey, NamedX509Key, EvpMlDsaPublicKey), so
there is no silent fallback or cross-provider key reuse. Harness,
provenance check, verbatim output, and the pinned environment (exact
Maven Central artifacts with sha256s) are at:
https://github.com/Arpan0995/pqc-provider-differential/tree/main/threeway-accp
I raise it here because it bears on the question I asked. SUN's
throw-on-malformed path does not look like the common contract for
ML-DSA verify(), given that two of the three providers return false,
so a short note in the SUN provider documentation about which inputs
surface as SignatureException versus a false return would help anyone
writing portable code against verify(). I'm not arguing SUN is wrong
(the javadoc permits both), just that the divergence is worth pinning
down one way or the other. Still glad to share the harness or
contribute test vectors that lock in whatever behaviour is decided.
Best regards,
Arpan Sharma