Add ML-DSA (FIPS 204) post-quantum signature support - #651
Conversation
Adds ML-DSA-44/65/87 XML digital signature support via the JSR-105 API (DOM) and the STAX signature path, wired through JCEMapper and the JSR-105 provider's algorithm URI registrations. Part of the post-quantum work tracked under SANTUARIO-634 (originally proposed in SANTUARIO-633 / apache#645), split out here as the signature-only half per community request. - The ML-DSA test keystore is generated on the fly per test run instead of a committed PKCS12 binary, avoiding the maintenance burden of binary test fixtures. Uses SelfSignedCertGenerator, originally authored by Joze Rihtarsic (unmerged PR apache#617), copied in and extended here with ML-DSA-44/65/87 AlgorithmIdentifier support per his suggestion on apache#645. - Adds negative-test coverage on both the DOM/JSR-105 and STAX paths: a tampered SignatureValue is rejected, and verification against the wrong public key fails. Added per Arpan0995's review feedback on apache#645.
| if (provider == null) { | ||
| String providerId = JCEMapper.getProviderId(); | ||
| if (providerId == null) { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID); |
| if (provider == null) { | ||
| String providerId = JCEMapper.getProviderId(); | ||
| if (providerId == null) { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID); |
| if (providerId == null) { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID); | ||
| } else { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID, providerId); |
| if (providerId == null) { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID); | ||
| } else { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID, providerId); |
| this.signatureAlgorithm = Signature.getInstance(algorithmID, providerId); | ||
| } | ||
| } else { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID, provider); |
| this.signatureAlgorithm = Signature.getInstance(algorithmID, providerId); | ||
| } | ||
| } else { | ||
| this.signatureAlgorithm = Signature.getInstance(algorithmID, provider); |
… sets Converts the tampered-SignatureValue and wrong-public-key rejection tests on both the DOM/JSR-105 and StAX paths from single hardcoded ML-DSA-65 cases to @ParameterizedTest/@CsvSource across ML-DSA-44/65/87, matching the style of the existing sign-and-verify tests. The StAX helper takes the signature and key algorithms as parameters instead of hardcoding ML-DSA-65.
Parameterize the negative signature tests across all ML-DSA parameter sets
|
While adding inbound-path coverage after the rebase, I found that StAX verification of an ML-DSA signature is not exercised by the current tests, and driving it revealed a round-trip issue with the KeyValue key identifier. Everything below was reproduced on this branch. The issue. With <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Id="..."><dsig:KeyValue/></dsig:KeyInfo>Feeding that document back through the library's own inbound side ( Root cause. The inbound side of this PR is otherwise sound. Repeating the same round trip with Why the tests do not catch it: Two possible fixes, in order of preference:
Happy to contribute the inbound-path tests either way (valid round trip, tampered signature, wrong key, all through |
Hi @Arpan0995 , Agree that "Emit DEREncodedKeyValue " is better and please go this way.
|
|
@ffang Done: the DEREncodedKeyValue fix is up as ffang#4 into this branch, with a parameterized test across ML-DSA-44/65/87. As noted there, it makes the KeyValue schema-valid and the embedded key recoverable; full StAX-inbound round-trip additionally needs inbound DEREncodedKeyValue extraction, which I am happy to follow up with separately. |
Adds ML-DSA-44/65/87 XML digital signature support via the JSR-105 API (DOM) and the STAX signature path, wired through JCEMapper and the JSR-105 provider's algorithm URI registrations. Part of the post-quantum work tracked under SANTUARIO-634 (originally proposed in SANTUARIO-633 / #645), split out here as the signature-only half per community request.