Skip to content

Commit 253e927

Browse files
Arpan0995dghgit
authored andcommitted
Blake2bpDigest.update() now measures the room left against the 512 byte buffer it has, and the Blake2bp/Blake2sp tests are now run by the core test suite, relates to github #2488.
1 parent 44585f9 commit 253e927

5 files changed

Lines changed: 70 additions & 2 deletions

File tree

‎CONTRIBUTORS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ We also wish to acknowledge financial and collaborative support from [CISCO](htt
542542
- jmeeder \<https://github.com/jmeeder\> - reporting that RFC 4998 evidence-record generation rejected time stamps from an authority naming the digest with NULL parameters where BC names it with them absent, both of which RFC 5754 requires a receiver to accept (issue #2379).
543543
- rimuln \<https://github.com/rimuln\> - diagnosis and fix for PKCS12 getCertificateAlias returning the alias of an unrelated certificate, tracing it to the alias and certificate enumerations of the keystore's certs table diverging in order once keys() enumerated a copy (issue #2384, PR #2385).
544544
- Yu Bao \<yubao&#064;paypal.com\> - reporting an API gap, on behalf of the PayPal Cyber Security Team, that the high-level OpenPGP message API (OpenPGPMessageProcessor / OpenPGPMessageInputStream) gave a caller no way to bound how far a compressed data packet expands, where the low-level PGPCompressedData it wraps has carried a bounded getDataStream(long) overload all along, and that OpenPGPPolicy exposed no equivalent property to set. Suggesting a protocol whitelist for CRL Distribution Point fetching, which is now the org.bouncycastle.x509.CRLDP_protocols property, and suggesting that an OCSP response was read up to the length the responder declared for itself, now capped by org.bouncycastle.ocsp.max_response_size, and suggesting bounds on the OpenPGP ASCII armor headers, now capped by org.bouncycastle.openpgp.max_armor_header_length and org.bouncycastle.openpgp.max_armor_headers.
545-
- Arpan Sharma \<https://github.com/Arpan0995\> - initial audit of BCPQC provider consistency starting with HQC, which led to the exposure of a number of issues in the JCA provider service interfaces for other BCPQC algorithms. In-depth auditing of PQC signature algorithms in the provider leading to the correction of a number of JCA API compliance issues. Initial implementation of the guard that lets a signature context be set on the composite ML-DSA services before initSign / initVerify, the composite counterpart of the base-engine fix for issue #2396 (issue #2412). Audit of the LMS / HSS stateful private key decoder, establishing that the level count, the one-time index and the persisted tree cache were all accepted without validation (issue #2414); that issue was also the inspiration for the further investigation which found the HSS and XMSS^MT decoders accepting a private key whose declared index disagreed with the traversal state stored beside it, allowing a one-time key to be used twice. Audit of the high-level OpenPGP API (org.bouncycastle.openpgp, org.bouncycastle.openpgp.api) and the bcpg packet layer beneath it, spanning certificate evaluation, message verification, decryption and signature subpacket parsing, which found certificates being used without the certifications RFC 9580 requires of them, malformed but signed content reaching ordinary reader code as unchecked exceptions, a truncated message being read as a clean end of message, and unverified plaintext being released ahead of the integrity check (issues #2417, #2424, #2426). Audit of the recipient side of the CMS RFC 9629 KEMRecipientInfo path, which found three sender-controlled fields leaving methods declared to throw CMSException as unchecked exceptions and the kekLength never being confirmed against the wrap algorithm, and which led to the same translation being added to the neighbouring AuthEnvelopedData constructor and the two streaming parsers (issue #2422). Audit of RSA-PSS parameter handling in the provider, which found Signature.setParameter reporting a rejected trailer field as an unchecked exception and leaving the rejected spec half-applied, so that the parameters reported for a signature were not the ones it had been made with (issue #2421). Audit of the PKCS#12 key store entry-setting boundary, which found setKeyEntry reporting a certificate whose public key the provider cannot resolve as an unchecked exception rather than the KeyStoreException it declares, and which led to the same guard being applied to setCertificateEntry and to the RFC 9579 PBMAC1 store, and to the rejected entry no longer being left behind in the store (issue #2419). Further auditing of the verification and expiration paths of the high-level OpenPGP API, which found a configured algorithm policy not being applied to inline message verification, an expired primary key still offering its subkeys, and a data signature being reported valid past its own expiration time. CMS SignedData and PKIX certification path test coverage for the eighteen Composite ML-DSA parameter sets, which had been reachable through the CMS and cert-path signature algorithm finders with no test exercising either (PR #2437). Identification and fix of the output offset in the AEAD stream cipher data operator that made Grain-128AEAD return corrupted plaintext from a decryption driven in chunks, with the streamed decryption coverage that had been missing (PR #2447). Identification and fix of the RFC 9709 content-encryption algorithm being left wrapped at the CMS recipient key-size, allowed-algorithm and tag-size checks, so that none of them applied to the algorithm the content was encrypted under (PR #2446). Identification and fix of the YubiKey OpenPGP smart-card decryptor factories zeroizing the user PIN array belonging to the KeyPassphraseProvider that supplied it, which left the next private-key operation presenting an all-zero PIN to the card (PR #2444). Identification and fix of the TupleHash element length prefix being computed in int arithmetic, which left an element of 2^28 bytes or more either not returning from the length encoding loop or carrying the prefix of an empty element, and of the same loop not returning on a negative output length (PR #2462). Identification and fix of the ITS explicit certificate builder choosing the certificate's signature type from the verification key indicator rather than the signing key's curve, which mislabelled brainpoolP256r1 signatures and implicit-issuer P-256 signatures and left brainpoolP384r1 keys unable to sign a certificate (PR #2463). Making CipherOutputStream.close() idempotent in the light-weight and JCA stream classes (PR #2486). Identification and fix of PGPSignatureSubpacketVector.getPreferredLibrePgpEncryptionModes() reading the RFC 9580 Preferred AEAD Ciphersuites subpacket rather than the LibrePGP Preferred Encryption Modes subpacket, which left it throwing a ClassCastException for the direct-key signature of every key generated by the high-level OpenPGP API (PR #2489).
545+
- Arpan Sharma \<https://github.com/Arpan0995\> - initial audit of BCPQC provider consistency starting with HQC, which led to the exposure of a number of issues in the JCA provider service interfaces for other BCPQC algorithms. In-depth auditing of PQC signature algorithms in the provider leading to the correction of a number of JCA API compliance issues. Initial implementation of the guard that lets a signature context be set on the composite ML-DSA services before initSign / initVerify, the composite counterpart of the base-engine fix for issue #2396 (issue #2412). Audit of the LMS / HSS stateful private key decoder, establishing that the level count, the one-time index and the persisted tree cache were all accepted without validation (issue #2414); that issue was also the inspiration for the further investigation which found the HSS and XMSS^MT decoders accepting a private key whose declared index disagreed with the traversal state stored beside it, allowing a one-time key to be used twice. Audit of the high-level OpenPGP API (org.bouncycastle.openpgp, org.bouncycastle.openpgp.api) and the bcpg packet layer beneath it, spanning certificate evaluation, message verification, decryption and signature subpacket parsing, which found certificates being used without the certifications RFC 9580 requires of them, malformed but signed content reaching ordinary reader code as unchecked exceptions, a truncated message being read as a clean end of message, and unverified plaintext being released ahead of the integrity check (issues #2417, #2424, #2426). Audit of the recipient side of the CMS RFC 9629 KEMRecipientInfo path, which found three sender-controlled fields leaving methods declared to throw CMSException as unchecked exceptions and the kekLength never being confirmed against the wrap algorithm, and which led to the same translation being added to the neighbouring AuthEnvelopedData constructor and the two streaming parsers (issue #2422). Audit of RSA-PSS parameter handling in the provider, which found Signature.setParameter reporting a rejected trailer field as an unchecked exception and leaving the rejected spec half-applied, so that the parameters reported for a signature were not the ones it had been made with (issue #2421). Audit of the PKCS#12 key store entry-setting boundary, which found setKeyEntry reporting a certificate whose public key the provider cannot resolve as an unchecked exception rather than the KeyStoreException it declares, and which led to the same guard being applied to setCertificateEntry and to the RFC 9579 PBMAC1 store, and to the rejected entry no longer being left behind in the store (issue #2419). Further auditing of the verification and expiration paths of the high-level OpenPGP API, which found a configured algorithm policy not being applied to inline message verification, an expired primary key still offering its subkeys, and a data signature being reported valid past its own expiration time. CMS SignedData and PKIX certification path test coverage for the eighteen Composite ML-DSA parameter sets, which had been reachable through the CMS and cert-path signature algorithm finders with no test exercising either (PR #2437). Identification and fix of the output offset in the AEAD stream cipher data operator that made Grain-128AEAD return corrupted plaintext from a decryption driven in chunks, with the streamed decryption coverage that had been missing (PR #2447). Identification and fix of the RFC 9709 content-encryption algorithm being left wrapped at the CMS recipient key-size, allowed-algorithm and tag-size checks, so that none of them applied to the algorithm the content was encrypted under (PR #2446). Identification and fix of the YubiKey OpenPGP smart-card decryptor factories zeroizing the user PIN array belonging to the KeyPassphraseProvider that supplied it, which left the next private-key operation presenting an all-zero PIN to the card (PR #2444). Identification and fix of the TupleHash element length prefix being computed in int arithmetic, which left an element of 2^28 bytes or more either not returning from the length encoding loop or carrying the prefix of an empty element, and of the same loop not returning on a negative output length (PR #2462). Identification and fix of the ITS explicit certificate builder choosing the certificate's signature type from the verification key indicator rather than the signing key's curve, which mislabelled brainpoolP256r1 signatures and implicit-issuer P-256 signatures and left brainpoolP384r1 keys unable to sign a certificate (PR #2463). Making CipherOutputStream.close() idempotent in the light-weight and JCA stream classes (PR #2486). Identification and fix of PGPSignatureSubpacketVector.getPreferredLibrePgpEncryptionModes() reading the RFC 9580 Preferred AEAD Ciphersuites subpacket rather than the LibrePGP Preferred Encryption Modes subpacket, which left it throwing a ClassCastException for the direct-key signature of every key generated by the high-level OpenPGP API (PR #2489). Identification and fix of the BLAKE2bp digest measuring the room left in its 512 byte buffer against eight 128 byte blocks rather than four, which made a message passed in more than one call either fail with an ArrayIndexOutOfBoundsException or hash part of its input out of order, and of the BLAKE2bp test class never being run by the Gradle build (PR #2488).
546546
- Flowdalic \<https://github.com/Flowdalic\> - initial implementation of an AnimalSniffer-based Android API-level compatibility check for the Gradle build (PR #336).
547547
- hannesa2 \<https://github.com/hannesa2\> - initial Dependabot configuration for the Gradle and GitHub Actions ecosystems (PR #883).
548548
- vladhuma \<https://github.com/vladhuma\> - initial implementation of server-side OCSP stapling for the BCJSSE provider, on behalf of Thales Group (PR #1740).

‎core/src/main/java/org/bouncycastle/crypto/digests/Blake2bpDigest.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public void update(byte in)
6363
public void update(byte[] message, int offset, int len)
6464
{
6565
int left = bufferPos;
66-
int remainingLength = 8*BLAKE2B_BLOCKBYTES - left;
66+
int remainingLength = PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES - left;
6767

6868
if(left != 0 && len >= remainingLength)
6969
{

‎core/src/test/java/org/bouncycastle/crypto/test/AllTests.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public static Test suite()
2121
suite.addTestSuite(SimpleTestTest.class);
2222
suite.addTestSuite(GCMReorderTest.class);
2323
suite.addTestSuite(HPKETestVectors.class);
24+
suite.addTestSuite(Blake2bpDigestTest.class);
25+
suite.addTestSuite(Blake2spDigestTest.class);
2426
return new BCTestSetup(suite);
2527
}
2628

‎core/src/test/java/org/bouncycastle/crypto/test/Blake2bpDigestTest.java‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,69 @@ public void testReset()
119119
}
120120
}
121121
}
122+
123+
public void testMultiPartUpdate()
124+
{
125+
// unkeyed BLAKE2bp of the 1100 byte message 00 01 02 .. ff 00 01 ..
126+
byte[] expected = Hex.decode("1fbb59626e91bb75333395159d754453bfe699609d617d0ca94fa5028aaac576f2fa9c6f31d511341256132f65e24ce78097060800465113298fbd069f3c988f");
127+
byte[] key = new byte[64];
128+
byte[] buf = new byte[1100];
129+
byte[] hash = new byte[64];
130+
131+
for (int i = 0; i < key.length; i++)
132+
{
133+
key[i] = (byte) i;
134+
}
135+
for (int i = 0; i < buf.length; i++)
136+
{
137+
buf[i] = (byte) i;
138+
}
139+
140+
Blake2bpDigest digest = new Blake2bpDigest(null);
141+
digest.update(buf, 0, buf.length);
142+
digest.doFinal(hash, 0);
143+
assertTrue("BLAKE2bp mismatch on long message", Arrays.areEqual(expected, hash));
144+
145+
Blake2bpDigest keyedDigest = new Blake2bpDigest(key);
146+
byte[] keyedExpected = new byte[64];
147+
keyedDigest.update(buf, 0, buf.length);
148+
keyedDigest.doFinal(keyedExpected, 0);
149+
150+
for (int step = 1; step <= buf.length; step++)
151+
{
152+
for (int pOffset = 0; pOffset < buf.length; pOffset += step)
153+
{
154+
int len = Math.min(step, buf.length - pOffset);
155+
156+
digest.update(buf, pOffset, len);
157+
keyedDigest.update(buf, pOffset, len);
158+
}
159+
160+
digest.doFinal(hash, 0);
161+
assertTrue("BLAKE2bp mismatch on update step " + step, Arrays.areEqual(expected, hash));
162+
163+
keyedDigest.doFinal(hash, 0);
164+
assertTrue("keyed BLAKE2bp mismatch on update step " + step, Arrays.areEqual(keyedExpected, hash));
165+
}
166+
167+
for (int split = 0; split <= buf.length; split++)
168+
{
169+
digest.update(buf, 0, split);
170+
digest.update(buf, split, buf.length - split);
171+
digest.doFinal(hash, 0);
172+
assertTrue("BLAKE2bp mismatch on update split " + split, Arrays.areEqual(expected, hash));
173+
174+
keyedDigest.update(buf, 0, split);
175+
keyedDigest.update(buf, split, buf.length - split);
176+
keyedDigest.doFinal(hash, 0);
177+
assertTrue("keyed BLAKE2bp mismatch on update split " + split, Arrays.areEqual(keyedExpected, hash));
178+
}
179+
180+
for (int i = 0; i < buf.length; i++)
181+
{
182+
digest.update(buf[i]);
183+
}
184+
digest.doFinal(hash, 0);
185+
assertTrue("BLAKE2bp mismatch on single byte update", Arrays.areEqual(expected, hash));
186+
}
122187
}

‎docs/releasenotes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Date: 2026, TBD
101101
- The Signature implementations for AIMer, FAEST, HAETAE, MAYO, MQOM, QR-UOV, SDitH, SNOVA and UOV in the BCPQC provider discarded the underlying exception when initVerify refused a public key it could not decode, and those implementations, together with Falcon in the BC and BCPQC providers and SPHINCS-256, did the same when sign() failed, keeping only its text in the InvalidKeyException or SignatureException they threw. The SMAUG-T Cipher in the BCPQC provider did the same when wrap() or unwrap() failed, in the IllegalBlockSizeException, InvalidKeyException or NoSuchAlgorithmException it threw, where the other KEM Ciphers already kept the cause. The original exception is now attached as the cause through the SecurityExceptions factories, so callers can walk getCause(); exception types and message texts are unchanged.
102102
- The provider's PKIX CertPathValidator reported a revoked certificate with a CertPathValidatorException whose getReason() was BasicReason.UNSPECIFIED, whether the revocation came from a CRL or from an OCSP response, as the revocation was rewrapped with the constructor that takes no reason. It now reports BasicReason.REVOKED on both paths, with the exception message unchanged. This applies to the Java 8 and later validator, which is the one the provider registers on those JVMs.
103103
- PGPSignatureSubpacketVector.getPreferredLibrePgpEncryptionModes() looked up the RFC 9580 Preferred AEAD Ciphersuites subpacket (type 39) rather than the LibrePGP Preferred Encryption Modes subpacket (type 34) it is documented to return, and cast what it found to the LibrePGP subpacket class. Any signature carrying AEAD ciphersuite preferences, including the direct-key self-signature of a key generated through OpenPGPApi.generateKey() with its default preferences, therefore made it throw a ClassCastException, and a signature carrying only the LibrePGP preferences, such as one built with PGPSignatureSubpacketGenerator.setPreferredLibrePgpEncryptionModes(), gave null. It now reads type 34, also when that subpacket was added as a plain PreferredAlgorithms, and getPreferredAEADCiphersuites() remains the accessor for type 39 (github #2489).
104+
- The light-weight Blake2bpDigest could return a wrong digest, or fail with an ArrayIndexOutOfBoundsException, when a message longer than 512 bytes was passed to it in more than one call, and hashing a byte at a time failed at the 513th byte. The digest buffers one 128 byte block for each of its four leaves, 512 bytes in all, but update() measured the room left in that buffer against eight blocks, so once input was buffered a later call that did not fit in the room left either tried to copy past the end of the buffer or, with no error raised, hashed part of its input ahead of the bytes still waiting there. One-shot hashing and messages of 512 bytes or fewer were unaffected, as was Blake2spDigest, whose buffer holds eight of its 64 byte blocks. The room left is now measured against the four blocks the buffer holds (github #2488).
104105

105106
### 2.1.3 Additional Features and Functionality
106107

0 commit comments

Comments
 (0)