Repository navigation
Conversation
|
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>
…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>
…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>
… 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>
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>
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>
…and the workspace wiring The replay of this branch onto feature/key-rework-memory drops its merges from feature/simple-ciphers, and the move of bouncycastle-modes and bouncycastle-padding into bouncycastle-cipher, with Direction and ElectronicCodeBook's new homes, was resolved only in those merges. This is the sm4 crate, its CLI commands and tests and its memory bench exactly as the branch tip 4c336bf had them, with the four sm4 subcommands in main.rs restored, whose conflicts the replay resolved to the base. Assisted-by: Claude:claude-mythos-5-1
…e engine becomes SM4Internal The engine is renamed for what it is under the key objects of feature/key-rework-memory: SM4 -> SM4Internal, Clone, implementing ElectronicCodeBook (new) and BlockPermutation (the block methods) separately, and ModeNames for all nine modes, since SM4 has published modes beyond the four aliased here (draft-ribose-cfrg-sm4-10 Sec 8.3 ECB, RFC 8998 Sec 3.5 GCM and CCM). SM4Key<M: Mode<SM4Internal>> mirrors AESKey at the one key length: the key holds the mode's key state, FromKeyMaterial<16> builds it, and the role impls -- symmetric, block, ECB, AEAD and MAC pairs -- forward to the mode traits, so which roles a key carries is decided by its mode. The crate aliases CBC, CFB, CFB8 and CTR as before, now with the key-state lifetime (SM4_CBC<'k, Dir, Pad>, ...); the other modes are reached by naming them. Tests: cbc_alias_tests and stream_mode_alias_tests become padded_alias_tests, alg_name_tests and a key-side stream_mode_alias_tests; mode_key_tests runs the FromKeyMaterial, block, symmetric, stream and AEAD frameworks over the key; the draft's A.2.2 CBC vectors drive SM4Key<CBC<Blocks>>, its A.2.4 CFB vectors, the A.2.5 CTR reference and the CFB8 equations the stream keys, and A.2.1, A.1, bc-java and the reference transcription the engine. rfc8998_tests is new: the SM4-GCM and SM4-CCM vectors of RFC 8998 Appendix A through SM4Key<GCM> and SM4Key<CCM<12, 16>>, the two AEAD roles the key carries. CLI: the four sm4 commands load an SM4Key and keep their contract -- whole blocks under NoPadding for CBC, the IV or nonce in band, no --iv -- and their integration tests replay the draft's vectors through the binary and check the stream commands against the key's in-place decryption. Assisted-by: Claude:claude-mythos-5-1
…and MAC pairs go SM4Key<M> carried the full AESKey role set: the AEAD and declared-AEAD pairs (GCM, CCM), the hazmat ECB pair (ECB<Blocks>) and the MAC pair (CMAC), none of which this crate aliases or exposes in the CLI. They were there for parity with AESKey only. The symmetric and block pairs remain; between them they cover CBC, CFB, CFB8 and CTR, the four modes the SM4_* aliases name, and a key under any other mode has no role. mode_key_tests drops the ECB<Blocks> and GCM construction and framework runs. rfc8998_tests keeps the SM4-GCM and SM4-CCM vectors of RFC 8998 Appendix A but runs them through the hazmat mode traits over the permutation's key state, the path a key under those modes would forward to, since no key role reaches them any more. ModeNames keeps all nine names, since the mode types over the engine name themselves by them. Assisted-by: Claude:claude-mythos-5-1
Lifted onto this branch from feature/key-rework-memory (5607ba9), where AESKey and AESInternal gained the same: the engine compares the schedule through Secret's byte-wise equality, which inspects every byte so the time taken does not say where two differ, and a key is equal when its mode state is, bounded on the state rather than on Mode::KeyState. The key objects never hand the material back, so this is the only way two keys can be compared. mode_key_tests: equality follows the material for a clone, the same bytes and a single flipped bit. Assisted-by: Claude:claude-mythos-5-1
44855df to
9c343a5
Compare
|
Reviewed the PR with Mythos. Everything looks fine expect for the keys with no roles and the cli doesn't match the AES CLI. There's also some editorial findings as well as small nitpicks. Here is the findings below
Should fix (style guide)
Nits
Context, not findings
|
…table is measured, and the doc nits From the review of PR #140. SM4Key::from_key_material carried as_cipher_key and a `_ =>` arm for MAC-mode keys, which lost their role in 8df9e35; both go, and with them check_key_type, since the permutation's own validation is the same check for every mode that has a role here (a scoped `cargo mutants` on key.rs is now 0 missed where it was 4; the crate lists 395 mutants, from 406). The key docs name ECB<PKCS7> among the symmetric-pair modes, which mode_key_tests already covers. The Security Considerations heading takes the style guide's form and the anchor follows it. The Memory Usage section tabulates the measured stack per entry point, as the AES crate does: bench_sm4_mem_usage takes the AES bench's measure-closure shape for new and the six block entry points, and the figures are the closure frames from the release build's frame-layout remarks plus the return address. The three unit tests in hazmat/sm4.rs that tested public behaviour only (the engine size, the algorithm name, the LANES constant) move to alg_name_tests or go; the per-round pins stay. Nits: the manifest comment, the dead `crate::SM4::decrypt_block` link in schedule.rs, the camellia citation in bitslice.rs, the `src/sm4.rs` path in gbt32907_tests.rs, the ElectronicCodeBook attributions in hazmat/sm4.rs for methods that are on BlockPermutation, and every doc or comment line past 100 columns. Assisted-by: Claude:claude-mythos-5-1
…le and -d From the review of PR #140: sm4-cbc was CBC<NoPadding> with no --iv while aes128-cbc on this base is PKCS7 at any length with --iv/--iv-file, and the sm4 help pointed at aes128-cbc for an alignment rule it no longer has. sm4-cbc is now CBC<PKCS7> over the same block-mode helpers; the three stream commands take --iv/--iv-file through load_iv as the aes ones do; and all four take the direction as -d/--direction, which every other cipher command already does. The CLI tests pin encryption as well as decryption now that the IV can be supplied: sm4-cbc encrypt --iv reproduces the draft's A.2.2 ciphertexts followed by the PKCS7 block (computed from the permutation), sm4-cfb encrypt --iv reproduces A.2.4 exactly, and sm4-cfb8 and sm4-ctr under --iv are held to the library. The alignment test becomes an any-length test plus a rejected unpadded ciphertext. Their module docs cite the files that exist (cli/src/helpers/, test_aes_*.sh). Assisted-by: Claude:claude-mythos-5-1
…ts over the engine The review of PR #140 noted that the only SM4 Wycheproof vectors are sm4_gcm_test.json and sm4_ccm_test.json and that the four-mode scope left them unrun. They run now the way rfc8998_tests already runs the RFC 8998 vectors: over the hazmat mode traits and the permutation's key state, since the key carries no AEAD role. The suites follow crypto/aes/tests/{gcm,ccm}_wycheproof.rs at the one key length: GCM dispatches the 65 96-bit-IV cases (38 valid, 27 invalid) and counts the other 39 as unsupported; CCM dispatches the 162 cases within A.1's nonce and tag sets (135 valid, 27 invalid) and counts the other 22. All pass. The crate docs' provenance list names both files. Assisted-by: Claude:claude-mythos-5-1
|
Review items addressed in
|
…tack table is measured, and the doc nits The same round as PR #140's review, applied here. CamelliaKey's three constructors carried as_cipher_key and a `_ =>` arm for MAC-mode keys, which lost their role in 922236e; both go, and with them check_key_type, since the permutation's own validation is the same check for every mode that has a role (a scoped `cargo mutants` on key.rs is 0 missed; the crate lists 577 mutants). The key docs name ECB<PKCS7> among the symmetric-pair modes. The Security Considerations heading takes the style guide's form and the anchor follows it. The Memory Usage section tabulates the measured stack per entry point and key length: bench_camellia_mem_usage takes the AES bench's measure-closure shape for new and the six block entry points at each key length, and the figures are the closure frames from the release build's frame-layout remarks plus the return address. The three unit tests in hazmat/camellia.rs that tested public behaviour only (the engine sizes, the algorithm names, the LANES constant) move to alg_name_tests or go; the Sec 2.3.3 subkey-swap pin stays. Nits: the manifest comment, the sm4 citation in bitslice.rs, the ElectronicCodeBook attributions for methods that are on BlockPermutation, "An Camellia", and every doc or comment line past 100 columns, including the two rustfmt could not fit. Assisted-by: Claude:claude-mythos-5-1
…file and -d As for sm4 on PR #140: camellia*-cbc was CBC<NoPadding> with no --iv while aes*-cbc on this base is PKCS7 at any length with --iv/--iv-file, and the camellia help pointed at aes128-cbc for an alignment rule it no longer has. The three CBC commands are now CBC<PKCS7> over the same block-mode helpers; the nine stream commands take --iv/--iv-file through load_iv as the aes ones do; and all twelve take the direction as -d/--direction, which every other cipher command already does. The CLI tests pin encryption as well as decryption now that the IV can be supplied: camellia*-cbc encrypt --iv reproduces the OpenSSL evpciph_camellia CBC chains followed by the PKCS7 block (computed from the permutation of the key's length), camellia*-cfb encrypt --iv reproduces the CFB chains exactly, and camellia128-cfb8 and -ctr under --iv are held to the library. The alignment test becomes an any-length test plus a rejected unpadded ciphertext. Their module docs cite the files that exist. Assisted-by: Claude:claude-mythos-5-1
…ack table is measured, and the doc nits The same round as PR #140's review, applied here. TDESKey's two constructors ran check_key_type ahead of the engine's own validation, which is the same check for every mode that has a role; it goes (a scoped `cargo mutants` on key.rs is 0 missed; the crate lists 500 mutants). The constructors' docs say the checks are the engine's. The Security Considerations heading takes the style guide's form and the anchor follows it. The Memory Usage section tabulates the measured stack per entry point and engine: bench_tdes_mem_usage takes the AES bench's measure-closure shape for new and the block entry points of both engines, the two-key engine's decrypting ones only, under the TECBMMT3 COUNT = 0 bundle, and the figures are the closure frames from the release build's frame-layout remarks plus the return address. The unit tests in hazmat/tdes.rs and hazmat/tdes2.rs that tested public behaviour only (the engine sizes, the algorithm names and strengths, the ENCRYPTION_APPROVED gate) move to alg_name_tests; the split/join, parity and schedule pins stay. Nits: the manifest comment, the reference-style link in key.rs that could not fit, and every doc or comment line past 100 columns. Assisted-by: Claude:claude-mythos-5-1
|
Looks good, there are 2 new issues found after the fix: New issues:
|
Summary
Adds
bouncycastle-sm4: a constant-time, table-free, low-memory SM4 block cipher (GB/T 32907-2016, as described in draft-ribose-cfrg-sm4-10), ported from Bouncy Castle Java'sSM4Engine, on the key objects of #172:SM4Key<M>over the engineSM4Internal.Rebased onto
feature/key-rework-memory(#172) on 2026-10-25 and ported to its key-object design; the base is now that branch. The review items of 2026-10-27 are addressed in the last three commits: the key's leftover MAC-key path is gone, the CLI matches the AES commands, the Memory Usage table is measured, the style and nit items are fixed, and the SM4 Wycheproof GCM and CCM suites run through the mode traits. The four-mode scope (no AEAD roles) is unchanged.Description
SM4Key<M>mirrorsAESKeyat SM4's one key length: the key holds the mode's state, its workers borrow it, and which role pair a key carries is decided by its mode. The crate aliases CBC, CFB, CFB8 and CTR (SM4_CBC<'k, Dir, Pad>,SM4_CFB<'k, Dir>, ...); the key carries the symmetric and block role pairs only, which cover those four modes; the engine implementsModeNamesfor all nine, so the mode types over it are named, but a key under GCM, CCM, CMAC orECB<Blocks>has no role. Keys and engines implementPartialEq/Eqin constant time over the expanded schedule, as on Key object rework: the traits lose their constructors, every mode gets a key, and workers borrow the key #172.sm4-{cbc,cfb,cfb8,ctr}, through the key object, with theaes128-*contract:sm4-cbcisCBC<PKCS7>at any length, every command takes--iv/--iv-file(the IV or nonce is in band otherwise) and-d/--direction.Scope and Risk
New crate only; the shared changes it carries (the
bouncycastle-sm4workspace entry, the umbrella re-export, the memory bench) are additive. No behavioural change to AES or other existing ciphers.Validation
--release), A.2.1 ECB and BC Java's ownSM4Testvectors, through the engine; A.2.2 CBC throughSM4Key<CBC<Blocks>>, A.2.4 CFB throughSM4Key<CFB>, A.2.5 CTR through a reference built from the Sec 8.7.1 equations whichSM4Key<CTR>is held to, and the Sec 8.5.2 equations ats = 8forSM4Key<CFB8>.GCMandCCM<12, 16>hazmat mode traits over the permutation's key state (new with the port); Wycheproofsm4_gcm_test.json(65 of 104 cases dispatchable, the 96-bit-IV ones) andsm4_ccm_test.json(162 of 184, those within SP 800-38C A.1's nonce and tag sets) the same way.TestFrameworkFromKeyMaterialfor every mode, and the block, symmetric and stream suites.cargo test --workspace: all passing;cargo test --workspace --all-targets --no-runbuilds the benches;cargo +nightly fmt --checkclean. The CLI integration tests pin the draft's A.2.2 CBC and A.2.4 CFB vectors through the binary in both directions, encryption under--iv.cargo mutants -p bouncycastle-sm4after the review fixes: 395 mutants, 368 caught, 23 unviable, 4 missed (the XOR/OR equivalences inortho::swap,sboxandtau); the key.rs misses of the review are gone.AI Usage Statement
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-mythos-5-1
🤖 Generated with Claude Code