Conversation
This was referenced Sep 19, 2026
Contributor
|
We had agreed not to add more ciphers until we get through reviewing and locking in the overall architecture via the AES work. Closing this PR for now. |
…om bc-java New crate `crypto/sm4` (`bouncycastle::sm4`): the SM4 block cipher (GB/T 32907-2016, read via draft-ribose-cfrg-sm4-10) as a raw keyed permutation implementing ElectronicCodeBook, ported from BC Java's SM4Engine. The S-box is a 127-gate Boolean circuit over eight u32 bit-planes rather than the Java engine's 256-byte table, so there is no secret-indexed memory access in the cipher or the key schedule. The SM4 S-box is GF(2^8) inversion between two affine maps -- a decomposition found by exhaustive search against the spec's table -- so a field isomorphism lets the non-linear section of the Boyar-Peralta AES circuit (copied from aes) do the inversion, with generated affine top and bottom layers. A round substitutes four bytes per block, so the engine works on eight blocks per circuit pass: encrypt_8blocks/decrypt_8blocks are the full-rate path, encrypt_2blocks is overridden to use two lanes, and single-block calls do eight blocks' work. One stored schedule (128 B, in a Secret) serves both directions. Also: SM4_CBC<Dir> alias over bouncycastle-modes; `sm4-cbc` CLI subcommand, with aes_cbc_cmd.rs generalised into cbc_cmd.rs so the four CBC commands share one implementation; criterion bench; mem_usage_benches harness; umbrella re-export; release notes. Verified against every value in the draft's Appendix A.1 (Examples 1-6, including all 32 round keys and all 32 per-round outputs of Examples 1 and 4, and both 1,000,000-fold iterated ciphertexts, the latter in release builds only), the SM4-ECB and SM4-CBC vectors of Appendix A.2, the vectors of BC Java's core and provider SM4Test including test1000000(), an exhaustive 256-input S-box test, and a table-driven transcription of the Java engine kept in the tests, which the engine must agree with on thousands of keys and blocks in every lane and both directions. cargo mutants: 364 mutants, 353 caught, 8 unviable, 3 missed -- all three proven XOR/OR equivalences, commented at the site. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The S-box circuit moves from eight u32 bit-planes to eight u16 planes, matching the four-lane shape of bouncycastle-aes and bouncycastle-camellia. A round substitutes four bytes per block, so the u16 planes hold the T argument of four blocks -- each split into two 16-bit halves -- and encrypt_4blocks/decrypt_4blocks become the natural unit; the trait's blocks8 is two passes, blocks2 uses two lanes, and a single-block call does four blocks' work. The per-call working state drops from about 160 B (128 B of block state, 32 B of planes) to about 100 B (64 B of block state, 16 B of T arguments, 16 B of planes), at half the throughput on a 32/64-bit machine -- the trade this design makes. The 127-gate circuit itself is unchanged, and the transpose is the same BearSSL routine on u16 masks; the key schedule's T' now fills four lanes instead of eight. Also: the ElectronicCodeBook::encrypt_8blocks doc in core now names both four-lane crates, and the release notes and the CLI's import follow. Verified as before: every value of draft-ribose-cfrg-sm4-10 Appendix A.1 (round keys and per-round outputs of Examples 1 and 4, now pinned in every lane of the four-block path and every slot of the eight-block path, and both 1,000,000-fold iterated ciphertexts in release builds), the A.2 ECB and CBC vectors, BC Java's SM4Test including test1000000(), the exhaustive 256-input S-box test (sixteen passes of sixteen positions), and the table-driven transcription of the Java engine on thousands of keys and blocks in every lane and slot, both directions; the sm4-cbc CLI tests. cargo mutants: 376 mutants, 364 caught, 8 unviable, 4 missed -- all four proven |/^ equivalences on disjoint bits (the transpose, the half-word join in tau) or the documented Boyar-Peralta t37 gate, commented at the site. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…N as a parameter, so every block cipher crate can write its <Dir, Pad> CBC and ECB aliases over the one projection
…ode projection, and the block-aligned vectors move onto modes::Cbc
…mutation, and Block is no longer exported
Doc comments scattered per-file citations to BC Java's SM4Engine (its table-driven S-box, its F0..F3 round structure, its per-direction key expansion, its CK/FK tables, its algorithm name) throughout lib.rs, sm4.rs and schedule.rs. The crate's Provenance section already names BC Java's SM4Engine as the source implementation this crate is ported from and describes what it reproduces; the scattered comparisons are replaced with self-contained descriptions of the same points (a table-driven S-box's cache-timing exposure, a direction-aware engine's per-direction expansion) that don't depend on the reader having BC Java's source open. Test files that cross-check against BC Java's own vectors (tests/bc_java_tests.rs, tests/common/mod.rs) are untouched, since BC Java is literally their subject. No behavioural change; cargo fmt --check is clean; cargo test -p bouncycastle-sm4 passes (14 passed, 2 ignored long-running cases). Assisted-by: Claude:claude-sonnet-5 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same issue as PR #139 (tdes): mem_usage_benches/src/lib.rs makes every bench source a module of a lib target, so its //! header is rustdoc'd and an indented block compiles as a Rust doctest by default. Fenced as ```text like the other bench binaries. Assisted-by: Claude:claude-sonnet-5 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…/ sm4-cfb8 / sm4-ctr CLI commands, so SM4 covers the same four modes AES does rather than CBC alone -- `bouncycastle-modes` is cipher-agnostic and its Cfb, Cfb8 and Ctr already work over any ElectronicCodeBook, so each alias is three lines of type plus the const parameters SM4 fills in (KEY_LEN = BLOCK_LEN = 16, and a 12-byte CTR nonce leaving the 4-byte counter the mode caps at, the same split bouncycastle-aes chooses for its 16-byte block). SM4_CFB is draft-ribose-cfrg-sm4-10's SM4-CFB-128 and SM4_CFB8 its SM4-CFB-8, the two variants Sec 8.5.1 names; SM4_CTR is Sec 8.7, whose counter sequence the draft leaves to the caller ("any sequence that does not repeat within the block size"), so nonce || counter is an admissible choice rather than a deviation. Test coverage follows what the draft actually publishes: Appendix A.2.4's two SM4-CFB examples are straight known-answer tests through SM4_CFB, driven under the vector's own IV via a FixedSeedRNG exactly as the existing A.2.2 CBC tests are; Appendix A.2.5's SM4-CTR examples make the whole 16-byte IV the first counter block and increment it as a unit, which the nonce/counter split cannot produce, so those vectors pin a reference CTR written from the Sec 8.7.1 equations over the permutation and SM4_CTR is then required to agree with that reference on the counter blocks it does produce; SM4-CFB-8 has no published example at all, so it is pinned the same way against the Sec 8.5.2 equations at s = 8. stream_mode_alias_tests.rs covers the wiring the vectors do not -- that each alias names the mode it claims to, round-trips at any length with no padding, gives the same answer whatever the chunking, draws fresh init data per encryption, and that CFB128, CFB8 and CTR are mutually distinct. The three CLI commands are thin dispatchers over the existing stream_mode_cmd plumbing, so they inherit the IV-in-the-ciphertext convention, the 1 KiB streaming chunk and the key loader unchanged; their tests pin the A.2.4 vectors end to end through the pipe for sm4-cfb and hold sm4-cfb8 and sm4-ctr to the library for the two modes without vectors. No new mutants: `cargo mutants -p bouncycastle-sm4 --list` reports the same 374 before and after, because the three new source files are type aliases and documentation with no executable code of their own. quality_stats.sh's "unwraps in core code" for sm4 goes 3 -> 29, all of it `.unwrap()` inside the new doctests, which the script counts from src/ without distinguishing doc comments; the doctests are written in the same style as crypto/aes/src/{cfb,cfb8,ctr}.rs, which is why aes reports 58.
Assisted-by: Claude:claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pe on the error paths, which is what failed the Rust Tests workflow on fc35da9 -- `a_key_of_the_wrong_length_is_rejected` in sm4_cfb8_cli_tests.rs writes its stdin payload from the test thread and `.expect()`s the result, but the command it invokes rejects the key and `exit`s before reading a byte, so the write races the child's exit and gets EPIPE. The assertion the test actually makes -- non-zero status, and the algorithm named in stderr -- is unaffected, because `wait_with_output` still returns both, so the fix is to match on the write result and ignore `ErrorKind::BrokenPipe` while still panicking on every other write error. That is the same treatment, and the same reasoning, that aes_ctr_cli_tests.rs already documents for its threaded harness; the harness is copied per file, so all three new suites (cfb, cfb8, ctr) get it. The race is timing-dependent -- it passed locally and failed on the loaded CI runner -- so sm4_cfb8_cli_tests.rs also gains a deterministic guard: a 4 MiB payload to a command that exits on a rejected key cannot fit in a pipe buffer, so the write is certain to get EPIPE rather than merely likely to. Verified by reverting the harness under that guard, which reproduces the CI panic exactly, and re-applying it, which does not. Also drops an unused `tohex` helper that sm4_cfb8_cli_tests.rs never called, which was the one warning `cargo test --all` still emitted. No behaviour outside the test harness changes. Assisted-by: Claude:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eam-mode suites got in 703ca45, closing the last copy of that race in this crate's CLI tests -- `a_key_of_the_wrong_length_is_rejected` writes its stdin payload from the test thread and `.expect()`s the result, but `sm4-cbc` rejects the key and `exit`s before reading a byte, so the write races the child's exit and gets EPIPE. This file predates the stream modes and has never failed CI, but the defect is identical and was demonstrated here rather than assumed: dropping a 4 MiB error-path payload into the unpatched harness reproduces exactly the panic that failed the Rust Tests workflow on the cfb8 suite. The write result is now matched on, `ErrorKind::BrokenPipe` ignored and every other write error still a panic, which is what the five aes_*_cli_tests.rs suites have always done; an audit of cli/tests confirms no suite in the tree still `.expect()`s that write. The new `a_large_payload_on_an_error_path_does_not_break_the_harness` is the deterministic guard for it -- a payload that cannot fit in a pipe buffer makes EPIPE certain rather than merely likely, so the harness cannot silently regress to the flaky form. Also reflows the over-width comment lines this series left in sm4_cfb8_cli_tests.rs: rustfmt does not wrap comments, so they passed `cargo fmt --check` while sitting past the 100-column max_width every other comment in the tree respects. Nothing the existing tests assert on changes: `wait_with_output` still returns the exit status and the stderr they match against, and no non-test code is touched. Assisted-by: Claude:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ibed a repo that no longer exists -- this file's own preamble asks for stale lines to be reported and fixed, and these two were found the hard way, by a PR failing a workflow the file says does not exist. CI: it claimed `publish_doc_benches_to_ghpages.yaml` was the only workflow and that "there is no separate CI test/lint job -- local `cargo test --workspace` is the gate". There are five workflows, four of them real PR gates (`rust-build.yml`, `rust-test.yml`, `rust-docs.yml`, `rust-style.yml`), and `Rust Tests` fails PRs; the section is now a table of what each one runs, so the local commands that mirror the gate are in one place. Three further details are recorded because each has already cost time: the pages workflow only publishes on a push to `main` (its last two jobs are gated on `github.ref`), it does not run benchmarks at all despite the file name (the `run_benches` job is commented out, "the benches run crazy slow on the github agent") so the old claim that it "publishes docs, code stats, and benchmark results" was wrong on the third, and its `concurrency: group: "pages"` is global rather than per branch, so pushing several branches at once leaves all but the last reporting "cancelled" -- a result that looks like a failure and is not. Toolchain: it claimed the workspace uses nightly pinned in `rust-toolchain.toml` because `core/src/lib.rs` enables `#![feature(adt_const_params)]`. No `rust-toolchain.toml` exists anywhere in the tree, `core` has no feature gate, and the only `adt_const_params` line in the workspace is commented out in `crypto/mldsa/src/lib.rs` beside a commented `unsized_const_params`; CI builds and tests on stable (1.98.1 as installed by `dtolnay/rust-toolchain@stable`) and passes, so the workspace is stable-clean and the note now says so, and warns that a nightly default toolchain locally will hide nightly-only code until CI catches it. The one place nightly is genuinely required is `rust-style.yml`, which does `rustup override set nightly` before `cargo fmt --all --check` while every other job stays on stable; that is now stated rather than implied. Documentation only -- no code, tests or build files are touched. Assisted-by: Claude:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hich no longer exist since 334cd2b made the methods required Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bsorbed through its merges (SymmetricCipher/BlockCipherPadding renames, the ElectronicCodeBook batch methods, AES*Internal, core::security_strength, PaddedBlockCipher* adapters and the rest), restored in one commit after the linear rebase dropped those merges; the tree is identical to merging c5f639d with 2d4d038 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dghgit
changed the base branch from
feature/xof-cshake
to
feature/simple-ciphers
September 28, 2026 15:49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tests, stream-mode tests, the CLI CFB8/CTR tests, the raw-Cbc draft test and the crate's doc examples take the renamed in-place one-shots (encrypt_in_place/decrypt_in_place), renamed only where the call is in place (the padded SM4_CBC Vec one-shots are unchanged), and import the SymmetricCipher* supertraits their do_*_init calls now come from Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # cli/src/main.rs
# Conflicts: # alpha_0.1.3_release_notes.md # cli/src/main.rs # crypto/aes/src/cbc.rs # crypto/aes/src/ecb.rs # crypto/aes/src/lib.rs # crypto/padding/src/padded_mode.rs
SM4 moves to bouncycastle_sm4::hazmat with BLOCK_LEN, KEY_LEN and LANES defined at the crate root; everything else is the path in use lines and doc links. No logic change, no mutation run owed. Assisted-by: Claude Code:claude-fable-5-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # crypto/aes/src/cbc.rs # crypto/aes/src/hazmat/ecb.rs # crypto/padding/src/padded_mode.rs
Follows aes 160ac18; the padding crate's PaddedMode copy goes with it. Type alias only, no behaviour change, no mutation run owed. Assisted-by: Claude Code:claude-fable-5-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in 8dff255 (bouncycastle-modes and bouncycastle-padding folded into the new bouncycastle-cipher crate) and a27d9e6 (StreamCipher and the direction markers moved out of core), so the SM4 crate, its CLI commands and tests are rewritten onto the new paths: bouncycastle_cipher::modes::*, bouncycastle_cipher::padding::*, and bouncycastle_cipher::{Direction, Encrypting, Decrypting}. Import rewrite only, no behaviour change. Assisted-by: Claude Code:claude-fable-5-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Adds
bouncycastle-sm4: a constant-time, table-free, low-memory SM4 block cipher (GB/T 32907-2016), ported from Bouncy Castle Java'sSM4Engine.Description
encrypt_4blocks/decrypt_4blocks).SM4_CBC<Dir>alias plus asm4-cbcCLI subcommand.Scope and Risk
New crate only, plus the shared
<Dir, Pad>/PaddedModerefactor (movesPaddedModefrombouncycastle-aesintobouncycastle-paddingso every block cipher can share it) -- additive, covered by existing AES suites.Validation
draft-ribose-cfrg-sm4-10Appendix A.1 vectors (all 6 examples, every round key/output, two 1,000,000-fold iterations); Appendix A.2.1/A.2.2 ECB/CBC vectors; BC Java's ownSM4Testvectors.cargo test -p bouncycastle-sm4: all passing.cargo mutants -p bouncycastle-sm4: 374 mutants, 361 caught, 8 unviable, 4 missed (XOR/OR equivalences), 1 timeout.AI Usage Statement
Assisted-by: Claude:claude-sonnet-5
🤖 Generated with Claude Code