Skip to content

Feat: Core AEAD Cipher Split - #120

Merged
officialfrancismendoza merged 16 commits into
bcgit:feature/xof-cshakefrom
officialfrancismendoza:feature/officialfrancismendoza/119-core-aead-cipher
Sep 21, 2026
Merged

officialfrancismendoza merged 16 commits into
bcgit:feature/xof-cshakefrom
officialfrancismendoza:feature/officialfrancismendoza/119-core-aead-cipher

Conversation

@officialfrancismendoza

@officialfrancismendoza officialfrancismendoza commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Implements core AEAD cipher split (#119), based on #118 (feature/xof-cshake). Implements AEADCipherEncryptor and AEADCipherDecryptor, as well as cherry-picks commits from the ASCON PR (#21) so that ASCON is compatible — this PR supersedes #21 and implements #22.

PR superseded by PR #149

@officialfrancismendoza officialfrancismendoza self-assigned this Sep 8, 2026
@officialfrancismendoza
officialfrancismendoza changed the base branch from main to release/0.1.3alpha September 8, 2026 11:59
@ounsworth
ounsworth self-requested a review September 8, 2026 17:03
ounsworth

This comment was marked as outdated.

@ounsworth ounsworth left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holy crap. This is a 13,000 line PR. That's an insane amount of code to review.

It looks like this PR is bringing (at least) to other PRs with it? What is the actual change here?

I suggest that you either change the merge target to point to another currently-open PR so that the diff to review is smaller (and we can merge this into the other PR), or maybe hold off on this for now and we should focus out effort on reviewing and merging the other PRs first?

@officialfrancismendoza
officialfrancismendoza changed the base branch from release/0.1.3alpha to feature/symmetric-cipher September 8, 2026 17:07
@officialfrancismendoza
officialfrancismendoza changed the base branch from feature/symmetric-cipher to feature/officialfrancismendoza/ascon September 8, 2026 17:07
@officialfrancismendoza
officialfrancismendoza force-pushed the feature/officialfrancismendoza/119-core-aead-cipher branch from 7471ef2 to 01c3e6f Compare September 9, 2026 08:35
@dghgit
dghgit changed the base branch from feature/officialfrancismendoza/ascon to feature/symmetric-cipher September 9, 2026 11:14
@officialfrancismendoza
officialfrancismendoza force-pushed the feature/officialfrancismendoza/119-core-aead-cipher branch from 01c3e6f to aa209a9 Compare September 9, 2026 17:24
@dghgit

dghgit commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Yes, github failed to base it correctly. It's now just the split and ASCON.

Note: ASCON includes an XOF/Digest as well, so #118 is also relevant here - this PR will be blocked from merging by #118.

This was referenced Sep 10, 2026
@ounsworth
ounsworth marked this pull request as draft September 16, 2026 02:54
…in update_out_len and a FINAL_LEN final buffer so a buffering cipher or an inline ciphertext||tag layout can be expressed; TaggedEncryptor/TaggedDecryptor adapt any FINAL_LEN=0 pair to the SimpleCipherEncryptor/SimpleCipherDecryptor ciphertext||tag shape; the block, simple-cipher and AEAD strength sweeps assert they are not vacuous, and the AEAD streaming suite gains a genuinely-buffering toy plus undersized-buffer and std-one-shot coverage
…XOF128/CXOF128) implementing AEADCipherEncryptor/AEADCipherDecryptor via AsconAead128Encryptor/AsconAead128Decryptor, with HashFactory/XOFFactory registration and CLI wiring including a TaggedDecryptor-based decrypt stream
…OF/XOFSqueezer API and updated factory/CLI/tests/benches to compile against the new API (bcgit#119)
@officialfrancismendoza
officialfrancismendoza force-pushed the feature/officialfrancismendoza/119-core-aead-cipher branch from 5ba4350 to 2c479f4 Compare September 17, 2026 13:38
@officialfrancismendoza

Copy link
Copy Markdown
Contributor Author

As per @dghgit request, retargeting to merge into #118, rebased on that branch, and updated code accordingly. This PR contains the most recent form of ASCON that utilizes the new AEADCipherEncryptor/Decryptor. Hence, #22 will be closed in favor of this one.

@officialfrancismendoza
officialfrancismendoza marked this pull request as ready for review September 17, 2026 13:46
@dghgit
dghgit changed the base branch from feature/symmetric-cipher to feature/xof-cshake September 18, 2026 04:05

@dghgit dghgit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See sent Fable report. Looks good mostly though, just a bit further to go.

@officialfrancismendoza
officialfrancismendoza marked this pull request as draft September 18, 2026 06:48
dghgit and others added 2 commits September 20, 2026 18:33
…/officialfrancismendoza/119-core-aead-cipher
…CLI thread, and fix a broken intra-doc link (bcgit#119)

Review follow-ups on the head of bcgit#120; no behaviour changes.

- cli/src/main.rs, cli/src/ascon_cmd.rs: the ascon-aead128 command's help and module docs still
  described the pre-nonce-prefix format ("output = ciphertext||tag") after the command started
  generating a nonce and writing it as the first 16 bytes of the stream. They now spell the
  convention out in both directions, the way aes128-ctr's help does for its own nonce, and say what
  --nonce/--nonce-file turn off -- the part a user gets wrong, since feeding a prefixed ciphertext
  to "--decrypt --nonce ..." decrypts garbage and only then fails the tag check. The two encrypt
  paths each gain a line saying which API they drive and why the explicit-nonce one cannot use the
  AEADCipherEncryptor pair (do_encrypt_init generates the nonce by construction).
- cli/src/main.rs: fn main's 8 MiB thread gains a comment for the constraint it exists for. It is
  load-bearing: with it removed and `ulimit -s 1024`, every subcommand -- sha3-256 as much as
  ascon-aead128 -- overflows during argument parsing in a debug build, before any algorithm runs.
- crypto/ascon/src/lib.rs: [`ascon_aead128::AsconAead128Decryptor::do_decrypt_final`] does not
  resolve, because do_decrypt_final is an AEADCipherDecryptor method rather than an inherent one,
  so `cargo doc` warned and published a dead link. Points at the trait method instead.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dghgit and others added 5 commits September 20, 2026 18:41
…#119)

The entry carried the pre-remediation run, flagged as such ("20 missed before the XOF/CXOF
boundary-test additions"). Re-measured on this head with `cargo mutants -p bouncycastle-ascon
--test-package bouncycastle-ascon --jobs 3 --timeout 120`, with bc-test-data reachable from the
copied tree and a config whose examine_globs block is removed: 735 mutants, 618 caught, 111
unviable, 6 missed. The six are the known equivalences already commented at their sites -- the
sponge absorb/squeeze boundaries and the two disjoint-bit `|` -> `^` in set_state_byte -- so the
14 real survivors that run found in the XOF/CXOF Hash view are dead.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eded by the AEADCipherEncryptor/AEADCipherDecryptor split (bcgit#119)

AEADCipher was the single-type AEAD trait this issue exists to split. It had no implementor on the
base branch and its conformance suite had nothing to run against; this PR was about to give it its
first and only implementor, on AsconAead128, in the same change that introduces the pair meant to
replace it. That would have left the library with two parallel AEAD abstractions and Ascon-AEAD128
with four public one-shot encrypt surfaces. Deleted instead:

- crypto/core/src/traits.rs: the trait itself (encrypt/encrypt_out/decrypt/decrypt_out, the
  aead_* pair, do_aead_encrypt_final/do_aead_decrypt_final). The AEADCipherEncryptor doc that
  contrasted its tag placement with this trait's now just points at tagged_aead.
- crypto/core-test-framework/src/symmetric_ciphers.rs: TestFrameworkAEADCipher::test and
  ::test_plain_one_shots, the suites for it. The struct keeps test_encryptor_decryptor and
  test_buffering_toy, which exercise the pair.
- crypto/ascon/src/ascon_aead128.rs: the impl, and the module-doc sentence that justified the
  newtype pair by pointing at it.

Test coverage is kept where it was about Ascon rather than about the trait: the chunk-boundary
sweep and the wrong-tag rejection now drive the inherent do_encrypt_final/do_decrypt_final (they
only used the trait for its finalizers), and the undersized-buffer suite is rewritten against the
inherent one-shots, whose own length checks -- including the 16-byte-ciphertext and oversized-buffer
boundaries that must NOT be rejected -- were previously reached only through the trait. The three
tests that were about the deleted code (the std Vec wrappers, the plain view's
DecryptionFailed remapping, the AEADCipher framework conformance call) go with it.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ion figures (bcgit#119)

Deleting the trait and its suites takes bouncycastle-ascon from 735 mutants to 655: 558 caught, 91
unviable, 6 missed, the same six known equivalences as before, so the tests ported onto the
inherent one-shots hold the coverage the deleted trait's tests had.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ag layout on the AEAD traits, and address the remaining API-shape review points (bcgit#119)

The `tagged_aead` adapter pair is gone; what it did belongs to the traits themselves.

- crypto/core/src/traits.rs: AEADCipherEncryptor gains `tagged_encrypt` (one-shot into
  `ciphertext || tag`), `tagged_do_aead_encrypt_final` (streaming: flush, then append the tag) and
  `tagged_encrypt_out_len`; AEADCipherDecryptor gains `tagged_decrypt`,
  `tagged_do_aead_decrypt_final` (streaming: the tail is leftover ciphertext followed by the tag)
  and `tagged_decrypt_out_max_len`. All are defaults over the existing methods, so every
  implementor gets both layouts and neither has to be bolted on by a wrapper type that cannot
  express a buffering cipher's lengths (the `FINAL_LEN = 0` restriction TaggedEncryptor and
  TaggedDecryptor carried).
- crypto/core/src/tagged_aead.rs is deleted, with its module declaration and every use of it.
  crypto/core/tests/aead_tagged_tests.rs keeps the toy AEAD the deleted module's in-`src` tests
  used and points it at the new methods: round trip at every length crossing `TAG_LEN`, every
  chunking, tampering, a stream that ends before a whole tag, and every undersized buffer.
- crypto/core-test-framework: the AEAD suite now checks the inline layout for every implementor
  (one-shot against streaming, and a too-short tail as DecryptionFailed), and the buffering toy
  checks it where FINAL_LEN > 0, which is where `tagged_do_aead_encrypt_final` has to flush and
  append in one call. Its short-buffer probe on the decryptor now feeds the decryptor its own
  ciphertext rather than the plaintext, and uses the ciphertext's length.
- crypto/ascon: `AsconAead128::new`'s `for_encryption: bool` is no longer public API --
  `new_encrypting` / `new_decrypting` name the direction, and the bool constructor they share is
  private. The crate docs gain a `tagged_*` example.
- cli/src/ascon_cmd.rs: both directions drive the trait pair, holding the tag back by hand on the
  way in, which is what the adapter did for it. A failed `do_encrypt_init`/`do_decrypt_init` --
  the RNG or the key material -- now prints an error and exits rather than panicking, as
  block_mode_cmd.rs does for the same call, and the remaining unwraps carry their `infallible:`
  notes.
- crypto/core/src/traits.rs also: the allocating one-shot's three-part return is now the named
  `AEADEncrypted<NONCE_LEN, TAG_LEN>` (clippy `type_complexity`), and `decrypt_out` /
  `encrypt_out_rng` get the same "an implementor with FINAL_LEN > 0 must override this" note
  `encrypt_out` already had.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nges (bcgit#119)

661 mutants, 558 caught, 97 unviable, 6 missed -- the same six known equivalences (the sponge
absorb/squeeze boundaries and the two disjoint-bit `|` -> `^` in set_state_byte). The count moves
from 655 with the new_encrypting/new_decrypting constructors.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@dghgit dghgit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, there were a couple which I needed to change but it was basically done.

I'm not how communication broke down, but I've deleted TaggedEncryptor/Decryptor and added the extra methods to the AEAD class. It's really a finalisation decision whether a tag is in-line or not and the code has to always be adjusted accordingly, other than that though it's the some cipher type, it's just there's two ways of dealing with the tag. I've refactored the extra trait out.

The PR just needs an AI declaration as that's now the official policy in CONTRIBUTING.md, I'll mark this as approved, subject that been done. Declaration just needs to be something like "Assisted-by: Claude:claude-sonnet-5".

dghgit and others added 3 commits September 21, 2026 04:24
… in the tagged AEAD defaults (bcgit#119)

`cargo mutants -p bouncycastle-core -f crypto/core/src/traits.rs --re
'AEADCipherEncryptor|AEADCipherDecryptor' --test-package bouncycastle-core --test-package
bouncycastle-ascon` reported 116 mutants, 91 caught, 19 unviable, 6 missed. Four of the six were
real: the buffer guards could be weakened without a test noticing, because a too-short buffer is
rejected either by the guard or by the `do_update_out` behind it, and both report
IncorrectOutputBufferLength with the same length -- so the probes could not tell which had fired.

- crypto/core/tests/aead_tagged_tests.rs: `tagged_do_aead_decrypt_final` with a buffer of exactly
  `needed` must succeed. Kills `plaintext.len() < needed` -> `<=` and -> `==`.
- crypto/core-test-framework: the buffering toy now finishes from a tail that still holds
  ciphertext (TAG_LEN + 4 bytes) into an exactly-sized buffer, which is what makes
  `update_out_len(..) + FINAL_LEN` observable -- with a generous buffer any arithmetic there would
  do. Kills `+ FINAL_LEN` -> `* FINAL_LEN`. The AEAD suite also feeds `encrypt_out_rng` a buffer
  with room to spare, so its own guard cannot be flipped to `>` unnoticed.

The re-run is 116 mutants, 95 caught, 19 unviable, 2 missed; the two are `written + final_len` ->
`written - final_len` in `encrypt_out_rng`, equivalent while every implementor has FINAL_LEN = 0.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…defaults (bcgit#119)

The ascon crate's numbers were already there; the pair's own defaults in core were only in
f376c14's commit message. 116 mutants, 95 caught, 19 unviable, 2 missed.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…/119-core-aead-cipher

Two conflicts, both from the base branch's renames. In crypto/core/src/traits.rs the
AEADCipher trait this PR deletes collided textually with the AEADCipherDecryptor docs
that replaced it, resolved in favour of the deletion. In core-test-framework's
symmetric_ciphers.rs the two import lists were unioned, minus the deleted AEADCipher.

The PR's own code follows the base branch's API renames: SimpleCipherEncryptor /
SimpleCipherDecryptor are SymmetricCipherEncryptor / SymmetricCipherDecryptor,
TestFrameworkSimpleCipher is TestFrameworkSymmetricCipher, and
SymmetricCipherError::IncorrectOutputBufferLength(&'static str, usize) is
OutputBufferTooSmall(usize) -- the dropped name field took a sentence in ascon's two
one-shots, which is no loss since the error identifies the buffer by the call it came from.

Assisted-by: Claude:claude-opus-5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@officialfrancismendoza officialfrancismendoza added documentation Improvements or additions to documentation enhancement New feature or request labels Sep 21, 2026
Assisted-by: Claude:claude-sonnet-5
@officialfrancismendoza

Copy link
Copy Markdown
Contributor Author

Significant portions of this pull request were made with Claude Code, assisted by claude-sonnet-4-6 and reviewed by Fable.

@officialfrancismendoza
officialfrancismendoza dismissed ounsworth’s stale review September 21, 2026 08:55

Feature branch retargeted to bcgit:feature/xof-cshake to reduce bloat

@officialfrancismendoza
officialfrancismendoza marked this pull request as ready for review September 21, 2026 08:56
@officialfrancismendoza
officialfrancismendoza merged commit a777acd into bcgit:feature/xof-cshake Sep 21, 2026
8 checks passed
@dghgit

dghgit commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Reopening this as #149.

This PR is recorded as merged, but the merge happened on the GitHub mirror rather than on origin. The mirror is refreshed from origin, so the subsequent reset took the merge commit (a777acdc) with it: feature/xof-cshake is back at 8a13369 on both remotes and has none of this work in it. GitHub will not reopen a merged pull request, so the change continues in #149 — same head branch (62e6a2b), same base, same commits, nothing rebased or squashed.

The review history stays here; please do any further review on #149.

Assisted-by: Claude:claude-opus-5

officialfrancismendoza added a commit to officialfrancismendoza/bc-rust that referenced this pull request Sep 21, 2026
dghgit pushed a commit to officialfrancismendoza/bc-rust that referenced this pull request Sep 24, 2026
This was referenced Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants