You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Accept a pure ML-DSA key on the HashML-DSA signature path for its own parameter set, and bind the parameter-set specific SLH-DSA signature services to their parameter sets so the same rule applies there, relates to github #2397.
Copy file name to clipboardExpand all lines: docs/releasenotes.html
+2Lines changed: 2 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -119,6 +119,8 @@ <h3>2.1.2 Defects Fixed</h3>
119
119
<li>The AIMer, SMAUG-T and NTRU+ parameter set names were not registered as algorithm aliases in the BCPQC provider, so getInstance(spec.getName()) - the natural way to turn an AlgorithmParameterSpec into a service, and what the other sixteen PQC families accept - failed with NoSuchAlgorithmException for those three. The spellings differ from the registered names only in punctuation: AIMerParameterSpec names carry no hyphen ("aimer128f" against the registered "AIMer-128f"), SmaugTParameterSpec uses an underscore ("SMAUGT_MODE1" against "SMAUGT-MODE1") and NTRUPlusParameterSpec no hyphen before the size ("NTRU+KEM768" against "NTRU+KEM-768"), so the mismatch was easy to hit and gave no hint as to the spelling wanted. Each parameter set name is now an alias of its algorithm across every service the family registers - KeyFactory, KeyPairGenerator and Signature for AIMer, KeyFactory, KeyPairGenerator, KeyGenerator and Cipher for SMAUG-T and NTRU+. The registered names are unchanged and remain the canonical ones; JCA algorithm lookup is case insensitive, so both spellings resolve in any case.</li>
120
120
<li>Four PQC families disagreed with themselves about how a parameter set is spelled, because their KeyPairGenerator took its JCA algorithm name from the lightweight parameters object rather than from the name it was registered under. Falcon's generator reported getAlgorithm() as "falcon-1024" where it is registered as FALCON-1024 and its keys report FALCON-1024; SMAUG-T's reported "smaugt_mode1" where it is registered as SMAUGT-MODE1; and NTRU+ reported NTRU+KEM768 where it was registered as NTRU+KEM-768. Each family now uses one spelling throughout - generator, parameter spec, generated keys and the "key pair generator locked to ..." message - and each generator's getAlgorithm() returns the name it was obtained under: FALCON-512 / FALCON-1024, SMAUGT-MODE1 / MODE3 / MODE5 / MODET, and NTRU+KEM768 / NTRU+KEM864 / NTRU+KEM1152. The SMAUG-T parameter set names (SmaugTParameterSpec.getName(), and the algorithm the keys report) therefore move from the underscored SMAUGT_MODE1 form to the hyphenated SMAUGT-MODE1 form, and the NTRU+ services are now registered under the un-hyphenated NTRU+KEM768 form that its parameter sets already used. <b>Both previous spellings continue to resolve</b> - SMAUGT_MODE1 and NTRU+KEM-768 are registered as aliases across every service of their families - so existing getInstance calls keep working; only getAlgorithm() and the parameter set name change. SLH-DSA is corrected the same way in the entry above.</li>
121
121
<li>The JCA/JCE provider classes for the pre-standardisation SPHINCS+ and Kyber have been removed from the BCPQC provider hierarchy: org.bouncycastle.pqc.jcajce.provider.sphincsplus and .kyber, their SPHINCSPlus and Kyber Mappings classes, and the unused org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus mappings alongside them. Neither was listed in BouncyCastlePQCProvider's algorithm set or in BouncyCastleProvider's, so none of the services they described - "SPHINCS+-SHA2-128S", the BCPQC-side "ML-KEM-512", and the rest - had been obtainable through either provider; they were superseded by SLH-DSA and ML-KEM in the BC provider, which are registered and are what callers should use. The corresponding exports have been dropped from both module-info descriptors. The lightweight implementations are untouched, as is the unrelated org.bouncycastle.pqc.legacy.sphincsplus package.</li>
122
+
<li>The parameter-set specific HashML-DSA Signature services in the BC provider refused a pure ML-DSA key of their own parameter set, so a caller who obtained a key as "ML-DSA-65" and a signature as "ML-DSA-65-WITH-SHA512" was met with InvalidKeyException("signature configured for ML-DSA-65-WITH-SHA512"). FIPS 204 sec. 5 defines one key generation algorithm per parameter set and the keys it produces carry no commitment to the pure mode over the pre-hash one, so the pure key was a perfectly good HashML-DSA key; the SPIs were comparing the key's algorithm name against their own, which differ by the "-WITH-SHA512" suffix. It particularly bit certificate-sourced keys, which carry the pure OID (RFC 9881), and the provider was already inconsistent about it - the unparameterised "HASH-ML-DSA" and "HASH-ML-DSA-EXTERNAL-HASH" services accepted pure keys all along. The comparison is now against the parameter set rather than the name, so a pure key is accepted wherever its parameter set matches. Signatures are unchanged: HashMLDSASigner derives the same SHA-512 pre-hash for a pure and a pre-hash key of the same parameter set, so the bytes produced are identical either way. <b>The tolerance is one way only</b> - a key that names a HashML-DSA parameter set has been narrowed to the pre-hash mode and is still refused by the pure ML-DSA services - and a key of any other parameter set is still refused in both directions (github #2397).</li>
123
+
<li>The parameter-set specific SLH-DSA Signature services were not bound to their parameter set at all: every one of the twenty four algorithm names, and their OIDs, was registered as an alias of the unparameterised "SLH-DSA" or "HASH-SLH-DSA" service, so the name a caller asked for had no effect on which keys were accepted. Signature.getInstance("SLH-DSA-SHAKE-256S") would sign quite happily with an SLH-DSA-SHA2-128F key, producing a SHA2-128F signature, and likewise across every other pair. This matters to a caller using the algorithm name as a policy gate - a service that means to sign or verify only at a chosen parameter set - which had no way to tell that the name was being ignored. Each name and OID is now registered against an SPI bound to its own parameter set, applying the same rule described for ML-DSA above: a pure key is accepted by the pre-hash service of its own parameter set, a pre-hash key is refused by the pure service, and a key of any other parameter set is refused either way. <b>This is a behavioural change for a caller that relied on a parameter-set named SLH-DSA Signature accepting a key of a different parameter set</b>, which now raises InvalidKeyException; the unparameterised "SLH-DSA" and "HASH-SLH-DSA" services name no parameter set and are unchanged, so they remain the way to work with a key whose parameter set is not known in advance. Note also that the pre-hash OID table was in a different order from the algorithm names it is now index-matched against, which is corrected here - while everything aliased to a single service the ordering could not be observed.</li>
122
124
<li>An OCSP response carrying no nextUpdate could be cached and reused as though it stated a validity interval, so a response could go on answering for a certificate after the responder had newer information about it - after a revocation, in particular. Any such reuse was bounded only by garbage collection rather than by an interval: OcspCache holds each responder's response map through a WeakReference, itself in a WeakHashMap, and nothing else refers to that map once the call returns, so entries last until the next collection. That is unpredictable rather than long - short on a busy JVM, potentially much longer on a large heap that collects rarely - and it is not a window the responder or the caller had any say in. RFC 6960 sec. 4.2.2.1 says the opposite of what that assumes: "if nextUpdate is not set, the responder is indicating that newer revocation information is available all the time", which is a statement that there is no interval to reuse the response over, not that it never expires. OcspCache now separates the two questions it had been asking with one method: a response arriving from the responder is accepted as before, whether or not it states a nextUpdate, while only a response that states one may be served from the cache afterwards. Nothing is rejected that was previously accepted - a responder that omits nextUpdate simply costs another request per validation, which is what "available all the time" asks for. Additionally, both the cached and the caller-supplied (stapled) paths now apply RFC 6960 sec. 4.2.2.1's other freshness rule, "responses whose thisUpdate time is later than the local system time SHOULD be considered unreliable", which neither had checked: a response dated ahead of the time being validated for by more than a 15 minute clock-skew allowance is treated as unreliable, raising "OCSP response not yet valid" on the stapled path.</li>
0 commit comments