Skip to content

guard minimum length in sm2 decrypt and gost/dstu/desede/rc2 unwrap - #2359

Open
rootvector2 wants to merge 4084 commits into
bcgit:mainfrom
rootvector2:wrap-engine-min-length-guard
Open

guard minimum length in sm2 decrypt and gost/dstu/desede/rc2 unwrap#2359
rootvector2 wants to merge 4084 commits into
bcgit:mainfrom
rootvector2:wrap-engine-min-length-guard

Conversation

@rootvector2

Copy link
Copy Markdown
Contributor

SM2Engine.decrypt and the GOST28147/DSTU7624/DESede/RC2 key-wrap unwrap engines sized their output as new byte[inLen - overhead] without first checking inLen covers that overhead, so a short attacker-supplied ciphertext threw NegativeArraySizeException/ArrayIndexOutOfBoundsException instead of the declared InvalidCipherTextException; found auditing these against the already-guarded IESEngine/RFC3394WrapEngine/RFC5649WrapEngine and adds the same minimum-length check.

gefeili and others added 30 commits June 24, 2026 13:16
The earlier ArmoredOutputStream CR/LF fix hardened only the Builder; the
deprecated setHeader/addHeader, the ArmoredOutputStream(out, Hashtable)
constructor and the core PemWriter still emitted a header name/value
verbatim, so a CR or LF could inject an extra header line or terminate the
header block early with the remainder parsed as base64 body (header
injection). Guard the single sink every path funnels through
(ArmoredOutputStream.writeHeaderEntry, PemWriter.writeObject), throwing
IllegalArgumentException; the Builder's pre-split values are unaffected.
…ost label

ProvX509TrustManager.checkEndpointID dispatched the HTTPS algorithm to
HostnameUtil.checkHostname with all-labels wildcard matching, so a dNSName SAN
such as "foo.*.com" matched "foo.evil.com" and "*.*.com" matched any two-label
.com host -- more permissive than SunJSSE (HostnameChecker.matchLeftmostWildcard
for TYPE_TLS) and contrary to RFC 6125 sec. 6.4.3 / RFC 9525 sec. 6.3, which
require the wildcard to be the complete left-most label. The HTTPS path now uses
left-most-label-only matching, like the already-correct LDAP/LDAPS path.
- implement encodeTo directly
BCJSSE: restrict HTTPS endpoint-identification wildcards to the left-most label (hostname-verification hardening)

See merge request root/bc-java!230
Cross-validated against the BCFIPS SecureHash.SM3_HMAC implementation; full-length
32-byte output guards against MAC truncation regressions.
…ms, refusing others with CMSAlgorithmNotAllowedException
…multiply-add

