Hi Weijun, Thank you, that is exactly the clarity I was hoping for. The principle you stated also matches the data precisely: in every divergent row the input fails structural decoding (length changes, empty, corrupted hint region), and every
Hi Weijun,

Thank you, that is exactly the clarity I was hoping for. The principle
you stated also matches the data precisely: in every divergent row the
input fails structural decoding (length changes, empty, corrupted hint
region), and every decodable-but-wrong input returns false on all
three providers. The implementation is fully consistent with the rule.

Fair point on the checked exception. Since verify() declares
SignatureException, portable code has to handle it whichever provider
is underneath, and with that framing this is documented latitude
rather than a defect on either side.

One small suggestion: the two-line principle you stated seems worth
recording somewhere more discoverable than a list thread, for example
an implementation note in the provider documentation, since developers
moving between providers currently learn it by experiment. Happy to
contribute test vectors that pin the behaviour if that would help.

Thanks again for the quick response.

Best regards,
Arpan Sharma

On Fri, Jul 24, 2026 at 12:13 PM Wei-Jun Wang <[email protected]> wrote:
>
> Hi Arpan,
>
> Thanks for pointing out the difference. Our (built-in OpenJDK providers) principle is:
>
> - if the signature itself has an invalid structure, we throw an exception; otherwise,
> - if the signature cannot be verified by the public key, we return false.
>
> This has been true since DSA. I think it is also consistent with the spec [1]:
>
> * @throws SignatureException if this {@code Signature} object is not
> * initialized properly, ****the passed-in signature is improperly
> * encoded or of the wrong type****, if this signature algorithm is unable to
> * process the input data provided, etc.
>
> That said, I wouldn't say BC or ACCP is incorrect. It's even not a bad thing that we behave differently. Users should be prepared to deal with both outcomes in their applications. After all, SignatureException is declared there and you have to handle a checked exception anyway.
>
> Thanks,
> Weijun
>
> [1] https://github.com/openjdk/jdk/blob/0381286adedbd7daf1eb01a820ad8179dbb7da15/src/java.base/share/classes/java/security/Signature.java#L826
>
> > On Jul 24, 2026, at 11:47, Arpan Sharma <[email protected]> wrote:
> >
> > 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
> >
>

Reply via email to