CVE-2026-12803 Overview
CVE-2026-12803 is a cryptographic weakness in the Bouncy Castle for Java library affecting the KCCMBlockCipher implementation of the DSTU 7624 authenticated encryption mode. When associated authenticated data (AAD) is absent, the MAC computation skips the G1 block that binds the nonce, causing the resulting authentication tag to be independent of the nonce. This flaw enables cross-nonce Authenticated Encryption with Associated Data (AEAD) forgery, allowing an attacker to reuse a valid tag across different nonces without detection. The issue impacts Bouncy Castle for Java before 1.85 and Bouncy Castle for Java LTS before 2.73.12. The weakness is classified as [CWE-354] Improper Validation of Integrity Check Value.
Critical Impact
Authentication tag forgery undermines message integrity guarantees for any Java application relying on KCCM mode without AAD, enabling attackers to substitute ciphertexts across nonces while passing MAC verification.
Affected Products
- Bouncy Castle for Java versions before 1.85
- Bouncy Castle for Java LTS versions before 2.73.12
- Java applications using KCCMBlockCipher (DSTU 7624 KCCM mode) without associated data
Discovery Timeline
- 2026-08-03 - CVE-2026-12803 published to the National Vulnerability Database (NVD)
- 2026-08-04 - Last updated in NVD database
Technical Details for CVE-2026-12803
Vulnerability Analysis
The KCCMBlockCipher class implements the KCCM authenticated encryption mode defined by the Ukrainian DSTU 7624 standard. KCCM produces a MAC by processing a sequence of blocks that include a G1 block. The G1 block binds the nonce, the plaintext length, and the MAC-size flag into the authentication tag.
The vulnerable code path treats the G1 block as conditional on the presence of associated authenticated data. When AAD is absent, processAAD() returns before writing G1, and the MAC is computed without any nonce binding. Two ciphertexts encrypted under the same key but different nonces then produce interchangeable authentication tags.
An attacker who observes a valid (nonce, ciphertext, tag) triple can present the same ciphertext and tag under a different nonce. The receiver accepts the forgery because the MAC does not depend on the nonce value. This breaks the integrity property that AEAD modes are required to provide.
Root Cause
The DSTU 7624 specification encodes the associated-data-present indicator as a flag bit inside the G1 block itself. G1 is therefore mandatory for every MAC computation. The Bouncy Castle implementation incorrectly gated the entire G1 construction on assocLen > 0, treating AAD presence as a precondition for nonce binding rather than as a bit within G1.
Attack Vector
Exploitation requires an attacker positioned to observe or intercept KCCM-protected messages that are transmitted without AAD. The attacker replays or substitutes ciphertexts under attacker-chosen nonces. No privileges, user interaction, or local access are required, and the attack proceeds over the network against any application relying on KCCMBlockCipher for integrity.
// Patch from bc-java commit 697794413ebf7bc5e3fce609a707826ba52981af
// core/src/main/java/org/bouncycastle/crypto/modes/KCCMBlockCipher.java
private void processAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
{
- if (assocLen - assocOff < engine.getBlockSize())
- {
- throw new IllegalArgumentException("authText buffer too short");
- }
- if (assocLen % engine.getBlockSize() != 0)
+ boolean hasAssocText = assocLen > 0;
+
+ if (hasAssocText)
{
- throw new IllegalArgumentException("padding not supported");
+ if (assocLen - assocOff < engine.getBlockSize())
+ {
+ throw new IllegalArgumentException("authText buffer too short");
+ }
+ if (assocLen % engine.getBlockSize() != 0)
+ {
+ throw new IllegalArgumentException("padding not supported");
+ }
}
+ // The G1 block binds the nonce, data length and MAC-size flag into the MAC and must be
+ // processed unconditionally. DSTU 7624 carries the associated-data-present indicator as a flag
+ // bit inside G1, so it is not a gate on computing G1: skipping G1 when no AAD is present leaves
+ // the MAC independent of the nonce and enables cross-nonce forgery.
System.arraycopy(nonce, 0, G1, 0, nonce.length - Nb_ - 1);
Pack.intToLittleEndian(dataLen, buffer, 0); // for G1
}
Source: Bouncy Castle patch commit 6977944
Detection Methods for CVE-2026-12803
Indicators of Compromise
- Repeated ciphertexts with identical authentication tags appearing under different nonce values in KCCM-protected traffic
- Successful KCCM message verification for payloads that a receiver did not expect from the corresponding session or nonce sequence
- Application logs from Bouncy Castle consumers showing use of KCCMBlockCipher with empty AAD prior to patching
Detection Strategies
- Perform software composition analysis (SCA) across Java build artifacts and container images to identify bcprov and bcprov-lts versions below 1.85 and 2.73.12
- Instrument application startup to log the Bouncy Castle provider version and flag deployments running vulnerable releases
- Audit source repositories for direct instantiation of org.bouncycastle.crypto.modes.KCCMBlockCipher to determine actual exposure
Monitoring Recommendations
- Track dependency manifests (pom.xml, build.gradle, ivy.xml) in continuous integration for regressions to affected Bouncy Castle versions
- Correlate cryptographic library inventory with runtime process telemetry to identify hosts loading vulnerable JARs
- Alert on anomalous authentication-tag reuse patterns in application-layer logs that record nonce and tag values
How to Mitigate CVE-2026-12803
Immediate Actions Required
- Upgrade Bouncy Castle for Java to version 1.85 or later, or Bouncy Castle for Java LTS to version 2.73.12 or later
- Inventory all Java applications, microservices, and container images that bundle bcprov and rebuild them against a fixed release
- Where KCCM is not required, migrate to a widely reviewed AEAD such as AES-GCM or ChaCha20-Poly1305 until patched libraries are deployed
Patch Information
The fix landed in the Bouncy Castle bc-java repository across two commits. Commit 697794413ebf7bc5e3fce609a707826ba52981af restructures processAAD() so the G1 block is written unconditionally, restoring nonce binding when AAD is empty. Commit 7d79aa76e984da85f2a541cae8ba2ae56e1713bc refactors the routine into processAssociatedText() and retains the unconditional G1 processing. Both fixes are included in Bouncy Castle 1.85 and Bouncy Castle LTS 2.73.12. See the CVE-2026-12803 wiki entry for vendor guidance.
Workarounds
- Ensure every call to KCCMBlockCipher supplies non-empty associated authenticated data until the library can be upgraded
- Wrap KCCM usage in an application-layer construction that binds the nonce into the plaintext or AAD, so tag validity depends on the nonce even in vulnerable versions
- Restrict message acceptance to a strict, monotonically increasing nonce window at the receiver to reduce the practical window for forgery replay
# Verify the deployed Bouncy Castle version in a Java project (Maven)
mvn dependency:tree -Dincludes=org.bouncycastle
# Enforce a fixed version in pom.xml
# <dependency>
# <groupId>org.bouncycastle</groupId>
# <artifactId>bcprov-jdk18on</artifactId>
# <version>1.85</version>
# </dependency>
# Gradle equivalent
# implementation 'org.bouncycastle:bcprov-jdk18on:1.85'
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

