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

   Bumps `bouncycastle.version` from 1.85 to 1.86.
   Updates `org.bouncycastle:bcpkix-jdk18on` from 1.85 to 1.86
   <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.87<br />
   Date: 2026, TBD</p>
   <h3>2.1.2 Defects Fixed</h3>
   <ul>
   <li>
   <p>A KeyAgreement asked for its shared secret before doPhase returned data 
rather than refusing. javax.crypto.KeyAgreement specifies IllegalStateException 
for that state, but nothing in the provider tracked it, so each SPI handed back 
whatever its result field held: for Diffie-Hellman that was the private value 
itself - engineInit seeded result with x, so generateSecret() returned the 
private exponent padded to the prime's length and 
generateSecret(&quot;AES&quot;) an all-zero key taken from that padding - while 
ECDH returned null and its named-algorithm overload raised 
NullPointerException. BaseAgreementSpi now records whether a doPhase has 
completed the agreement since the last init and refuses the request with an 
IllegalStateException naming the algorithm, so every family in the provider - 
DH, ECDH and ECMQV, the SM2 exchange, both ECGOST families, XDH, SM9 and 
NewHope - answers the same way, and the DH SPI no longer holds the private 
value in that field at all.</p>
   </li>
   <li>
   <p>Mac.getInstance and KeyGenerator.getInstance by the HMAC SHA-512/224 and 
SHA-512/256 object identifiers (1.2.840.113549.2.12 and .13) failed, although 
the same algorithms resolved by name and the matching SecretKeyFactory aliases 
were registered: the SHA512 mappings called addHMACAlgorithm for the two 
truncated variants without the addHMACAlias that registers their OIDs against 
Mac and KeyGenerator. Both are now aliased, as every other HMAC in that class 
already was.</p>
   </li>
   <li>
   <p>A KTSParameterSpec naming an HKDF key-derivation function with a 
parameters field - a form the provider does not service - was accepted at 
Cipher init and then failed out of wrap or unwrap with an unchecked 
IllegalStateException neither method declares. The KTS key-wrapping Ciphers 
(ML-KEM, Classic McEliece, FrodoKEM, the composite KEM and RSA-KEM) now 
validate the spec's KDF when they take it, reporting an unserviceable one as 
the InvalidAlgorithmParameterException engineInit declares, which is what the 
javax.crypto.KEM services already did through KdfUtil.resolveKemSpec.</p>
   </li>
   <li>
   <p>A DTLS handshake deadlocked when a handshake message ahead of the peer's 