NTRUPlusEngine.finalizeWithAddition (the h*r + m step of encapsulation) applied the
Montgomery factor R as the unreduced literal 2^16 instead of its representative R mod q
(-147, the reference's NTRUPLUS_R). The two are congruent modulo q, so output stayed
correct mod q and the KAT vectors were unaffected, but 2^16 is ~445x larger, so for a
message coefficient near +/-(q-1)/2 the term c*2^16 pushed montgomery_reduce's input past
its valid range (+/- q*2^15). montgomery_reduce then returned a value outside (-Q, Q),
which poly_tobytes (single +Q fold) packed incorrectly, corrupting that ciphertext
coefficient and making ~0.2% of encapsulations decapsulate to the wrong shared secret
("unable to extract KTS secret: checksum failed" on key unwrap). Apply R mod q, matching
the NTRU+ reference. NTRU+ KAT output is byte-for-byte unchanged. Adds testRandomRoundTrips.
…tectedPart, AttributeTypeAndValue, POPOSigningKeyInput, IssuerAndSerialNumber, OriginatorPublicKey) with a lower-bound size check
…2-byte ML-DSA seed with IOException, not an IllegalArgumentException from Arrays.copyOfRange
…nt with a clean IOException in SExpression.parseExpression
…ay against the bytes remaining before allocating, matching the readOpaque/readAll guard
dghgit and others added 23 commits July 11, 2026 18:18
…AnchorID<1..2^8-1> length bound in TrustAnchorIDs: logId/landmarkId/landmarkGroupId now validate the caId input and reject a built ID outside 1..255 bytes, and fromDottedDecimal rejects an over-255-byte result, rather than relying on the downstream MTCSignature / MTCCosignedMessage opaque<1..255> checks to catch an out-of-range trust anchor ID only at serialization time.
…ureSpi: engineSign generated a 32-byte r that was never used (processPreHashedMessage was always called with null, matching draft-ietf-lamps-pq-composite-sigs sec. 2.2 which has no randomizer in M' = Prefix || Label || len(ctx) || ctx || PH(M)); dropped the unused r parameter, the SecureRandom field and its now-unused imports, and stale commented-out r-handling. Behaviour is unchanged - the message representative was already built without r.
… version=0 rule in the CMS KEMRecipientInfo / CMSORIforKEMOtherInfo ASN.1 types, which previously only checked the 65535 upper bound and accepted a non-zero KEMRecipientInfo version on parse; adds KEMRecipientInfoTest cases for a zero kekLength (constructor and parse paths) and a non-zero version.
…en-style -sources.jar/-javadoc.jar files for the jdk1.4 and jdk15to18 Ant distributions from the src.zip and javadoc artifacts the Ant build leaves behind (bcprov-ext reuses bcprov, bctest is sources-only), with a JDKDIR-parameterised script.
…lease.debug is true: jdk14.xml now loads bc-build[.user].properties so it can see release.debug and sets target.prefix to debug-jdk14 (else jdk14) via a condition, so target.name and every <module>-${target.name} artifact picks up the debug label; default (release.debug=false) output is unchanged.
…when release.debug is true, mirroring the jdk14.xml change: jdk15+.xml now loads bc-build[.user].properties so it can see release.debug and sets target.prefix to debug-jdk15to18 (else jdk15to18) via a condition; default (release.debug=false) output is unchanged.
…ars left at the top level (e.g. bctest-*.jar) cannot be accidentally staged and committed.
…t-build step reaches debug jars: the fixed 'bcprov-jdk15to18*.jar' / 'bcprov-ext-jdk15to18*.jar' globs did not match a release.debug build's 'bcprov-debug-jdk15to18-*.jar' names, so debug provider jars shipped without the JCA auto-registration file. Replaced the tar/mv/rm dance with a loop over 'bcprov*jdk15to18*.jar' (matches prov and ext, debug and non-debug) using jar uf -C prov/src/main/resources META-INF.
… @Override/@FunctionalInterface source strip that jdk15+.xml lacked (ant/jdk14.xml preprocesses it away), exclude the tests using JDK 7/8-only APIs (PKIXRevocationChecker, Cipher.updateAAD, java.nio.file.Files, JSSE SNI/ExtendedSSLSession, PipedInputStream(,int), cert.plants generics) with matching jdk1.5 tsp AllTests / jce RegressionTest overlays, and route three guarded Java 8 test calls (CertTest verify(pub,Provider), operator GCMParameterSpec, BouncyCastleProviderTest String.join) through 1.5-safe code
… the public X509Certificate type, not cert.getClass(): the BC cert impl (X509CertificateImpl/X509CertificateObject) is package-private, so a Method obtained from the runtime class failed invoke() with IllegalAccessException and broke cert.test.AllTests testSimpleTests after the jdk15to18 reflective-call change.
… a re-initialised cipher does not carry the previous operation's associated data into the authentication tag, incorporating github PR bcgit#2349 (converting its tab indentation to spaces) and adding a DSTU7624Test regression case that a re-init matches a freshly constructed cipher; relates to github bcgit#2349.
… (or -debug-jdk15to18- on a Java <= 8 daemon) and compile with debug symbols when release.debug is true, mirroring the jdk14.xml / jdk15+.xml debug-label change: build.gradle reads release.debug (via -Prelease.debug or gradle.properties, default false) and prefixes the shared vmrange, so every module's jar archiveBaseName/appendix and Maven artifactId pick up the debug label, and options.debug follows the flag; default (release.debug=false) output is unchanged.
…Cipher (DSTU 7624 CCM), incorporating github PR bcgit#2350: init zero-extends a short nonce to the block size and processAssociatedText zero-pads a trailing partial AAD block instead of throwing "padding not supported". Corrects the PR's AAD MAC loop, whose bound byteIndex < authLen - assocOff double-subtracted the offset (authLen already equals aadLen - assocOff) and skipped every AAD block after the first, silently changing the MAC for multi-block associated data; the bound is byteIndex < authLen. Adds DSTU7624Test cases locking the multi-block AAD MAC to its pre-PR value and round-tripping partial AAD and short nonces; relates to github bcgit#2350.
…Debug builds subsection under Building explaining that -Prelease.debug=true (or release.debug=true in gradle.properties) labels every module's jars/artifacts <module>-debug-jdk18on-<version> and compiles with debug symbols, with clean build / copyJars examples.
run_mtt.sh extracted the release version with `fgrep version`, an
unanchored substring match. The debug-build comment added to
gradle.properties in 10056f5 contains "version" (in <version>), so
fgrep matched it too and $version became a two-line string, garbling
every -scan jar path and failing the CI module-dependency step with a
spurious "File not found: .../bcjmail-jdk18on-1.85\n# A debug build...".
Match only the line starting with version= instead.
…overlay resync (partial: the provider does not yet build clean)

Reviving the long-rotted jdk1.3 legacy build. A genuine 32-bit Sun JDK 1.3.1 no
longer runs on a modern host, so the build-jdk13-docker skill runs it inside a
debian/eol:etch (i386, glibc 2.3.6) container: glibc < 2.4 avoids the PTR_MANGLE
pointer-mangling that segfaults the green-threads Classic VM, forcing -classic
(green threads) avoids the extinct libstdc++-libc6.1-1.so.2 the native launcher
needs, a uname->i686 shim works around the pre-x86-64 launch wrapper, and ORO is
supplied to ant via -lib for <replaceregexp> (no java.util.regex before 1.4).

Build/overlay fixes made so far:
- ant/jdk13.xml: add the @deprecated strip pass (previously only in jdk14.xml, so
  @deprecated survived into 1.3 javac as "illegal character"); exclude the
  algorithm families added since the tree was last synced, matching jdk14.xml
  (hash2curve, crypto/bls + BLS*, Ascon, asymmetric/compositekem, CompositeKEMs,
  jcajce/PKCS12LoadStoreParameter, drbg Entropy{Daemon,Gatherer}).
- initCause() is a Java 1.4 API absent on 1.3: message-only jdk1.3 overlays for
  util/Exceptions, jcajce/provider/util/SecurityExceptions and asn1/DEREncodingException
  (1.3 has no getCause, so the dropped cause is unobservable; message text kept
  verbatim and signatures kept in lockstep with base).
- asn1/StreamUtil overlay: add the checkLength(long,long) overload base callers use.
- jce/src: backport javax.security.auth.Destroyable and DestroyFailedException
  (Java 1.4, absent from the JDK 1.3.1 rt.jar; javax.* is loadable from the class
  path where java.* is not) - fixes ~32 key classes at once.
- Relocate the picnic overlays from pqc/crypto/picnic to pqc/legacy/picnic to match
  the base package move (removes a duplicate-class error).

Known remaining before bcprov-jdk13 builds (real source floor is Java 1.4): logging
overlays for the java.util.logging users (SM2Signer, PKCS12KeyStoreSpi,
PKCS12PBMAC1KeyStoreSpi, BouncyCastleProvider); refresh the stale X509CertificateObject
overlays for the new hasFriendlyName(); the symmetric AES/Camellia/SEED/Serpent/LEA
unresolved symbol; and jdk1.4-style overlays (or excludes) for Falcon, SDitH and SQIsign.
…-SNAPSHOT (release.suffix/version 1.85.99), and the BC and BCPQC provider info strings to v1.86-SNAPSHOT with provider version 1.8599 across the base, jdk1.1 and jdk1.4 copies
…i-release overlays and normalise OpenSSH KeyFactory error handling

The edec KeyFactorySpi jdk1.11/jdk1.15 overlays had drifted from base on the
OpenSSH private-key path: they used the no-passphrase parsePrivateKeyBlob
overload (so passphrase-encrypted openssh-key-v1 Ed25519 keys failed on JDK 11+),
left a raw RuntimeException escape a malformed blob, and jdk1.11 threw
IllegalStateException rather than InvalidKeySpecException. Both overlays now
match base. Across rsa/dsa/ec/edec the OpenSSH wrong-type and decode-failure
paths now consistently throw InvalidKeySpecException and wrap the public-key
parse, and the RSA private path's "public key is not RSA private key" message
was corrected. Adds OpenSSHKeyFactoryMRTest under src/test/jdk1.11 and jdk1.15
(wired into AllTests11/AllTests15) so test11/test15 run the OpenSSH key specs
against the multi-release jar and catch overlay drift.
…erage for overlaid behaviour, in docs/claude/architecture.md
IANAObjectIdentifiers was consolidated to a single home in core (shipped in
bcprov) in 1.85 (github bcgit#2176), but ant/bc+-build.xml's build-util target still
copied org/bouncycastle/asn1/iana/** into bcutil, so the Ant distributions
(bcutil-jdk15to18, bcutil-jdk14) duplicated the class already present in bcprov
and an Android/R8 build depending on both failed with "Duplicate class
org.bouncycastle.asn1.iana.IANAObjectIdentifiers". The iana arc is the only
package in the build-util include list also shipped publicly by core, and no
util class references it, so dropping the include removes the duplicate while
leaving the class in bcprov. The Gradle jdk18on jars were already correct.
Relates to github bcgit#2356.
@ligefeiBouncycastle
ligefeiBouncycastle self-requested a review July 23, 2026 04:25
@ligefeiBouncycastle

Copy link
Copy Markdown
Collaborator

Thank you for the PR, nice catch. A few days ago I had identified the same issue in SM2 and GOST28147, and your PR also covers DSTU7624, DESede and RC2, so this is a good opportunity to combine the fixes. I'll merge them together.

@rootvector2

Copy link
Copy Markdown
Contributor Author

sounds good, makes sense to combine them. thanks for picking it up.

@rootvector2

Copy link
Copy Markdown
Contributor Author

sounds good. ShortWrapCipherTextTest in this PR exercises all five engines, so it should carry over to the combined fix as-is.

hubot pushed a commit that referenced this pull request Jul 26, 2026
…ht engines

Follow-up to the previous commit (PR #2359, Naveed Khan). SM2Engine.decrypt rejected a
short ciphertext with "ciphertext too short"; every other engine and AEAD mode in the tree
uses "data too short" for this condition (CCMBlockCipher, GCMBlockCipher, EAXBlockCipher,
OCBBlockCipher, KCCMBlockCipher, KGCMBlockCipher, ChaCha20Poly1305, AEADBaseEngine, the
Ascon engines), so use that wording and update the ShortWrapCipherTextTest assertion in
lockstep. Also hoists c1Len into a local so the length check and the following
new byte[c1Len] cannot drift apart, and expands the two guard comments to record which
unchecked exception each guard prevents. No functional change beyond the message.
hubot pushed a commit that referenced this pull request Jul 26, 2026
… / DESede / RC2 unwrap minimum-length guards (PR #2359)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants