GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — CCM, KCCM, KGCM, EAX, OCB: refuse a second encryption without a new init by AlexLaroche · Pull Request #2562 · bcgit/bc-java

Conversation

@AlexLaroche

@AlexLaroche AlexLaroche commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Summary

After an encryption completes, CCMBlockCipher, KCCMBlockCipher, KGCMBlockCipher, EAXBlockCipher and OCBBlockCipher encrypt a further message on the same instance under the same key and nonce if they are used again without init, either directly or after reset().

  • For CCM, KCCM, KGCM and EAX this reuses the CTR keystream: C1 xor C2 = P1 xor P2. It also undermines the authentication for that nonce.
  • For OCB it is nonce reuse.

The init-time guards ("cannot reuse nonce for ... encryption") don't cover this, because no new init happens. GCMBlockCipher and ChaCha20Poly1305 already refuse it ("... cannot be reused for encryption").

Reproduced on main (a1bb4ad): CCM, KCCM, KGCM and EAX give the same keystream for the second message, and OCB accepts it. bcprov 1.86 behaves the same for CCM.

Change

Each of the five records when an encryption has used the current key and nonce, and only init clears that record. A further encryption throws IllegalStateException("<mode> cipher cannot be reused for encryption"), the same message pattern as GCM.

  • CCM, KCCM and KGCM only produce output in processPacket / doFinal. The nonce counts as used once an encryption gets past its output-length check, so a too-small buffer can still be retried. reset() on its own does not use the nonce.
  • EAX and OCB stream ciphertext from processByte(s). The nonce is used by an encryption doFinal, or by a reset() after message data has been processed. In OCB's doFinal this happens on entry, since it writes the final block before checking room for the tag. A reset() before any message data still allows encryption.
  • Decryption is unaffected.
  • The jdk1.1 copy of EAXBlockCipher gets the same change.

Behaviour changes to note

  • JCE: Cipher.doFinal() resets the cipher for reuse with the same key and IV. For AES/CCM, AES/EAX, AES/OCB and the DSTU 7624 KCCM/KGCM ciphers, a second encryption without init now throws IllegalStateException. That is what GCM already does in BC and SunJCE.
  • KGMac: like GMac over GCM, KGMac now needs a fresh init between messages rather than reset(). Reusing a GMAC nonce for a different message weakens the MAC anyway.
  • OpenPGP, TLS and CMS call init with a fresh nonce per chunk, record or message, so they are not affected.

Tests changed

Several existing tests encrypted twice under one key and nonce. They now expect the refusal and use a fresh init or instance for the repeat:

  • Core helpers:
    • AEADTestUtil.testReset: the encryption half asserts the refusal; the decryption half is unchanged.
    • AEADTestUtil.testBufferSizeChecks: the encryption half uses init with the existing varyNonce helper instead of reset().
  • Core tests:
    • CCMTest: bufferClearingTest and the two processPacket overloads use a fresh instance for the repeat.
    • EAXTest: the repeat runs use fresh encryptors.
    • OCBTest: the "(reused)" run now reuses only the decryptor.
    • DSTU7624Test:
      • doFinalTest now expects the repeat to be refused.
      • KGMacPartialBlockTests re-initialises with a fresh IV, keeping its check that the result doesn't depend on what the instance processed before.
      • KGCMNonceReuseTests and KCCMNonceReuseTests previously asserted that "reset()-based reuse must still work". That is the reuse removed here, so they now assert the refusal. Please check this matches your intent.
  • Provider tests:
    • AEADTest expects the refusal.
    • BlockCipherTest already took a fresh Cipher for GCM before repeating an encryption. It now does the same for EAX, OCB and CCM through a small newEncryptor helper that mirrors the original init, including the RC2/RC5 specs.

Release note added to docs/releasenotes.md (1.87).

Tests

  • New AEADEncryptionReuseTest (SimpleTest, registered in crypto.test.RegressionTest) covers CCM, KCCM, KGCM, EAX and OCB. It checks that:

    • a second encryption is refused, before and after reset(), and through CCM processPacket;
    • init with a new nonce encrypts and decrypts correctly;
    • reset() before any data still allows encryption;
    • reset() after streamed data uses the nonce (EAX, OCB);
    • decryption stays reusable.

    It fails on main and passes with the change.

  • Core tests pass: CCMTest, EAXTest, OCBTest, DSTU7624Test, GCMTest, ChaCha20Poly1305Test, GCMSIVTest.

  • Provider tests pass: AEADTest, BlockCipherTest, OCBTest, DSTU7624Test, AESTest, ARIATest, CipherStreamTest2, AlgorithmParametersTest, ECIESTest. Before the test updates, AEADTest and BlockCipherTest failed only on the EAX reuse described above; all of these pass on main too.

  • I ran these with javac and the test classes' runners (JDK 25), not through Gradle.

The same issue in bc-csharp is fixed in bcgit/bc-csharp#754.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@AlexLaroche
CCM, KCCM, KGCM, EAX and OCB now refuse a second encryption under the…
1dfb9d5
… same key and nonce without a new init, as GCM and ChaCha20-Poly1305 already did, rather than reusing the keystream or nonce.
@dghgit

dghgit commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Please include a statement saying you reviewed the code. See CONTRIBUTING.md for details.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@dghgit dghgit self-assigned this Nov 9, 2026
@AlexLaroche

AlexLaroche commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Contribution statement

Use of generative AI. I used Claude Code (Anthropic's coding agent, model Claude Opus) to help with this pull request. It helped find and reproduce the issue, write the change and its regression test, run the existing tests, and write this description. I chose what to submit and I am responsible for it.

Review. I have reviewed every line of this change, including the tests and the release note, and I understand what it does and why. I checked that the new test fails on main and passes with the change, and that the existing tests listed in the description pass.

Representations under CONTRIBUTING.md. By submitting this pull request I make the representations in the project's contributing guidelines, including the following:

  • I hold the rights needed to release this contribution under the Bouncy Castle License. To the best of my knowledge it will not give rise to third-party intellectual property claims against the Legion of the Bouncy Castle Inc. or its recipients.
  • To the extent any part of it is protected by copyright, I am its author or am otherwise authorised to release it under the Bouncy Castle License.
  • Where any part was generated with AI tools and is not protected by copyright, I do not represent that part as owned intellectual property, and I understand that the Legion of the Bouncy Castle Inc. accepts it on that basis.
  • I have complied with Anthropic's terms of use for Claude Code. Those terms assign to me whatever rights Anthropic has in the outputs and place no restriction on contributing them under the Bouncy Castle License.
  • To the best of my knowledge, the contribution does not reproduce or derive from third-party material in a way that would infringe anyone's intellectual property rights. The code was written against the existing Bouncy Castle sources in this repository.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@dghgit dghgit

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@AlexLaroche @dghgit
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.