ChangeCipherSpec (a client's CertificateVerify, say) was lost while the 
ChangeCipherSpec and Finished behind it arrived: the record layer moved its 
read epoch on at the ChangeCipherSpec and then discarded every retransmission 
of the lost message as belonging to the old epoch, whose records are only 
accepted once the handshake has completed. Each side then waited on the other 
until a handshake timeout, if one was configured, ended it. Every 
client-authenticated handshake, and every handshake in which the server issues 
a NewSessionTicket, was exposed. Until the handshake completes, handshake 
records from the current epoch are now still accepted after the read epoch has 
moved on, and each message is checked against the epoch of the record that 
carried it. The DTLS loopback tests now run their handshakes at 10% datagram 
loss in each direction, with a client-authenticated handshake at 25%.</p>
   </li>
   <li>
   <p>The lightweight SubjectPublicKeyInfoFactory and PrivateKeyInfoFactory 
encoded a GOST R 34.10-2012 key on one of the legacy CryptoPro curves under 
id-GostR3410-2001, although RFC 9215 sec. 4.2 permits those curves for 2012 
keys. The digestParamSet now decides: a GOST R 34.11-94 parameter set means 
2001 (RFC 4491 sec. 2.3.2), a GOST R 34.11-2012 digest or none means 2012 with 
256/512 taken from the curve field size, and any other value is rejected. 
GOST3410PublicKeyAlgParameters treats digestParamSet as OPTIONAL on both read 
and write per RFC 9215, and PrivateKeyInfoFactory now passes attributes through 
for ECGOST3410 keys (bc-csharp github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/707";>#707</a>).</p>
   </li>
   <li>
   <p>The name-constraint host canonicalisation removed a single RFC 1034 
root-label dot, the only empty label a name may legally carry, but nothing 
refused the ones that are not legal: a dNSName, rfc822Name host or 
uniformResourceIdentifier host such as &quot;example.com..&quot; kept a phantom 
empty label after the strip and so matched no constraint at all, escaping an 
excluded subtree naming the host it appears to carry. A tested name whose host 
carries an empty label - a second trailing dot, a doubled dot or a leading dot 
- is now refused outright wherever a constraint of that type is in force, 
rather than canonicalised into a name it is not: removing the extra dots would 
decide on the caller's behalf that &quot;example.com..&quot; names example.com, 
which is not how a consumer resolving or comparing the name reads it, and 
refusing fails closed in both directions where canonicalising would newly admit 
such a name under a permitted subtree. The single trailing dot is canonicalised 
 as before, a bare &quot;.&quot; remains the root label rather than an empty 
one, and the guard is scoped to the host, so the doubled dot a quoted local 
part may legally carry is unaffected. Constraints are untouched - one may still 
begin with a dot, which is how this implementation spells &quot;subdomains 
only&quot; (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2436";>#2436</a>).</p>
   </li>
   <li>
   <p>SSLContext.createSSLEngine() from the BCJSSE provider in the 1.86 bctls 
jar failed with NoSuchMethodError on every JDK from 9 up, leaving engine-based 
users of the provider (Netty, Vert.x and the like) unable to open a connection. 
The jdk1.5 and jdk1.9 copies of the package-private SSLEngineUtil had declared 
create(ContextData) with different return types since 2019 - SSLEngine and 
ProvSSLEngine - and the root ProvSSLContextSpi, compiled against the first, is 
paired at runtime with the versions/9 copy on any modern JDK. Until 1.86 the 
java9 compile had hidden this by implicitly recompiling the whole base tree 
into META-INF/versions/9 (447 classes, ProvSSLContextSpi among them); the 
-implicit:none added in 1.86 to stop that duplication exposed the mismatch. The 
jdk1.9 copy now declares the same return type as the root one, a JDK 25 test 
creates engines against the built jar, and a new multiReleaseCheck Gradle task 
on every distributed jar reads the constant pool of each class in
  the jar and in the sibling BC jars it depends on and fails the build when a 
member reference does not resolve against the copy of its target that a JDK 
would pair it with, so the class of defect cannot ship again; the same check 
runs on arbitrary jars, a published release included, as multiReleaseCheckJar 
(github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2448";>#2448</a>).</p>
   </li>
   <li>
   <p>The BCFKS key store derived its scrypt keys with the block size r in 
place of the parallelization parameter p, while writing the p the caller 
configured out to the store: BcFKSKeyStoreSpi passed getBlockSize() to 
SCrypt.generate for both arguments and never read the encoded parallelization 
parameter at all, so every store whose ScryptConfig gave a p other than its r 
encoded parameters that do not derive its own keys. The store was 
self-consistent - BC read back what BC wrote - but a conformant RFC 7914 reader 
computed a different key and so failed the integrity check and the store 
decryption, and BC could not open such a store written by anyone else. 
Derivation now follows RFC 7914. A store written by 1.86 or earlier is still 
read: the integrity check is retried under the old convention, and where a 
signature check leaves no MAC to settle it the store decryption is retried 
instead, in both cases reporting the failure of the encoded parameters rather 
than of the fallback. The wr
 ite side is governed by org.bouncycastle.bcfks.scrypt_p_eq_r, default true, 
which writes p equal to r whatever the ScryptConfig asked for: the two 
conventions then agree, so a store written here is both RFC 7914 correct and 
readable by 1.86 and earlier. Clearing the property honours the configured p, 
which those releases cannot read unless p already equals r; the default is 
intended to become false in a later release, once enough of the installed base 
is writing parameters that describe themselves. Loading with a 
BCFKSLoadStoreParameter carrying a ScryptConfig accepts an encoded p equal to 
either the configured p or the block size, so a store round trips under the 
configuration that wrote it whichever way the property was set; every other 
parameter is compared as before. The parallelization parameter is now bounded 
alongside the cost parameter before the derivation, as the PKCS#8 and PKCS#12 
scrypt paths already bound it.</p>
   </li>
   <li>
   <p>Building an evidence record was cubic in the number of data objects: 
SortedHashList and SortedIndexedHashList held their hashes in a LinkedList and 
found each insertion point by walking it with get(index), so a single add() was 
quadratic in the position it inserted at and building a list of n hashes cubic, 
and both lists sit on the generation path - the reduced hash tree of 
ERSArchiveTimeStampGenerator, the Merkle tree of 
BinaryTreeRootCalculator.computeRootHash(), and the hash list of every 
ERSDataGroup. Each now collects its hashes and sorts them once, in toList(); 
the sort is stable and the old insertion placed a hash after the last one 
comparing equal to it, which is where a stable sort puts it, so the order of 
the leaves and every root hash are unchanged. getFirst() answers with a scan 
rather than a sort and toList() sorts a copy, so neither accessor disturbs what 
has been added. Generating a time-stamp request over 8,000 data objects goes 
from about 210 seconds to under a
  tenth of a second, and 100,000 objects, which the old code could not reach in 
any practical time, takes about 0.2 seconds (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2456";>#2456</a>).</p>
   </li>
   <li>
   <p>An ERSDataGroup recomputed its hash on every request rather than taking 
it from the cache ERSCachingData exists to provide: the group overrode 
getHash(), which left the calculateHash() the cache calls unreachable - and 
wrong, as it copied the member hashes with a loop bounded by the size of the 
empty list it was copying into, so it would have digested nothing had anything 
reached it. The computation is back in calculateHash() and the override is 
gone, so a group's hash is computed once per digest algorithm and 
previous-chain hash, as every other ERSData's is. The value itself is 
unchanged.</p>
   </li>
   <li>
   <p>ERSArchiveTimeStampGenerator rebuilt its reduced hash tree from the data 
objects on every call, so the usual generateTimeStampRequest() followed by 
generateArchiveTimeStamp() or generateArchiveTimeStamps() built it twice. The 
leaves are now built once and dropped when data or a previous chain is added. A 
Set of the data groups it had been given, which nothing ever read, has gone 
with it.</p>
   </li>
   <li>
   <p>Grain-128AEAD returned corrupted plaintext from a decryption driven in 
chunks. The stream cipher data operator splits a processBytes() call that spans 
the buffered authentication tag into two output segments, the bytes released 
from the tag buffer and then the bytes taken straight from the caller's input, 
and wrote the second segment at the caller's output offset instead of after the 
first, so the second segment overwrote the head of the first and the tail of 
the reported output was never written at all. The call still returned the full 
byte count, and because the engine's state update depends on the input and the 
keystream rather than on where the output lands, the tag still verified: the 
wrong plaintext came back with no error raised. Any chunk after the first that 
carried more than the 8 byte tag length was affected. Grain-128AEAD is the only 
engine that uses this operator, and one shot decryption, the encryption path 
and every other AEAD engine were unaffected. The second s
 egment is now written at the advanced offset, matching the equivalent step of 
the general decryption path (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2447";>#2447</a>).</p>
   </li>
   <li>
   <p>The AEAD stream cipher data operator, which Grain-128AEAD alone uses, 
wrote its output without first checking that the caller's buffer was long 
enough, so a short output buffer surfaced as an ArrayIndexOutOfBoundsException 
from inside the engine rather than as the OutputLengthException the general 
path reports for every other AEAD engine. Both directions of processBytes(), 
and processByte(), now check before anything is written or buffered, and only 
when the call releases output, as the general path does.</p>
   </li>
   <li>
   <p>The RFC 9709 content-encryption AlgorithmIdentifier, which carries the 
real algorithm inside the parameters of an outer id-alg-cek-hkdf-sha256, was 
unwrapped at only one of the points where a CMS recipient makes a decision 
about it. Key-size validation was corrected for plain key transport in 1.86, 
but the same call in the KEK, RSA-KTS and KEM recipients, and in the 
key-transport recipient's own ORI-KEM branch, still compared the recovered key 
against the outer identifier, which registers no key size, so 
setKeySizeValidation(true) silently checked nothing there; the 
setAllowedContentAlgorithms allow-list and the setMinimumTagSize floor were 
applied to the outer identifier on every recipient family, including the one 
already corrected, so neither constrained an RFC 9709 message. The unwrap now 
happens once for the key-size check and once for the two policy checks, and 
every recipient polices and validates the content-encryption algorithm the 
message actually carries. A recipient
  with no allow-list, no tag floor and no key-size validation configured 
behaves exactly as before; a caller who listed id-alg-cek-hkdf-sha256 in an 
allow-list in order to admit RFC 9709 messages must now list the 
content-encryption algorithms themselves (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2446";>#2446</a>).</p>
   </li>
   <li>
   <p>A CMS message whose EncryptedContentInfo named the RFC 9709 key 
derivation but carried no readable content-encryption AlgorithmIdentifier in 
its parameters was reported as a NullPointerException, or as an 
IllegalArgumentException from the ASN.1 decoder, out of methods declared to 
throw CMSException, RecipientInformation.getContent() among them. The four 
places that resolve the wrapper - the CEK derivation, the content cipher 
selection, the key-size check, and the recipient's allowed-algorithm and 
tag-size checks - now share one resolver, which reports an absent or unreadable 
inner algorithm as a CMSException.</p>
   </li>
   <li>
   <p>The two YubiKey OpenPGP smart-card decryptor factories zeroized the user 
PIN array the KeyPassphraseProvider handed them rather than a copy of it, in a 
finally block after each private-key operation. Both providers BC ships return 
the application's own array by reference - DefaultKeyPassphraseProvider hands 
back the char[] it has cached for the key, and the provider inside 
OpenPGPApi.editKey returns its argument - so the first card operation destroyed 
the caller's PIN and the next private-key operation presented an all-zero PIN 
to the card, which the card refuses at the cost of a PIN retry. The PIN is now 
fetched as a clone the card operation owns - in 
OpenPGPSmartCard.requireUserPin, where the smart-card restructuring of this 
release put the fetch the two factories used to make - and 
KeyPassphraseProvider.getKeyPassword records that the array it returns stays 
owned by the provider (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2444";>#2444</a>).</p>
   </li>
   <li>
   <p>The CRMF PKIPublicationInfo structure accepted, and could be built with, 
publication information RFC 4211 sec. 6.3 forbids: pubInfos MUST NOT be present 
if the action is dontPublish, and the field is SEQUENCE SIZE (1..MAX), so a 
present one is never empty. Both contradictions are now rejected with an 
IllegalArgumentException, on parsing and on construction from an array of 
SinglePubInfo. An absent pubInfos with the pleasePublish action, which is how 
the RFC spells &quot;don't care&quot;, is unaffected, and no constructor in the 
library could produce either rejected form.</p>
   </li>
   <li>
   <p>The CMS RFC 8418 key agreement schemes 
(dhSinglePass-stdDH-hkdf-sha256/384/512, used with X25519 and X448) derived the 
key-encryption key with the user keying material in the entityUInfo of the 
ECC-CMS-SharedInfo but never as the HKDF salt, where RFC 8418 sec. 2.2 requires 
both - its recipe is salt = ukm, PRK = HKDF-Extract(salt, K), KEK = 
HKDF-Expand(PRK, DER(ECC-CMS-SharedInfo), SizeInOctets(KEK)). A message 
carrying a ukm therefore did not interoperate with a conforming implementation 
in either direction. The ukm is now passed as the salt as well, on both the 
generating and the receiving side, for those three schemes. A message with a 
ukm written by 1.86, the only release with RFC 8418 support, is not readable by 
this release and vice versa; messages without a ukm, and the X9.63-KDF key 
agreement schemes, are unaffected. The round-trip test now covers both the ukm 
and no-ukm cases for all six curve and scheme combinations, and checks the 
key-encryption key against the RFC's 
 own recipe rather than only against BC itself (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2454";>#2454</a>).</p>
   </li>
   <li>
   <p>A JKS store shorter than the SHA-1 checksum it ends with threw an 
unchecked ArrayIndexOutOfBoundsException out of KeyStore.load, which declares 
IOException for a store it cannot read: JKSKeyStoreSpi.validateStream 
subtracted the digest size from the raw store length without checking it, so 
the digest update clamped its negative length to zero and the System.arraycopy 
that lifted the stored checksum out failed on a negative source index. The 
length is now checked against the checksum plus the 12-byte header before the 
checksum position is used, and a store too short to carry either is reported as 
an EOFException. The JKS store is reached through the compatibility probe in 
AdaptingKeyStoreSpi, so any key store type that probes for it was exposed, and 
the legacy jdk1.1 and jdk1.4 provider copies carry the same fix (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2451";>#2451</a>).</p>
   </li>
   <li>
   <p>A custom Argon2BytesGenerator.BlockPool was left to zeroise the blocks it 
recycled itself, and had no way to know how many blocks to hold: the generator 
returned each block to the pool with the password-derived data still in it, so 
only the FixedBlockPool BC ships cleared them, and sizing any other pool meant 
replicating the internal memory alignment and the block count of the fill step. 
The generator now clears every block before it goes back, so a pool neither has 
to clear nor can observe that data, and 
Argon2BytesGenerator.getBlockCount(memory, lanes) gives the number of blocks a 
run takes - which the default pool now uses, so it no longer discards and 
reallocates the four blocks of the fill step on every call. FixedBlockPool 
drops the two clears it no longer needs, leaving one zeroisation per block per 
use rather than two, and a generateBytes() that fails part way through now 
returns and clears the blocks it took, along with its own working buffer, 
rather than leaving both 
 to the garbage collector (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2452";>#2452</a>).</p>
   </li>
   <li>
   <p>The PKCS#12 key stores wrote the MAC key-derivation parameters of a file 
they had loaded into every file they wrote afterwards, under whatever password 
the caller stored with. For PKCS12-PBMAC1 that carried the loaded file's PBKDF2 
salt, iteration count, key length and PRF, because the parameters were minted 
only when the store held none and were then assigned back, so the branch ran 
once per store object rather than once per write - which also meant one store 
reused a single PBKDF2 salt across every write, including writes under 
different passwords, with no file loaded at all. The classic store inherited 
the MAC salt length and digest algorithm the same way, so a file declaring a 
zero-length MAC salt was re-stored with one, and a file it had loaded under RFC 
9579 handed on that file's PBKDF2 salt too, both stores reading PBMAC1. The 
values were also latched before the MAC was verified and were not cleared by 
the load(null, null) a caller must issue to recover, so a file that f
 ailed the check left them behind for the caller's own file. The PBKDF2 salt 
and the MAC salt are now generated for every write, the MAC salt at no fewer 
than 8 octets, nothing is latched until the file has verified, and an 
AlgorithmIdentifier supplied through a PKCS12StoreParameter is still written as 
it was given. A loaded file's PRF, key length, digest algorithm and MacData 
iteration count are still kept, the last as before; the PBKDF2 count is kept 
where it is at least the count being written with and raised to it otherwise, 
since the file being re-stored is not the one that count was chosen for - RFC 
9579's own test vectors ask for 2048. That count is now 
org.bouncycastle.pkcs12.pbkdf2_it_count, default 65,536, the write-side 
counterpart for a PBMAC1 MAC of what org.bouncycastle.pkcs12.store_it_count is 
for the PBE. Reading is unaffected: a file's MAC is verified with the 
parameters it carries, whatever they are (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2
 450">#2450</a>).</p>
   </li>
   <li>
   <p>Cipher.SM9 took its data-encapsulation mode for decryption from the 
ciphertext rather than from the mode the Cipher was configured with. The GM/T 
0080-2020 SM9Cipher structure names the mode in an enType field, but GM/T 
0044.4 defines the authenticator as C3 = MAC(K2, C2), over the encapsulated 
message alone, so enType is not covered by it: re-encoding a ciphertext with 
the other enType leaves C1, C3 and C2 untouched and steers the recipient into 
the other mode. GM/T 0044.4 takes K1 and K2 from a single KDF output of klen = 
mlen + K2_len bits in stream mode and K1_len + K2_len bits in SM4 mode, where 
K1_len = 128, so when C2 is 16 bytes long the two modes make the identical KDF 
call and derive the same K1 and K2: a one-block SM4 ciphertext relabelled as 
stream mode passes the MAC check and the recipient returns K1 xor C2 - from 
which both the SM4 key K1 and the padded plaintext block follow, wherever that 
output is observable. CipherSpi now decrypts in the configured mode and r
 ejects a ciphertext whose enType disagrees with it, so the mode is symmetric 
between encryption and decryption. That check compares two values a relabelling 
attacker can make agree, and so does not by itself protect a recipient whose 
Cipher is configured for stream mode - the relabelled one-block ciphertext then 
matches the configuration - so SM9Engine additionally refuses a 16-byte C2, the 
one C2 length at which the two modes collide, in both modes and both 
directions: on decryption, and on encryption a 16-byte message in stream mode 
and a message of fewer than 16 bytes, which pads to one block, in SM4 mode. 
Refusing the length on decryption protects the recipient that does so, but the 
message a relabelled ciphertext gives away is the SM4-mode sender's, who cannot 
tell whether the recipient's implementation refuses it, which is why the SM4 
mode no longer produces one; messages of every other length are unchanged in 
both modes. A stream-mode ciphertext must accordingly be decrypted 
 through a stream-mode Cipher (&quot;SM9/XOR/NoPadding&quot;) rather than the 
SM4-mode default that Cipher.getInstance(&quot;SM9&quot;) gives; a message of 
fewer than 16 bytes has to be sent in stream mode, and one of exactly 16 bytes 
- a 128-bit key, say - in SM4 mode; and a ciphertext made by an earlier version 
whose C2 is 16 bytes long is no longer decrypted, whichever mode wrote it - a 
one-block SM4-mode ciphertext, or a stream-mode one carrying a 16-byte message. 
The SM9 KEM is unaffected.</p>
   </li>
   <li>
   <p>Decrypting an OpenPGP message in two steps - recovering the session key 
from a SKESK packet and then decrypting the SEIPD v1 body through 
PGPEncryptedDataList.extractSessionKeyEncryptedData() - stopped detecting a 
wrong passphrase. 1.86 suppressed the legacy CFB &quot;quick check&quot; on the 
two repeated prefix bytes for every session-key decryption, to close the 
Mister-Zuccherato oracle on the path a PKESK session key reaches, but the same 
class also carries password-derived session keys, where reporting the check is 
what identifies a wrong passphrase and lets the next passphrase or SKESK packet 
be tried. A SKESK v4 packet deriving the session key from the S2K output 
directly (no encrypted session key) yields a well formed session key for any 
passphrase, so a wrong one no longer failed at all: it surfaced as a parse or 
integrity failure further down the stream. BouncyCastle's own high-level API 
decrypts this way, so OpenPGPMessageProcessor took the first wrong passphrase 
offe
 red for a success and never tried the remaining ones. A new 
PGPEncryptedDataList.extractSessionKeyEncryptedData(boolean) states whether the 
session key came from a password: true restores the check and with it the 
PGPDataValidationException on a wrong passphrase, the existing no-argument 
method goes on suppressing it, and the high-level API passes true on its 
passphrase paths alone, so a session key recovered from a public key operation 
is still never quick checked (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2459";>#2459</a>).</p>
   </li>
   <li>
   <p>Both copies of PKIXCertPathReviewer (org.bouncycastle.pkix.jcajce and the 
legacy org.bouncycastle.x509) took the first date-valid CRL issued by the 
certificate's issuer as an answer about that certificate, applying neither of 
the RFC 5280 sec. 6.3.3 rules that decide whether a CRL covers it: the 
(b)(2)(i) match between a name in the CRL's issuing distribution point and a 
name in the certificate's distribution point, and the (d) intersection of the 
revocation reasons the two assert. Only the (b)(2)(ii) to (iv) onlyContains 
booleans were applied. A CA-signed, in-date CRL with no entries, scoped to 
another distribution point or to a partition of the revocation reasons, was 
therefore reported as proof of non-revocation - isValidCertPath() true with an 
empty error list for a certificate its own CA had revoked for key compromise, 
where CertPathValidator(&quot;PKIX&quot;) rejects the same chain against the 
same trust anchor - and it suppressed the distribution point fetch that would o
 therwise have retrieved the authoritative CRL. Where the reviewer makes the 
trust decision rather than serving as diagnostics beside a real validation this 
is a revocation bypass, and SignedMailValidator (bcmail) reaches it with the 
CRLs carried inside the signed message. Both copies now apply the (b)(2)(i) 
name match and require a CRL to cover every revocation reason before it can 
settle the certificate's status, through the public PKIXCRLValidator helpers 
the validation engine already uses, and keep looking when a candidate does not 
qualify - falling back, as before, to the distribution point fetch and then to 
the existing &quot;no valid CRL found&quot; error. A CRL carrying no issuing 
distribution point, one naming the certificate's own distribution point, and 
one naming the certificate issuer (the distribution point the engine falls back 
to) are all still accepted.</p>
   </li>
   <li>
   <p>RFC3280CertPathUtilities.checkCRL threw java.lang.NullPointerException 
rather than a CertPathValidatorException when every candidate CRL for a 
distribution point was skipped instead of rejected, which is what happens when 
the reasons a CRL covers add nothing to those already checked - the RFC 5280 
sec. 6.3.3 (d) case - since the exception it rethrows is only ever recorded in 
a catch block. Validation failed closed either way, but outside the declared 
contract of CertPathValidator.validate(); a run with nothing recorded now 
reports &quot;No valid CRL found.&quot;. All four copies are corrected: pkix, 
prov, and the prov jdk1.3 and jdk1.4 overlays.</p>
   </li>
   <li>
   <p>TupleHash prefixed an element of 2^28 bytes or more with a length 
computed in int arithmetic: org.bouncycastle.crypto.digests.XofUtils built the 
encode_string prefix of NIST SP 800-185 sec. 2.3.3 as left_encode(len * 8) with 
len an int, so the bit length wrapped before it reached the long parameter it 
was passed to. A single 256 MiB update wrapped it negative, and left_encode 
sizes its output by shifting its argument right eight bits at a time, which 
never reaches zero from a negative value, so the call did not return; at 512 
MiB the length wrapped to zero and the element carried the prefix of an empty 
one, letting two different tuples absorb the same byte string - the ambiguity 
the tuple encoding of sec. 5.3 exists to prevent. The multiply is now widened, 
as the other left_encode and right_encode call sites in CSHAKEDigest, KMAC, 
TupleHash and ParallelHash already were, and left_encode and right_encode 
refuse a negative length rather than spinning on one, so a negative output 
 length handed to the three-argument doFinal of TupleHash, ParallelHash or KMAC 
reports IllegalArgumentException instead of not returning. An element below 
2^28 bytes is unaffected, the two arithmetics agreeing exactly there.</p>
   </li>
   <li>
   <p>KeyAgreement.init() reported an initialisation it could not carry out as 
an unchecked exception for the unified and VKO agreements, where the JCA 
declares InvalidKeyException and InvalidAlgorithmParameterException. The ECCDHU 
and X25519U/X448U services took a plain UserKeyingMaterialSpec, or no spec at 
all, and carried it into the agreement, throwing ClassCastException from the 
cast that follows; the DHU and DH MQV services accepted the initialisation and 
then threw NullPointerException from doPhase, where the absent spec is read 
back; and the ECGOST3410 and ECGOST3410-2012 VKO services put a null where the 
UKM belongs, although RFC 7836 sec. 4.3 makes the UKM optional with the value 
1. Each of them now reports a parameter error as ECMQV already did, and the VKO 
agreements apply the RFC 7836 default, so an agreement initialised without a 
UserKeyingMaterialSpec derives the key a UKM of 1 gives rather than failing. 
The user keying material itself was not being dropped anywhere: e
 very key agreement service carrying a key derivation function was checked for 
it.</p>
   </li>
   <li>
   <p>The RFC 5990 RSA-KTS CMS recipients (JceKTSKeyTransEnvelopedRecipient and 
JceKTSKeyTransAuthenticatedRecipient, through JceKTSKeyUnwrapper) took the 
keyLength carried in the message's RsaKemParameters as the number of octets for 
the key encapsulation mechanism to derive, and only compared it with the 
key-wrapping algorithm once the derivation had been done. EnvelopedData is not 
integrity protected and the recipient reaches this before anything about the 
message has been verified, so the declared length decided how much the key 
derivation function was asked to produce, and a large enough one also 
overflowed the bit count it was converted into. RFC 5990 sec. 4 fixes the 
length of the derived key as the key length of the data encapsulation 
mechanism's key-wrapping algorithm, so the recipient now derives that length 
and rejects a keyLength which disagrees with it before deriving anything, 
reporting it as the CMSException the API declares - the check the RFC 9629 
KEMRecipientInfo pa
 th already made against its kekLength.</p>
   </li>
   <li>
   <p>The read-side well-formedness checks on UTCTime and GeneralizedTime 
bounded the day at 01-31 without regard to the month, so the 30th of February, 
the 31st of April and the 29th of February in a common year were all accepted 
and the lenient calendar behind getDate() rolled each into the following month: 
&quot;180230101423Z&quot; was read back as the 2nd of March 2018, an instant 
some other encoding already denotes, and a certificate validity or CRL update 
time could name a day that does not exist. The day is now checked against the 
length of the month, February following the Gregorian leap rule and a UTCTime's 
two-digit year resolved through the RFC 5280 sec. 4.1.2.5.1 window, so such a 
value is rejected on read as OpenSSL rejects it. The JDK's own 
CertificateFactory accepts and rolls these, so 
org.bouncycastle.asn1.allow_non_der_time - already the switch for the 
write-side DER restrictions - also admits the impossible day for a caller that 
has to read what it accepts; the impo
 ssible month and the zero day stay rejected either way.</p>
   </li>
   <li>
   <p>When the provider's CertPathBuilder validated the certification path of 
an indirect CRL's signer, it did so with the builder defaults rather than the 
caller's PKIXBuilderParameters: the maximum path length was always 5 and the 
excluded certificates set was always empty, so a signer the caller had excluded 
could still vouch for a CRL, a caller's tighter path length limit was not 
applied to the signer's path, and a caller's looser one (or no limit at all) 
could leave a longer but otherwise valid signer path rejected. Both settings 
now carry through to the CRL signer's path build.</p>
   </li>
   <li>
   <p>The provider's CertPathBuilder applied 
PKIXBuilderParameters.setMaxPathLength() one certificate too leniently, 
building a path with one intermediate certificate more than the limit (so a 
limit of 0 admitted a target and one intermediate, and the default of 5 
admitted six), and counted self-issued certificates against it. The limit now 
counts the non-self-issued intermediate certificates, as PKIXBuilderParameters 
defines it, so a path which only built because of the extra certificate, one 
with six intermediates under the default limit for example, now needs the limit 
raised.</p>
   </li>
   <li>
   <p>The DSTU 7624 (Kalyna) GCM mode and MAC, KGCMBlockCipher and KGMac, 
accepted an operation with both the associated text and the data empty, which 
DSTU 7624:2014 sec. 12.1 excludes. Its tag is then E_K(0), which is the GHASH 
key itself: Kalyna GCM does not mask the tag with a nonce-derived block as NIST 
GCM does, so a caller who authenticated an empty message disclosed the 
authentication key, and anyone holding it can construct a different message 
with the same tag as any other message seen under that key. The decrypting side 
accepted E_K(0) as the tag of an empty message in the same way. Both directions 
now reject the case, encryption and KGMac with a DataLengthException and 
decryption with an InvalidCipherTextException; either one of the associated 
text and the data may still be empty. The Mac.DSTU7624GMAC services were 
affected through KGMac.</p>
   </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 />
   
   Updates `org.bouncycastle:bcprov-jdk18on` from 1.85 to 1.86
   <details>
   <summary>Changelog</summary>
   <p><em>Sourced from <a 
href="https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.md";>org.bouncycastle:bcprov-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.87<br />
   Date: 2026, TBD</p>
   <h3>2.1.2 Defects Fixed</h3>
   <ul>
   <li>
   <p>A KeyAgreement asked for its shared secret before doPhase returned data 
rather than refusing. javax.crypto.KeyAgreement specifies IllegalStateException 
for that state, but nothing in the provider tracked it, so each SPI handed back 
whatever its result field held: for Diffie-Hellman that was the private value 
itself - engineInit seeded result with x, so generateSecret() returned the 
private exponent padded to the prime's length and 
generateSecret(&quot;AES&quot;) an all-zero key taken from that padding - while 
ECDH returned null and its named-algorithm overload raised 
NullPointerException. BaseAgreementSpi now records whether a doPhase has 
completed the agreement since the last init and refuses the request with an 
IllegalStateException naming the algorithm, so every family in the provider - 
DH, ECDH and ECMQV, the SM2 exchange, both ECGOST families, XDH, SM9 and 
NewHope - answers the same way, and the DH SPI no longer holds the private 
value in that field at all.</p>
   </li>
   <li>
   <p>Mac.getInstance and KeyGenerator.getInstance by the HMAC SHA-512/224 and 
SHA-512/256 object identifiers (1.2.840.113549.2.12 and .13) failed, although 
the same algorithms resolved by name and the matching SecretKeyFactory aliases 
were registered: the SHA512 mappings called addHMACAlgorithm for the two 
truncated variants without the addHMACAlias that registers their OIDs against 
Mac and KeyGenerator. Both are now aliased, as every other HMAC in that class 
already was.</p>
   </li>
   <li>
   <p>A KTSParameterSpec naming an HKDF key-derivation function with a 
parameters field - a form the provider does not service - was accepted at 
Cipher init and then failed out of wrap or unwrap with an unchecked 
IllegalStateException neither method declares. The KTS key-wrapping Ciphers 
(ML-KEM, Classic McEliece, FrodoKEM, the composite KEM and RSA-KEM) now 
validate the spec's KDF when they take it, reporting an unserviceable one as 
the InvalidAlgorithmParameterException engineInit declares, which is what the 
javax.crypto.KEM services already did through KdfUtil.resolveKemSpec.</p>
   </li>
   <li>
   <p>A DTLS handshake deadlocked when a handshake message ahead of the peer's 
ChangeCipherSpec (a client's CertificateVerify, say) was lost while the 
ChangeCipherSpec and Finished behind it arrived: the record layer moved its 
read epoch on at the ChangeCipherSpec and then discarded every retransmission 
of the lost message as belonging to the old epoch, whose records are only 
accepted once the handshake has completed. Each side then waited on the other 
until a handshake timeout, if one was configured, ended it. Every 
client-authenticated handshake, and every handshake in which the server issues 
a NewSessionTicket, was exposed. Until the handshake completes, handshake 
records from the current epoch are now still accepted after the read epoch has 
moved on, and each message is checked against the epoch of the record that 
carried it. The DTLS loopback tests now run their handshakes at 10% datagram 
loss in each direction, with a client-authenticated handshake at 25%.</p>
   </li>
   <li>
   <p>The lightweight SubjectPublicKeyInfoFactory and PrivateKeyInfoFactory 
encoded a GOST R 34.10-2012 key on one of the legacy CryptoPro curves under 
id-GostR3410-2001, although RFC 9215 sec. 4.2 permits those curves for 2012 
keys. The digestParamSet now decides: a GOST R 34.11-94 parameter set means 
2001 (RFC 4491 sec. 2.3.2), a GOST R 34.11-2012 digest or none means 2012 with 
256/512 taken from the curve field size, and any other value is rejected. 
GOST3410PublicKeyAlgParameters treats digestParamSet as OPTIONAL on both read 
and write per RFC 9215, and PrivateKeyInfoFactory now passes attributes through 
for ECGOST3410 keys (bc-csharp github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/707";>#707</a>).</p>
   </li>
   <li>
   <p>The name-constraint host canonicalisation removed a single RFC 1034 
root-label dot, the only empty label a name may legally carry, but nothing 
refused the ones that are not legal: a dNSName, rfc822Name host or 
uniformResourceIdentifier host such as &quot;example.com..&quot; kept a phantom 
empty label after the strip and so matched no constraint at all, escaping an 
excluded subtree naming the host it appears to carry. A tested name whose host 
carries an empty label - a second trailing dot, a doubled dot or a leading dot 
- is now refused outright wherever a constraint of that type is in force, 
rather than canonicalised into a name it is not: removing the extra dots would 
decide on the caller's behalf that &quot;example.com..&quot; names example.com, 
which is not how a consumer resolving or comparing the name reads it, and 
refusing fails closed in both directions where canonicalising would newly admit 
such a name under a permitted subtree. The single trailing dot is canonicalised 
 as before, a bare &quot;.&quot; remains the root label rather than an empty 
one, and the guard is scoped to the host, so the doubled dot a quoted local 
part may legally carry is unaffected. Constraints are untouched - one may still 
begin with a dot, which is how this implementation spells &quot;subdomains 
only&quot; (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2436";>#2436</a>).</p>
   </li>
   <li>
   <p>SSLContext.createSSLEngine() from the BCJSSE provider in the 1.86 bctls 
jar failed with NoSuchMethodError on every JDK from 9 up, leaving engine-based 
users of the provider (Netty, Vert.x and the like) unable to open a connection. 
The jdk1.5 and jdk1.9 copies of the package-private SSLEngineUtil had declared 
create(ContextData) with different return types since 2019 - SSLEngine and 
ProvSSLEngine - and the root ProvSSLContextSpi, compiled against the first, is 
paired at runtime with the versions/9 copy on any modern JDK. Until 1.86 the 
java9 compile had hidden this by implicitly recompiling the whole base tree 
into META-INF/versions/9 (447 classes, ProvSSLContextSpi among them); the 
-implicit:none added in 1.86 to stop that duplication exposed the mismatch. The 
jdk1.9 copy now declares the same return type as the root one, a JDK 25 test 
creates engines against the built jar, and a new multiReleaseCheck Gradle task 
on every distributed jar reads the constant pool of each class in
  the jar and in the sibling BC jars it depends on and fails the build when a 
member reference does not resolve against the copy of its target that a JDK 
would pair it with, so the class of defect cannot ship again; the same check 
runs on arbitrary jars, a published release included, as multiReleaseCheckJar 
(github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2448";>#2448</a>).</p>
   </li>
   <li>
   <p>The BCFKS key store derived its scrypt keys with the block size r in 
place of the parallelization parameter p, while writing the p the caller 
configured out to the store: BcFKSKeyStoreSpi passed getBlockSize() to 
SCrypt.generate for both arguments and never read the encoded parallelization 
parameter at all, so every store whose ScryptConfig gave a p other than its r 
encoded parameters that do not derive its own keys. The store was 
self-consistent - BC read back what BC wrote - but a conformant RFC 7914 reader 
computed a different key and so failed the integrity check and the store 
decryption, and BC could not open such a store written by anyone else. 
Derivation now follows RFC 7914. A store written by 1.86 or earlier is still 
read: the integrity check is retried under the old convention, and where a 
signature check leaves no MAC to settle it the store decryption is retried 
instead, in both cases reporting the failure of the encoded parameters rather 
than of the fallback. The wr
 ite side is governed by org.bouncycastle.bcfks.scrypt_p_eq_r, default true, 
which writes p equal to r whatever the ScryptConfig asked for: the two 
conventions then agree, so a store written here is both RFC 7914 correct and 
readable by 1.86 and earlier. Clearing the property honours the configured p, 
which those releases cannot read unless p already equals r; the default is 
intended to become false in a later release, once enough of the installed base 
is writing parameters that describe themselves. Loading with a 
BCFKSLoadStoreParameter carrying a ScryptConfig accepts an encoded p equal to 
either the configured p or the block size, so a store round trips under the 
configuration that wrote it whichever way the property was set; every other 
parameter is compared as before. The parallelization parameter is now bounded 
alongside the cost parameter before the derivation, as the PKCS#8 and PKCS#12 
scrypt paths already bound it.</p>
   </li>
   <li>
   <p>Building an evidence record was cubic in the number of data objects: 
SortedHashList and SortedIndexedHashList held their hashes in a LinkedList and 
found each insertion point by walking it with get(index), so a single add() was 
quadratic in the position it inserted at and building a list of n hashes cubic, 
and both lists sit on the generation path - the reduced hash tree of 
ERSArchiveTimeStampGenerator, the Merkle tree of 
BinaryTreeRootCalculator.computeRootHash(), and the hash list of every 
ERSDataGroup. Each now collects its hashes and sorts them once, in toList(); 
the sort is stable and the old insertion placed a hash after the last one 
comparing equal to it, which is where a stable sort puts it, so the order of 
the leaves and every root hash are unchanged. getFirst() answers with a scan 
rather than a sort and toList() sorts a copy, so neither accessor disturbs what 
has been added. Generating a time-stamp request over 8,000 data objects goes 
from about 210 seconds to under a
  tenth of a second, and 100,000 objects, which the old code could not reach in 
any practical time, takes about 0.2 seconds (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2456";>#2456</a>).</p>
   </li>
   <li>
   <p>An ERSDataGroup recomputed its hash on every request rather than taking 
it from the cache ERSCachingData exists to provide: the group overrode 
getHash(), which left the calculateHash() the cache calls unreachable - and 
wrong, as it copied the member hashes with a loop bounded by the size of the 
empty list it was copying into, so it would have digested nothing had anything 
reached it. The computation is back in calculateHash() and the override is 
gone, so a group's hash is computed once per digest algorithm and 
previous-chain hash, as every other ERSData's is. The value itself is 
unchanged.</p>
   </li>
   <li>
   <p>ERSArchiveTimeStampGenerator rebuilt its reduced hash tree from the data 
objects on every call, so the usual generateTimeStampRequest() followed by 
generateArchiveTimeStamp() or generateArchiveTimeStamps() built it twice. The 
leaves are now built once and dropped when data or a previous chain is added. A 
Set of the data groups it had been given, which nothing ever read, has gone 
with it.</p>
   </li>
   <li>
   <p>Grain-128AEAD returned corrupted plaintext from a decryption driven in 
chunks. The stream cipher data operator splits a processBytes() call that spans 
the buffered authentication tag into two output segments, the bytes released 
from the tag buffer and then the bytes taken straight from the caller's input, 
and wrote the second segment at the caller's output offset instead of after the 
first, so the second segment overwrote the head of the first and the tail of 
the reported output was never written at all. The call still returned the full 
byte count, and because the engine's state update depends on the input and the 
keystream rather than on where the output lands, the tag still verified: the 
wrong plaintext came back with no error raised. Any chunk after the first that 
carried more than the 8 byte tag length was affected. Grain-128AEAD is the only 
engine that uses this operator, and one shot decryption, the encryption path 
and every other AEAD engine were unaffected. The second s
 egment is now written at the advanced offset, matching the equivalent step of 
the general decryption path (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2447";>#2447</a>).</p>
   </li>
   <li>
   <p>The AEAD stream cipher data operator, which Grain-128AEAD alone uses, 
wrote its output without first checking that the caller's buffer was long 
enough, so a short output buffer surfaced as an ArrayIndexOutOfBoundsException 
from inside the engine rather than as the OutputLengthException the general 
path reports for every other AEAD engine. Both directions of processBytes(), 
and processByte(), now check before anything is written or buffered, and only 
when the call releases output, as the general path does.</p>
   </li>
   <li>
   <p>The RFC 9709 content-encryption AlgorithmIdentifier, which carries the 
real algorithm inside the parameters of an outer id-alg-cek-hkdf-sha256, was 
unwrapped at only one of the points where a CMS recipient makes a decision 
about it. Key-size validation was corrected for plain key transport in 1.86, 
but the same call in the KEK, RSA-KTS and KEM recipients, and in the 
key-transport recipient's own ORI-KEM branch, still compared the recovered key 
against the outer identifier, which registers no key size, so 
setKeySizeValidation(true) silently checked nothing there; the 
setAllowedContentAlgorithms allow-list and the setMinimumTagSize floor were 
applied to the outer identifier on every recipient family, including the one 
already corrected, so neither constrained an RFC 9709 message. The unwrap now 
happens once for the key-size check and once for the two policy checks, and 
every recipient polices and validates the content-encryption algorithm the 
message actually carries. A recipient
  with no allow-list, no tag floor and no key-size validation configured 
behaves exactly as before; a caller who listed id-alg-cek-hkdf-sha256 in an 
allow-list in order to admit RFC 9709 messages must now list the 
content-encryption algorithms themselves (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2446";>#2446</a>).</p>
   </li>
   <li>
   <p>A CMS message whose EncryptedContentInfo named the RFC 9709 key 
derivation but carried no readable content-encryption AlgorithmIdentifier in 
its parameters was reported as a NullPointerException, or as an 
IllegalArgumentException from the ASN.1 decoder, out of methods declared to 
throw CMSException, RecipientInformation.getContent() among them. The four 
places that resolve the wrapper - the CEK derivation, the content cipher 
selection, the key-size check, and the recipient's allowed-algorithm and 
tag-size checks - now share one resolver, which reports an absent or unreadable 
inner algorithm as a CMSException.</p>
   </li>
   <li>
   <p>The two YubiKey OpenPGP smart-card decryptor factories zeroized the user 
PIN array the KeyPassphraseProvider handed them rather than a copy of it, in a 
finally block after each private-key operation. Both providers BC ships return 
the application's own array by reference - DefaultKeyPassphraseProvider hands 
back the char[] it has cached for the key, and the provider inside 
OpenPGPApi.editKey returns its argument - so the first card operation destroyed 
the caller's PIN and the next private-key operation presented an all-zero PIN 
to the card, which the card refuses at the cost of a PIN retry. The PIN is now 
fetched as a clone the card operation owns - in 
OpenPGPSmartCard.requireUserPin, where the smart-card restructuring of this 
release put the fetch the two factories used to make - and 
KeyPassphraseProvider.getKeyPassword records that the array it returns stays 
owned by the provider (github PR <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2444";>#2444</a>).</p>
   </li>
   <li>
   <p>The CRMF PKIPublicationInfo structure accepted, and could be built with, 
publication information RFC 4211 sec. 6.3 forbids: pubInfos MUST NOT be present 
if the action is dontPublish, and the field is SEQUENCE SIZE (1..MAX), so a 
present one is never empty. Both contradictions are now rejected with an 
IllegalArgumentException, on parsing and on construction from an array of 
SinglePubInfo. An absent pubInfos with the pleasePublish action, which is how 
the RFC spells &quot;don't care&quot;, is unaffected, and no constructor in the 
library could produce either rejected form.</p>
   </li>
   <li>
   <p>The CMS RFC 8418 key agreement schemes 
(dhSinglePass-stdDH-hkdf-sha256/384/512, used with X25519 and X448) derived the 
key-encryption key with the user keying material in the entityUInfo of the 
ECC-CMS-SharedInfo but never as the HKDF salt, where RFC 8418 sec. 2.2 requires 
both - its recipe is salt = ukm, PRK = HKDF-Extract(salt, K), KEK = 
HKDF-Expand(PRK, DER(ECC-CMS-SharedInfo), SizeInOctets(KEK)). A message 
carrying a ukm therefore did not interoperate with a conforming implementation 
in either direction. The ukm is now passed as the salt as well, on both the 
generating and the receiving side, for those three schemes. A message with a 
ukm written by 1.86, the only release with RFC 8418 support, is not readable by 
this release and vice versa; messages without a ukm, and the X9.63-KDF key 
agreement schemes, are unaffected. The round-trip test now covers both the ukm 
and no-ukm cases for all six curve and scheme combinations, and checks the 
key-encryption key against the RFC's 
 own recipe rather than only against BC itself (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2454";>#2454</a>).</p>
   </li>
   <li>
   <p>A JKS store shorter than the SHA-1 checksum it ends with threw an 
unchecked ArrayIndexOutOfBoundsException out of KeyStore.load, which declares 
IOException for a store it cannot read: JKSKeyStoreSpi.validateStream 
subtracted the digest size from the raw store length without checking it, so 
the digest update clamped its negative length to zero and the System.arraycopy 
that lifted the stored checksum out failed on a negative source index. The 
length is now checked against the checksum plus the 12-byte header before the 
checksum position is used, and a store too short to carry either is reported as 
an EOFException. The JKS store is reached through the compatibility probe in 
AdaptingKeyStoreSpi, so any key store type that probes for it was exposed, and 
the legacy jdk1.1 and jdk1.4 provider copies carry the same fix (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2451";>#2451</a>).</p>
   </li>
   <li>
   <p>A custom Argon2BytesGenerator.BlockPool was left to zeroise the blocks it 
recycled itself, and had no way to know how many blocks to hold: the generator 
returned each block to the pool with the password-derived data still in it, so 
only the FixedBlockPool BC ships cleared them, and sizing any other pool meant 
replicating the internal memory alignment and the block count of the fill step. 
The generator now clears every block before it goes back, so a pool neither has 
to clear nor can observe that data, and 
Argon2BytesGenerator.getBlockCount(memory, lanes) gives the number of blocks a 
run takes - which the default pool now uses, so it no longer discards and 
reallocates the four blocks of the fill step on every call. FixedBlockPool 
drops the two clears it no longer needs, leaving one zeroisation per block per 
use rather than two, and a generateBytes() that fails part way through now 
returns and clears the blocks it took, along with its own working buffer, 
rather than leaving both 
 to the garbage collector (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2452";>#2452</a>).</p>
   </li>
   <li>
   <p>The PKCS#12 key stores wrote the MAC key-derivation parameters of a file 
they had loaded into every file they wrote afterwards, under whatever password 
the caller stored with. For PKCS12-PBMAC1 that carried the loaded file's PBKDF2 
salt, iteration count, key length and PRF, because the parameters were minted 
only when the store held none and were then assigned back, so the branch ran 
once per store object rather than once per write - which also meant one store 
reused a single PBKDF2 salt across every write, including writes under 
different passwords, with no file loaded at all. The classic store inherited 
the MAC salt length and digest algorithm the same way, so a file declaring a 
zero-length MAC salt was re-stored with one, and a file it had loaded under RFC 
9579 handed on that file's PBKDF2 salt too, both stores reading PBMAC1. The 
values were also latched before the MAC was verified and were not cleared by 
the load(null, null) a caller must issue to recover, so a file that f
 ailed the check left them behind for the caller's own file. The PBKDF2 salt 
and the MAC salt are now generated for every write, the MAC salt at no fewer 
than 8 octets, nothing is latched until the file has verified, and an 
AlgorithmIdentifier supplied through a PKCS12StoreParameter is still written as 
it was given. A loaded file's PRF, key length, digest algorithm and MacData 
iteration count are still kept, the last as before; the PBKDF2 count is kept 
where it is at least the count being written with and raised to it otherwise, 
since the file being re-stored is not the one that count was chosen for - RFC 
9579's own test vectors ask for 2048. That count is now 
org.bouncycastle.pkcs12.pbkdf2_it_count, default 65,536, the write-side 
counterpart for a PBMAC1 MAC of what org.bouncycastle.pkcs12.store_it_count is 
for the PBE. Reading is unaffected: a file's MAC is verified with the 
parameters it carries, whatever they are (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2
 450">#2450</a>).</p>
   </li>
   <li>
   <p>Cipher.SM9 took its data-encapsulation mode for decryption from the 
ciphertext rather than from the mode the Cipher was configured with. The GM/T 
0080-2020 SM9Cipher structure names the mode in an enType field, but GM/T 
0044.4 defines the authenticator as C3 = MAC(K2, C2), over the encapsulated 
message alone, so enType is not covered by it: re-encoding a ciphertext with 
the other enType leaves C1, C3 and C2 untouched and steers the recipient into 
the other mode. GM/T 0044.4 takes K1 and K2 from a single KDF output of klen = 
mlen + K2_len bits in stream mode and K1_len + K2_len bits in SM4 mode, where 
K1_len = 128, so when C2 is 16 bytes long the two modes make the identical KDF 
call and derive the same K1 and K2: a one-block SM4 ciphertext relabelled as 
stream mode passes the MAC check and the recipient returns K1 xor C2 - from 
which both the SM4 key K1 and the padded plaintext block follow, wherever that 
output is observable. CipherSpi now decrypts in the configured mode and r
 ejects a ciphertext whose enType disagrees with it, so the mode is symmetric 
between encryption and decryption. That check compares two values a relabelling 
attacker can make agree, and so does not by itself protect a recipient whose 
Cipher is configured for stream mode - the relabelled one-block ciphertext then 
matches the configuration - so SM9Engine additionally refuses a 16-byte C2, the 
one C2 length at which the two modes collide, in both modes and both 
directions: on decryption, and on encryption a 16-byte message in stream mode 
and a message of fewer than 16 bytes, which pads to one block, in SM4 mode. 
Refusing the length on decryption protects the recipient that does so, but the 
message a relabelled ciphertext gives away is the SM4-mode sender's, who cannot 
tell whether the recipient's implementation refuses it, which is why the SM4 
mode no longer produces one; messages of every other length are unchanged in 
both modes. A stream-mode ciphertext must accordingly be decrypted 
 through a stream-mode Cipher (&quot;SM9/XOR/NoPadding&quot;) rather than the 
SM4-mode default that Cipher.getInstance(&quot;SM9&quot;) gives; a message of 
fewer than 16 bytes has to be sent in stream mode, and one of exactly 16 bytes 
- a 128-bit key, say - in SM4 mode; and a ciphertext made by an earlier version 
whose C2 is 16 bytes long is no longer decrypted, whichever mode wrote it - a 
one-block SM4-mode ciphertext, or a stream-mode one carrying a 16-byte message. 
The SM9 KEM is unaffected.</p>
   </li>
   <li>
   <p>Decrypting an OpenPGP message in two steps - recovering the session key 
from a SKESK packet and then decrypting the SEIPD v1 body through 
PGPEncryptedDataList.extractSessionKeyEncryptedData() - stopped detecting a 
wrong passphrase. 1.86 suppressed the legacy CFB &quot;quick check&quot; on the 
two repeated prefix bytes for every session-key decryption, to close the 
Mister-Zuccherato oracle on the path a PKESK session key reaches, but the same 
class also carries password-derived session keys, where reporting the check is 
what identifies a wrong passphrase and lets the next passphrase or SKESK packet 
be tried. A SKESK v4 packet deriving the session key from the S2K output 
directly (no encrypted session key) yields a well formed session key for any 
passphrase, so a wrong one no longer failed at all: it surfaced as a parse or 
integrity failure further down the stream. BouncyCastle's own high-level API 
decrypts this way, so OpenPGPMessageProcessor took the first wrong passphrase 
offe
 red for a success and never tried the remaining ones. A new 
PGPEncryptedDataList.extractSessionKeyEncryptedData(boolean) states whether the 
session key came from a password: true restores the check and with it the 
PGPDataValidationException on a wrong passphrase, the existing no-argument 
method goes on suppressing it, and the high-level API passes true on its 
passphrase paths alone, so a session key recovered from a public key operation 
is still never quick checked (github <a 
href="https://redirect.github.com/bcgit/bc-java/issues/2459";>#2459</a>).</p>
   </li>
   <li>
   <p>Both copies of PKIXCertPathReviewer (org.bouncycastle.pkix.jcajce and the 
legacy org.bouncycastle.x509) took the first date-valid CRL issued by the 
certificate's issuer as an answer about that certificate, applying neither of 
the RFC 5280 sec. 6.3.3 rules that decide whether a CRL covers it: the 
(b)(2)(i) match between a name in the CRL's issuing distribution point and a 
name in the certificate's distribution point, and the (d) intersection of the 
revocation reasons the two assert. Only the (b)(2)(ii) to (iv) onlyContains 
booleans were applied. A CA-signed, in-date CRL with no entries, scoped to 
another distribution point or to a partition of the revocation reasons, was 
therefore reported as proof of non-revocation - isValidCertPath() true with an 
empty error list for a certificate its own CA had revoked for key compromise, 
where CertPathValidator(&quot;PKIX&quot;) rejects the same chain against the 
same trust anchor - and it suppressed the distribution point fetch that would o
 therwise have retrieved the authoritative CRL. Where the reviewer makes the 
trust decision rather than serving as diagnostics beside a real validation this 
is a revocation bypass, and SignedMailValidator (bcmail) reaches it with the 
CRLs carried inside the signed message. Both copies now apply the (b)(2)(i) 
name match and require a CRL to cover every revocation reason before it can 
settle the certificate's status, through the public PKIXCRLValidator helpers 
the validation engine already uses, and keep looking when a candidate does not 
qualify - falling back, as before, to the distribution point fetch and then to 
the existing &quot;no valid CRL found&quot; error. A CRL carrying no issuing 
distribution point, one naming the certificate's own distribution point, and 
one naming the certificate issuer (the distribution point the engine falls back 
to) are all still accepted.</p>
   </li>
   <li>
   <p>RFC3280CertPathUtilities.checkCRL threw java.lang.NullPointerException 
rather than a CertPathValidatorException when every candidate CRL for a 
distribution point was skipped instead of rejected, which is what happens when 
the reasons a CRL covers add nothing to those already checked - the RFC 5280 
sec. 6.3.3 (d) case - since the exception it rethrows is only ever recorded in 
a catch block. Validation failed closed either way, but outside the declared 
contract of CertPathValidator.validate(); a run with nothing recorded now 
reports &quot;No valid CRL found.&quot;. All four copies are corrected: pkix, 
prov, and the prov jdk1.3 and jdk1.4 overlays.</p>
   </li>
   <li>
   <p>TupleHash prefixed an element of 2^28 bytes or more with a length 
computed in int arithmetic: org.bouncycastle.crypto.digests.XofUtils built the 
encode_string prefix of NIST SP 800-185 sec. 2.3.3 as left_encode(len * 8) with 
len an int, so the bit length wrapped before it reached the long parameter it 
was passed to. A single 256 MiB update wrapped it negative, and left_encode 
sizes its output by shifting its argument right eight bits at a time, which 
never reaches zero from a negative value, so the call did not return; at 512 
MiB the length wrapped to zero and the element carried the prefix of an empty 
one, letting two different tuples absorb the same byte string - the ambiguity 
the tuple encoding of sec. 5.3 exists to prevent. The multiply is now widened, 
as the other left_encode and right_encode call sites in CSHAKEDigest, KMAC, 
TupleHash and ParallelHash already were, and left_encode and right_encode 
refuse a negative length rather than spinning on one, so a negative output 
 length handed to the three-argument doFinal of TupleHash, ParallelHash or KMAC 
reports Ill...
   
   _Description has been truncated_


-- 
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]

Reply via email to