dependabot[bot] opened a new pull request, #20186:
URL: https://github.com/apache/druid/pull/20186

   Bumps [org.bouncycastle:bcpkix-jdk18on](https://github.com/bcgit/bc-java) 
from 1.84 to 1.85.
   <details>
   <summary>Changelog</summary>
   <p><em>Sourced from <a 
href="https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.md";>org.bouncycastle:bcpkix-jdk18on's
 changelog</a>.</em></p>
   <blockquote>
   <h1>Bouncy Castle Crypto Package - Release Notes</h1>
   <h2>1.0 Introduction</h2>
   <p>The Bouncy Castle Crypto package is a Java implementation of 
cryptographic algorithms. The package is organised so that it contains a 
light-weight API suitable for use in any environment (including the J2ME) with 
the additional infrastructure to conform the algorithms to the JCE 
framework.</p>
   <h2>2.0 Release History</h2>
   <p><!-- raw HTML omitted --><!-- raw HTML omitted --></p>
   <h3>2.1.1 Version</h3>
   <p>Release: 1.86<br />
   Date: 2026, TBD</p>
   <h3>2.1.2 Defects Fixed</h3>
   <ul>
   <li>The S/MIME example smoke test in the misc module 
(org.bouncycastle.mail.smime.examples.test.AllTests) drove 
SendSignedAndEncryptedMail against smtp.gmail.com, and that example finishes 
with Transport.send() under JavaMail's default settings, which have no connect 
timeout. Where outbound port 25 is refused the failure was swallowed and the 
test passed; where it is silently dropped, as on many home networks, the 
connect blocked and ./gradlew build hung in :misc:test indefinitely with 
&quot;0 tests completed&quot;. The test now delivers to an SMTP stub on a 
loopback port, with connect / read / write timeouts as a backstop, and asserts 
the message arrived (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2407";>#2407</a>).</li>
   <li>Composite ML-KEM encapsulation took the traditional component public key 
bytes it feeds the KEM combiner from the recipient key's own encoding, while 
decapsulation recomputes the point from the private key and so always produced 
an uncompressed one. Section 4 of draft-ietf-lamps-pq-composite-kem requires an 
EC component to be carried as an uncompressed point, but a component key that 
encodes itself compressed - a BC EC key whose point format has been set through 
org.bouncycastle.jce.interfaces.ECPointEncoder, or a key from a provider that 
preserves a compressed encoding - was passed through as it came. Both sides 
then combined a different tradPK and derived different shared secrets, with no 
error reported on either: encapsulation and decapsulation both succeeded and 
the recipient simply could not decrypt. The EC component is now normalised to 
an uncompressed point wherever the engine serialises one, which covers the 
ephemeral key that forms the ciphertext as well. X25519 and X
 448 components have a single encoding and were unaffected, as were EC keys 
left in their default (uncompressed) format, whose shared secrets are 
unchanged. CompositePublicKey.getEncoded() took its component bytes the same 
way, so such a key also encoded to a composite key other implementations reject 
and whose bytes changed across an encode / decode / encode round trip - 1238 
bytes rather than 1270 for MLKEM768-ECDH-P256, and for the composite ML-DSA 
keys sharing that method, 2006 rather than 2038 for MLDSA65-ECDSA-P256. It now 
normalises the component the same way. This is a write-side change only: a 
composite key carrying a compressed EC component is still decoded, since the 
component key factories accept either form, and continues to verify signatures 
as before - it simply re-encodes in the normalised form. The shared 
normalisation is 
org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil.getUncompressedSubjectPublicKeyBytes.</li>
   <li>Composite ML-KEM encapsulation threw a NullPointerException, wrapped in 
an IllegalStateException out of KeyGenerator.generateKey(), when the 
SecureRandom it was given was null - which javax.crypto.KEM.newEncapsulator() 
documents as a request for the provider's default, and which 
KeyGenerator.init(spec, null) passes straight through. The three RSA-OAEP 
composites draw the traditional shared secret from that random directly, so 
they were the ones affected; the ECDH and X25519 / X448 composites escaped only 
because their component KeyPairGenerators default a random of their own. 
CompositeMLKEMEngine now defaults one through 
CryptoServicesRegistrar.getSecureRandom() on first use, as the composite KEM 
Cipher's wrap path already did, and as the KEM generators corrected earlier in 
this cycle now do. Related, the engine now also clears the ML-KEM component's 
shared secret alongside the traditional one on both the encapsulate and 
decapsulate paths - the copy handed back by getEncoded()
  was left in the heap - as section 3.5 of draft-ietf-lamps-pq-composite-kem 
requires.</li>
   <li>CompositePublicKey.getAlgorithm() and CompositePrivateKey.getAlgorithm() 
returned null for all twelve Composite ML-KEM 
(draft-ietf-lamps-pq-composite-kem) parameter sets. Both classes resolved the 
name through the composite signature index only, which holds the composite 
ML-DSA OIDs, so a composite KEM key pair - generated, parsed from a 
certificate, or read from PKCS#8 - reported no algorithm at all, and the 
standard JCA idiom of reconstructing a key with 
KeyFactory.getInstance(key.getAlgorithm()) raised a NullPointerException. The 
lookup now falls back to the composite KEM index, so the name returned is the 
one the provider registers the algorithm under (e.g. MLKEM768-X25519-SHA3-256), 
matching the composite ML-DSA behaviour. The same single-index assumption made 
the CompositePublicKey(SubjectPublicKeyInfo) and 
CompositePrivateKey(PrivateKeyInfo) constructors reject a composite KEM key 
with &quot;unable to create CompositePublicKey from SubjectPublicKeyInfo&quot;; 
they now d
 ispatch to the composite KEM key factory for those OIDs. Keys obtained through 
KeyFactory or through BouncyCastleProvider.getPublicKey / getPrivateKey were 
unaffected and are unchanged (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2404";>#2404</a>).</li>
   <li>The org.bouncycastle.jcajce.spec.KEMKDFSpec constructor stored a null 
otherInfo as given, so getOtherInfo() returned null, and three of the KDF 
branches KdfUtil.makeKeyBytes dispatches to - KMAC-128, KMAC-256 and SHAKE-256 
- read the otherInfo length without a guard and threw NullPointerException out 
of the KEM operation, where the KDF2, KDF3 and HKDF branches tolerate a null 
through KDFParameters / HKDFParameters. The Builder of every spec in the 
package already mapped null to empty, so no provider path reached it, but the 
constructor is protected on a public class and KdfUtil is documented for 
callers building their own KEM integration; the deprecated KEMParameterSpec 
passes a null itself and escaped only because it also pins the KDF to null. The 
constructor now stores empty for a null, so getOtherInfo() never returns null, 
and a null and an explicitly empty otherInfo derive the same key.</li>
   <li>QR-UOV signature verification accepted a signature encoding that was not 
canonical, so the encoding of a signature was not unique even after the 
trailing-byte fix of github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a>. Each 
F_q element of the signature is stored in ceil(log2 q) bits, one more bit 
pattern than the field has elements: q itself is representable and is 
arithmetically congruent to zero, so an element written as q verified exactly 
as the same element written as zero would, and the bits padding the last 
element out to the byte boundary were never read at all. Every zero element of 
a signature therefore carried a second encoding, and for the q = 7 parameter 
sets roughly one element in seven is zero - a single qruov_5_q7_L10 signature 
measured 256 spare bits, so on the order of 2^256 distinct byte strings 
verified for the one message and key. Verification now rejects any element 
outside [0, q) and any set padding bit; a signature produced b
 y this or by the reference implementation is unaffected, as the KAT vectors of 
every parameter set confirm. (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a>)</li>
   <li>SNOVA signature verification ignored four bits inside the signature for 
any parameter set whose solution is an odd number of GF(16) nibbles - the 
SNOVA_24_5_5, SNOVA_25_8_3, SNOVA_29_6_5 and SNOVA_66_15_3 families, sixteen of 
the forty-four parameter sets. The last byte of the encoded solution carries a 
single nibble and the signer leaves the top four bits zero, but the decoder did 
not read them, so sixteen distinct byte strings verified for one signature. 
This is the same non-unique encoding github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a> closed 
for bytes following the signature, applied inside it; the verifier now requires 
those bits to be zero. (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a>)</li>
   <li>SnovaPrivateKeyParameters did not validate the length of the private key 
encoding handed to it - the only one of the five schemes of github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a> that did 
not - and SnovaParameters.getPrivateKeyLength() reported the expanded 
(&quot;ESK&quot;) length even for a parameter set whose private key is the seed 
pair. A private key encoding reaches this constructor straight from a PKCS#8 
blob, so a wrong length went undetected: a seed-form key with extra bytes 
appended was accepted and signed under a different derived key, and a short 
expanded-form key sized the signer's decode buffer negatively, throwing 
NegativeArraySizeException out of generateSignature() rather than being 
reported at construction. Related, the signing retry loop could not terminate: 
the vinegar values are derived from a single-byte counter, so only 256 distinct 
linear systems can be tried, and an expanded-form private key that is not a 
real central
  map is singular for all of them - generateSignature() then span forever 
rather than failing. The length is now checked at construction, 
getPrivateKeyLength() reports the length that parameter set's private key 
actually has, and the retry loop gives up after its 256 attempts as MAYO's 
does.</li>
   <li>MayoSigner and MayoKeyPairGenerator did not clear several buffers 
holding secret key material that the MAYO reference implementation explicitly 
clears. Signing left the secret oil space O, the expanded L = (P1 + P1^t) * O + 
P2, and the M / VPV / Ox intermediates of the central map in place, having gone 
to the trouble of clearing eleven other buffers; key generation left the 
expanded seed, whose tail is the encoded oil space, and the P1 * O + P2 half of 
P; and the row-echelon step left the packed echelon form of the secret linear 
system and its pivot rows. Separately, if all 256 attempts at solving for the 
signature had given a rank-deficient system, signing emitted a signature built 
from the failed attempt's state instead of reporting the failure the reference 
returns, and AIMerSigner.generateSignature returned an empty array on failure, 
which a caller would hand on as though it were a signature. Both now throw.</li>
   <li>Five PQC signature schemes - MAYO, SNOVA, QR-UOV, SQIsign and AIMer - 
returned the NIST crypto_sign &quot;sm&quot; signed-message envelope from 
generateSignature() rather than the signature. That envelope is an artefact of 
the reference KAT harness, which records the message alongside the signature so 
a vector file can be self-contained; it is not part of any of the five 
specifications, and no other BC signer emits it (Falcon's KAT test rebuilds the 
equivalent envelope in the test, which is where it belongs). Two consequences 
followed, both reaching the JCA Signature services of every parameter set of 
the five schemes in BouncyCastlePQCProvider. First, since the message was 
appended to the signature, verification had to skip whatever followed the 
signature proper, and it did so by checking only that the buffer was long 
enough - so any number of trailing bytes could be added to a valid signature, 
or the appended message replaced with unrelated data, and it still verified. A 
sig
 nature encoding was therefore not unique: anyone holding one valid signature 
could produce unlimited distinct byte strings that all verified for the same 
message and key, which breaks any use that treats the signature bytes as an 
identifier, deduplicates on them, or records them as evidence. Second, the 
envelope propagated into everything built on the operator layer: because 
ContentSigner hands the signature straight into the structure being signed, 
every X.509 certificate, CRL, CMS SignedData and TLS CertificateVerify BC 
produced with one of these algorithms carried a verbatim copy of the signed 
data inside its own signature field - a self-signed MAYO-1 certificate came to 
3567 bytes where the same certificate is now 2020 - which no other 
implementation can parse as a signature, and which in a detached CMS signature 
meant the &quot;detached&quot; signature carried the content. 
generateSignature() now returns the bare signature, and verifySignature() 
requires exactly the parameter s
 et's signature length, so appended or truncated data is rejected rather than 
ignored. <strong>This is a behavioural change for signatures produced by an 
earlier release</strong> - MAYO and SNOVA from 1.84, QR-UOV, SQIsign and AIMer 
from 1.85 - which are no longer accepted in the envelope form they were emitted 
in; the signature bytes themselves are unchanged, so a stored value can be 
recovered by taking the leading signature-length bytes, or for AIMer, whose 
envelope was message || signature rather than signature || message, the 
trailing ones. The KAT tests now rebuild the envelope before comparing against 
the vector files, which continue to record it. Note that AIMer's verification 
had already been made length-exact during this cycle (see the entry below 
relating to github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2401";>#2401</a>), so of 
the five only its envelope remained (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2403";>#2403</a>).</li>
   <li>The MLS implementation did not bind an X.509 credential to the 
LeafNode's signature_key. LeafNode.verify() checked a leaf's signature against 
the signature_key declared in the leaf itself, while the X.509 credential's 
certificate chain was stored but never parsed or checked, so the certificate's 
public key was never required to match signature_key (RFC 9420 sec. 5.3). A 
leaf could therefore carry one party's certificate while being signed by an 
unrelated key and still be accepted under that party's identity through 
KeyPackage.verify() and the Group leaf-validation path. LeafNode.verify() now 
requires the end-entity certificate's subject public key, in the cipher suite's 
signature encoding, to equal signature_key for an X.509 credential, and rejects 
the leaf otherwise - including an empty chain or a certificate whose key type 
does not match the cipher suite; certificate-chain and identity validation to a 
trust anchor remain the application's responsibility per RFC 9420 sec. 5.3
 .1. A public org.bouncycastle.mls.codec.Certificate(byte[]) constructor and a 
Credential.getCertificates() accessor are added so callers can build and 
inspect X.509 credentials. Basic credentials are unaffected.</li>
   <li>The SecureRandom supplied to 
org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder.setSecureRandom() did 
not drive the content IV / nonce for any algorithm other than RC2. 
EnvelopedDataHelper.generateParameters passed the caller's SecureRandom to the 
AlgorithmParameterGenerator only in the RC2_CBC branch; every other 
content-encryption algorithm - AES-CBC, AES-GCM, AES-CCM, Camellia, ARIA, SEED 
and the rest - reached pGen.generateParameters() on an uninitialised generator, 
so the IV / nonce was drawn from a default SecureRandom and setSecureRandom() 
was silently ignored (the builder's javadoc states that random is used for 
IV/nonce generation). The generator is now initialised with the supplied random 
on the general path as well, so a caller who provides a specific randomness 
source - for a controlled or FIPS-approved DRBG, say - has it honoured for the 
content IV / nonce. The session-key generation path was unaffected and already 
used the supplied random. Because the cont
 ent IV / nonce now comes from the supplied SecureRandom, the 
org.bouncycastle.crypto.util JournalingSecureRandom / JournaledAlgorithm 
reproducible-encryption support records it in the transcript: a resumed session 
reproduces the IV / nonce by regenerating it from the replayed randomness - 
build the resuming encryptor from the content-algorithm OID - rather than by 
reusing the AlgorithmIdentifier captured from the first encryption, which no 
longer keeps the transcript aligned.</li>
   <li>The NTRU LPRime, NTRU+ and SMAUG-T KEM generators threw a 
NullPointerException when constructed with a null SecureRandom, where every 
other KEM generator - including NTRU LPRime's own SNTRU Prime counterpart in 
the same package - defaults one through 
CryptoServicesRegistrar.getSecureRandom(). This is reachable from the 
lightweight API directly, and from javax.crypto.KEM, whose newEncapsulator() 
documents a null random as a request for the provider's default.</li>
   <li>FrodoKEMEngine kept a single SHAKE instance in a field, so an engine 
reached concurrently produced wrong results. It is reached that way through 
org.bouncycastle.crypto.kems.FrodoKEMExtractor, which holds one engine for its 
lifetime: two threads extracting through one extractor interleaved the digest's 
absorb and squeeze phases, yielding shared secrets that silently did not match 
the sender's, or an IllegalStateException of &quot;attempt to absorb while 
squeezing&quot; from inside extractSecret. The digest is now built per call, as 
CMCEEngine's already was, which makes an extractor safe to share. Encapsulation 
was unaffected, since FrodoKEMGenerator builds an engine per call. Results for 
any single-threaded use are unchanged - the reference KAT vectors are 
byte-identical.</li>
   <li>The BCJSSE provider carried the TLS 1.2 coupling between the 
supported_groups extension and ECDSA over into TLS 1.3: an ECDSA signature 
scheme was treated as usable - offered in the signature_algorithms and 
signature_algorithms_cert extensions, and eligible when selecting the local 
credentials - only while the corresponding curve was among the named groups 
enabled for key exchange, both per context (a group unavailable for key 
agreement disabled the scheme outright) and per connection (the curve had to be 
in the supported_groups list about to be sent). RFC 8446 sec. 4.2.7 scopes 
supported_groups to key exchange only, with signature algorithms negotiated 
independently (sec. 4.2.3), so this incorrect restriction in TLS 1.3 has been 
removed. Ed25519, Ed448 and the RSA schemes were unaffected (as well as typical 
deployments using a default configuration for named groups).</li>
   <li>The bcmail module descriptor did not declare its 
javax.mail/javax.activation dependences, so a modular (module-path) consumer of 
the jar hit IllegalAccessError/module-resolution failures when the S/MIME 
classes touched the mail API. The descriptor now requires them optionally 
(requires static) under all four module names those libraries are known by - 
the automatic names mail and activation carried by the javax.mail:mail / 
javax.activation:activation artifacts, and the explicit names java.mail and 
java.activation carried by the newer com.sun.mail / com.sun.activation ones - a 
hard requires on any one name would break users of the others (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2389";>#2389</a>).</li>
   <li>Four type-coercion helpers in the OER / IEEE 1609.2 (ITS) decoder tested 
the wrong type in the identity fast path that lets a getInstance() factory 
return an argument that is already of the target type. 
org.bouncycastle.oer.its.ieee1609dot2.basetypes.UINT32.getInstance and 
org.bouncycastle.oer.its.etsi102941.basetypes.Version.getInstance guarded on 
UINT8 - a sibling of UINT32 under UintBase, and unrelated to Version - so 
passing a UINT8 threw ClassCastException, while passing an actual UINT32 or 
Version missed the fast path and fell through to ASN1Integer.getInstance, which 
rejects them: neither factory accepted its own type. 
org.bouncycastle.oer.its.etsi103097.EtsiTs103097DataEncryptedUnicast.getInstance
 guarded on its sibling EtsiTs103097DataEncrypted and then cast to the unicast 
type, so an EtsiTs103097DataEncrypted threw ClassCastException. 
org.bouncycastle.oer.OEROptional.getObject(Class) called 
value.getClass().isInstance(type) with the arguments transposed, which is alw
 ays false because the argument is a java.lang.Class, so the cast path was dead 
and every optional field was resolved reflectively, failing with 
IllegalStateException for a target type with no static getInstance. Each guard 
now names the type it returns, matching the sibling UINT8 / UINT16 / UINT64 and 
EtsiTs103097DataEncrypted factories (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2373";>#2373</a>).</li>
   <li>DefaultAlgorithmNameFinder and DefaultSignatureNameFinder had no entries 
at all for the ShangMi algorithms, so an SM2 signature AlgorithmIdentifier that 
DefaultSignatureAlgorithmIdentifierFinder itself produces came back named only 
by its OID string - getAlgorithmName(GMObjectIdentifiers.sm2sign_with_sm3) 
returned &quot;1.2.156.10197.1.501&quot; and hasAlgorithmName returned false. 
Both finders now name sm2sign_with_sm3 as SM3WITHSM2 and sm2sign_with_sha256 as 
SHA256WITHSM2, and DefaultAlgorithmNameFinder additionally names the sm3 
digest. All three resolve through the BC provider, as Signature and 
MessageDigest respectively. The remaining GM arc - the SM4 cipher modes, the 
sm2encrypt variants, and the SM1 / SM6 / SSF33 ciphers BC does not implement - 
is still unnamed (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2377";>#2377</a>).</li>
   <li>The RFC 4998 evidence-record classes compared the digest 
AlgorithmIdentifier named by a time-stamp authority with the one their own 
DigestCalculator uses, and did so with AlgorithmIdentifier.equals(), which 
compares the encodings. A TSA that names SHA-256 with an explicit NULL 
parameters field - DigiCert among them - therefore failed against BC's own 
calculator, which names it with the parameters absent, and 
ERSArchiveTimeStampGenerator.generateArchiveTimeStamp rejected the response 
with &quot;time stamp imprint for wrong algorithm&quot;. Both spellings name 
the same digest and RFC 5754 sec. 2 requires a receiver to accept either, while 
requiring that identifiers be generated with the parameters absent, which BC 
already does. The three affected comparisons - the two in 
ERSArchiveTimeStampGenerator and the digest check in ERSEvidenceRecord.renew - 
now use the new AlgorithmIdentifier.areEquivalent, which matches on the 
algorithm and treats an absent parameters field and NULL as 
 the same, and the consistency check across an evidence record's archive time 
stamp chain uses it too. An identifier carrying an actual parameter structure 
is never equivalent to one carrying none (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2379";>#2379</a>).</li>
   <li>EDIPartyName.toASN1Primitive emitted the nameAssigner and partyName 
DirectoryStrings without their context tags, so an EDIPartyName built through 
its public constructor could not be parsed back by EDIPartyName.getInstance, 
which correctly requires them. RFC 5280 sec. 4.2.1.6 tags both members [0] and 
[1], and those tags are explicit despite the module's IMPLICIT TAGS because 
DirectoryString is a CHOICE, which X.680 does not allow to be tagged implicitly 
- the decoder already had this right. The encoder now matches it. Note the type 
was added during the 1.85 cycle and GeneralName validates its ediPartyName 
alternative through it, so a GeneralName carrying an untagged ediPartyName - 
including one BC itself produced - is rejected where 1.84 passed it through 
unexamined; the untagged form is not read leniently (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2380";>#2380</a>).</li>
   <li>RSASSA-PSS could not be used with a RIPEMD digest through the JCA API. 
Nothing registered the RIPEMD PSS signatures, so 
Signature.getInstance(&quot;RIPEMD160WITHRSAANDMGF1&quot;) raised 
NoSuchAlgorithmException, and the generic RSASSA-PSS route with an explicit 
PSSParameterSpec failed too: 
org.bouncycastle.jcajce.provider.util.DigestFactory.getDigest returned null for 
a RIPEMD name, and isSameDigest - an allow-list of the SHA families and MD5 - 
reported two identical RIPEMD names as different digests, so the spec was 
rejected with &quot;digest algorithm for MGF should be the same as for PSS 
parameters&quot;. isSameDigest now answers true for equal names whatever the 
digest, which also covers Whirlpool, SM3, GOST3411 and anything else outside 
that allow-list; DigestFactory recognises RIPEMD128, RIPEMD160 and RIPEMD256 by 
name and OID; the three PSS signatures are registered with MGF1 over the same 
digest and a salt of the digest length; and 
DefaultSignatureAlgorithmIdentifierFi
 nder gains the matching RIPEMD*WITHRSAANDMGF1 entries with their 
RSASSA-PSS-params, so the operator/JcaContentSignerBuilder path works as well. 
Note BC continues to require the PSS hash and the MGF1 hash to be the same, 
which RFC 8017 does not itself demand (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2381";>#2381</a>).</li>
   <li>The opt-in key-size validation on CMS key-transport recipients 
(org.bouncycastle.cms.jcajce.JceKeyTransRecipient.setKeySizeValidation(true)) 
never ran for a message using RFC 9709 CEK derivation (id-alg-cek-hkdf-sha256): 
the branch that should have selected the actual content-encryption algorithm 
carried in the KDF AlgorithmIdentifier's parameters compared the encrypted-key 
byte array against the id-alg-cek-hkdf-sha256 object identifier - a comparison 
that is always false - so the check fell through to a key-size lookup on the 
outer KDF OID, which has no registered key size, and silently checked nothing. 
A key-transport EnvelopedData/AuthEnvelopedData whose transported (and 
HKDF-derived) content-encryption key did not match the key size of the 
advertised content-encryption algorithm was therefore accepted even with 
validation enabled. The recipient now dispatches on the content-encryption 
AlgorithmIdentifier's algorithm OID, so key-size validation of RFC 9709 
messages checks t
 he recovered key against the inner content-encryption algorithm. Messages with 
a matching key size, non-HKDF messages, and recipients that do not enable 
validation are unaffected.</li>
   <li>The OpenPGP v6 SEIPD (Symmetrically Encrypted Integrity Protected Data, 
version 2 / RFC 9580 sec. 5.13.2) packet parser read the AEAD chunk-size octet 
without bounding it. The chunk length is 2^(chunkSize + 6) bytes and the AEAD 
decryptor allocates a buffer of that size up front, so a crafted v6 encrypted 
message (reachable with only the recipient's public key) declaring chunkSize 24 
forced a 1 GiB allocation on decrypt, and chunkSize 25 (where the int cast of 
the chunk length wraps negative) threw a NegativeArraySizeException -- a 
pre-authentication resource-exhaustion denial of service. This is the version 6 
sibling of the version 5 AEADEncDataPacket issue fixed under CVE-2026-3505, 
which bounded that packet's chunk size at 16 but left the v6 
SymmetricEncIntegrityPacket unbounded. SymmetricEncIntegrityPacket now rejects 
a chunk-size octet outside 0..16 (a 4 MiB chunk, matching the v5 ceiling) with 
a MalformedPacketException at parse, before any allocation.</li>
   <li>The Ed25519 KeyFactory in the JDK 11+ and JDK 15+ multi-release overlays 
(META-INF/versions/11 and /15) had drifted from the base implementation on the 
OpenSSH key-spec path: it used the no-passphrase 
OpenSSHPrivateKeyUtil.parsePrivateKeyBlob overload, so a passphrase-encrypted 
openssh-key-v1 Ed25519 private key that 
KeyFactory.getInstance(&quot;Ed25519&quot;, &quot;BC&quot;).generatePrivate(new 
OpenSSHPrivateKeySpec(blob, passphrase)) decoded correctly on JDK 8 failed on 
JDK 11 and later; it also let a raw RuntimeException escape on a malformed blob 
and threw IllegalStateException (JDK 11) instead of InvalidKeySpecException for 
a non-Ed25519 key. The overlays now match the base implementation (passphrase 
support, parse errors wrapped as InvalidKeySpecException). Relatedly, the 
OpenSSH wrong-key-type and decode-failure paths across the RSA, DSA, EC and 
Ed25519 KeyFactorySpi implementations now consistently raise 
InvalidKeySpecException (previously a mix of IllegalArgumentExcep
 tion / IllegalStateException) and wrap a malformed OpenSSH public key, and an 
incorrect &quot;public key is not RSA private key&quot; message on the RSA 
private path was corrected. The multi-release test tasks now exercise the 
OpenSSH key specs against the multi-release jar.</li>
   <li>The Ant-built utility jars (bcutil-jdk15to18, bcutil-jdk14) duplicated 
org.bouncycastle.asn1.iana.IANAObjectIdentifiers, which since 1.85 (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2176";>#2176</a>) lives 
only in core and is therefore already shipped in bcprov. The shared 
ant/bc+-build.xml build-util target still copied org/bouncycastle/asn1/iana/** 
into bcutil, so a project depending on both bcprov and bcutil (for example via 
bcpkix) failed an Android/R8 build with &quot;Duplicate class 
org.bouncycastle.asn1.iana.IANAObjectIdentifiers found in modules 
bcprov-jdk15to18-1.85.jar and bcutil-jdk15to18-1.85.jar&quot;. The iana package 
is no longer bundled into bcutil (it remains in bcprov); the Gradle jdk18on 
jars were already correct (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2356";>#2356</a>).</li>
   <li>org.bouncycastle.util.BigIntegers.intValueExact (and the byte/short/long 
variants) delegated to BigInteger.intValueExact from 1.85, a Java 8 method 
Android only provides from API level 33, so on earlier Android versions any 
code path using them - most visibly loading a PKCS12 keystore, whose 
iteration-count validation calls intValueExact - crashed with 
NoSuchMethodError. The range checks are open-coded again, as they were in 1.84 
(github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2369";>#2369</a>).</li>
   <li>GOST R 34.10-94 signing (org.bouncycastle.crypto.signers.GOST3410Signer) 
raised the domain generator to the per-signature nonce k with a bare 
BigInteger.modPow, whose running time varies with the exponent. Recovering k 
from that timing yields the private key straight out of the signature equation 
s = k*m + x*r, so k is now randomised with a random multiple of q before it is 
raised, exactly as DSASigner already does with its own k. Since the domain 
parameter a has order q, raising it to a multiple of q gives 1 and the 
signature is unchanged - the RFC-style known-answer vectors in GOST3410Test 
still produce the same r and s. Those vectors drive signing from a 
FixedSecureRandom, so they now carry one further byte for the randomiser to 
consume, in the same way the DSA signing vectors already do.</li>
   <li>KCCMBlockCipher (DSTU7624-128/256/512 CCM mode) returned the input 
length rather than 0 from getUpdateOutputSize(int), but like 
CCMBlockCipher/KGCMBlockCipher it buffers all input until doFinal and produces 
no output on an update. Through the JCA layer this made the 
caller-supplied-buffer Cipher.update(input, inOff, inLen, output, outOff) 
reject a correctly sized output buffer with 
&quot;javax.crypto.ShortBufferException: output buffer too short for 
input.&quot; when decrypting. getUpdateOutputSize now returns 0, matching the 
sibling CCM/KGCM modes (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2354";>#2354</a>).</li>
   <li>J-PAKE raised values to exponents carrying private material with bare 
BigInteger.modPow calls, whose running time varies with the exponent: the 
private ephemerals x1 and x2 in round 1, x2*s and the negated form of it in 
round 2 and in the keying material - both of which carry the password - and the 
v behind each Schnorr zero-knowledge proof, which together with the published r 
would give up x. JPAKEParticipant itself notes that leaking x1 or x2 lets an 
attacker brute-force the password. All of these exponents are now randomised 
with a random multiple of q before they are raised. A multiple of q rather than 
of p-1 is sound here, and much cheaper since the exponents are the size of q: 
the generator is checked with g^q = 1 when the JPAKEPrimeOrderGroup is built, 
and each value received from the other participant is checked the same way by 
validateZeroKnowledgeProof before it is used as a base. JPAKEUtil.calculateA 
and JPAKEUtil.calculateKeyingMaterial gained overloads taking a Se
 cureRandom, and there is a new JPAKEUtil.calculateGx taking q and a 
SecureRandom; the existing overloads still work, taking the default from 
CryptoServicesRegistrar. The three-argument calculateGx has no q to work with, 
so it blinds with a multiple of p-1 and is deprecated in favour of the new one. 
The three modPow calls in validateZeroKnowledgeProof are unchanged, since their 
exponents are all public. The elliptic-curve variant was already routing its 
private scalars through ECAlgorithms.multiplySecret and needed no change.</li>
   <li>The PKIX CertPathBuilder 
(&quot;PKIX&quot;/&quot;RFC5280&quot;/&quot;RFC3280&quot;) matched candidate 
issuers by subject name only during its depth-first search, so a CertStore 
containing many self-issued certificates that share a single subject name and 
never chain to a trust anchor could be explored as a large number of partial 
paths before the build concluded no chain exists. The builder now bounds the 
total number of nodes visited per build; the limit is configurable via the 
org.bouncycastle.x509.max_cert_path_build_nodes system property (default 
262144, far above any legitimate build) and, when exceeded, the build fails 
with a CertPathBuilderException naming the property. This is the builder-side 
companion to the existing org.bouncycastle.x509.max_policy_nodes bound.</li>
   <li>A group of parse and revocation-handling entry points let an unchecked 
runtime exception (NullPointerException, ArrayIndexOutOfBoundsException, 
IllegalStateException or ArithmeticException) escape on empty, content-less or 
out-of-range input instead of the checked exception each entry point declares - 
the malformed input was rejected either way, but the leaked type could escape a 
documented throws contract. Each now fails with its declared type, and 
well-formed input is unaffected: org.bouncycastle.tsp.cms.CMSTimeStampedData 
(an empty or truncated stream; the fix also covers its 
org.bouncycastle.asn1.cms.MetaData and TimeStampDataUtil helpers) and 
org.bouncycastle.cms.CMSEnvelopedData (an EnvelopedData carrying no 
encryptedContent) now throw IOException / CMSException rather than 
NullPointerException; org.bouncycastle.tsp.TimeStampToken rejects a token whose 
SignerInfo carries no signed attributes with TSPValidationException rather than 
NullPointerException; org.bouncycastle.c
 ert.cmp.GeneralPKIMessage, org.bouncycastle.est.CSRAttributesResponse, 
org.bouncycastle.cmc.SimplePKIResponse, 
org.bouncycastle.openssl.X509TrustedCertificateBlock, 
org.bouncycastle.tsp.TimeStampRequest, org.bouncycastle.tsp.TimeStampResponse, 
org.bouncycastle.pkcs.PKCS12PfxPdu and 
org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo reject empty / no-content / 
truncated input with their declared CertIOException / IOException / 
PKCSIOException rather than a leaked NullPointerException, and 
org.bouncycastle.cert.crmf.CertificateRequestMessage.hasSigningKeyProofOfPossessionWithPKMAC
 answers false for an absent or non-signing-key proof-of-possession rather than 
throwing NullPointerException; 
org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil.parsePrivateKeyBlob and 
org.bouncycastle.math.ec.ECCurve.decodePoint reject an empty (or null) blob / 
point encoding with IllegalArgumentException rather than 
ArrayIndexOutOfBoundsException, closing the point-decode path reached by every 
untrusted-po
 int consumer (EC key parsing, ECDH/ECIES, TLS); and the PKIX revocation code 
no longer leaks a runtime exception on attacker-controlled CRL/OCSP fields - 
PKIXCertPathReviewer and X509RevocationChecker bound an out-of-range CRLReason 
code against their fixed reason table (reporting &quot;unknown&quot; instead of 
ArrayIndexOutOfBoundsException / ArithmeticException), RFC3280CertPathUtilities 
tolerates an absent reasons mask on a CRL distribution point, and 
ProvOcspRevocationChecker tolerates an OCSP response with no nonce 
extension.</li>
   <li>java.security.AlgorithmParameters.init(byte[]) is contracted to throw 
IOException on a decoding error, but several BC AlgorithmParameters SPIs (RSA 
OAEP/PSS, EC, DSA, DH, ElGamal, IES, GOST, and the GCM/CCM parameters of AES, 
ARIA, LEA and SM4) could leak an unchecked exception. Each affected 
engineInit(byte[]) and both loadParameters helpers now convert a leaked runtime 
exception to IOException; well-formed parameters are unaffected.</li>
   <li>The Classic McEliece fixed-weight vector generator 
(org.bouncycastle.crypto.kems.cmce, ISO/IEC 18033-2:2006/Amd 2:2026 sec. 13.11 
step 4) checked its candidate indices for repetition with a scan that stopped 
at the first collision it found - the outer loop carried an &quot;and no 
duplicate yet&quot; condition and the inner loop broke out - so the time taken 
to reject a candidate set depended on which pair of indices collided. Those 
indices are the support of the error vector the encapsulation is built from, 
and while each set is drawn from fresh randomness rather than from long-term 
key material, the scan now visits every pair and accumulates the answer through 
the same branchless equality mask the generator already uses to write the error 
vector, so a rejected set costs the same regardless of where the collision was. 
The accept/reject decision is unchanged for every candidate set, as is the 
number of bytes drawn from the random source, so generated ciphertexts and 
shared secr
 ets are identical and the known-answer vectors are unaffected. This is a 
hardening rather than a fix for a demonstrated attack, and it is the Classic 
McEliece counterpart of the HQC fixed-weight sampler hardening in this release. 
The rejection loop's own iteration count remains data-dependent, as it is in 
the specified algorithm; the legacy NIST round 3 implementation under the 
deprecated org.bouncycastle.pqc.crypto.cmce is unchanged.</li>
   </ul>
   <!-- raw HTML omitted -->
   </blockquote>
   <p>... (truncated)</p>
   </details>
   <details>
   <summary>Commits</summary>
   <ul>
   <li>See full diff in <a 
href="https://github.com/bcgit/bc-java/commits";>compare view</a></li>
   </ul>
   </details>
   <br />
   
   
   [![Dependabot compatibility 
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.bouncycastle:bcpkix-jdk18on&package-manager=maven&previous-version=1.84&new-version=1.85)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
   
   Dependabot will resolve any conflicts with this PR as long as you don't 
alter it yourself. You can also trigger a rebase manually by commenting 
`@dependabot rebase`.
   
   [//]: # (dependabot-automerge-start)
   [//]: # (dependabot-automerge-end)
   
   ---
   
   <details>
   <summary>Dependabot commands and options</summary>
   <br />
   
   You can trigger Dependabot actions by commenting on this PR:
   - `@dependabot rebase` will rebase this PR
   - `@dependabot recreate` will recreate this PR, overwriting any edits that 
have been made to it
   - `@dependabot show <dependency name> ignore conditions` will show all of 
the ignore conditions of the specified dependency
   - `@dependabot ignore this major version` will close this PR and stop 
Dependabot creating any more for this major version (unless you reopen the PR 
or upgrade to it yourself)
   - `@dependabot ignore this minor version` will close this PR and stop 
Dependabot creating any more for this minor version (unless you reopen the PR 
or upgrade to it yourself)
   - `@dependabot ignore this dependency` will close this PR and stop 
Dependabot creating any more for this dependency (unless you reopen the PR or 
upgrade to it yourself)
   
   
   </details>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to