Skip to content

sm4: add bouncycastle-sm4, a constant-time SM4 block cipher - #140

Open
dghgit wants to merge 31 commits into
release/0.1.3alphafrom
feature/sm4
Open

dghgit wants to merge 31 commits into
release/0.1.3alphafrom
feature/sm4

Conversation

@dghgit

@dghgit dghgit commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

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's SM4Engine, on the key objects of #172: SM4Key<M> over the engine SM4Internal.

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.

Update 2026-11-04: with the key settled as the source of the operator, the AEAD roles are back (RFC 8998 defines AEAD_SM4_GCM and AEAD_SM4_CCM): SM4Key<GCM> and SM4Key<CCM<N, T>> / SM4Key<CCM_Packet<..>> carry the AEAD pairs, with the SM4_GCM and SM4_CCM aliases and the sm4-gcm and sm4-ccm subcommands (the GCM and CCM runners are the AES commands' over the SM4 key). RFC 8998 Appendix A and the Wycheproof suites now run through the keys.

Description

  • The S-box is a 127-gate Boolean circuit (found by exhaustive search, reusing the Boyar-Peralta AES non-linear section via a field isomorphism), not a lookup table.
  • Bit-sliced over 16-bit planes, four blocks at a time.
  • SM4Key<M> mirrors AESKey at 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, block and AEAD role pairs, which cover those four modes plus GCM, CCM and CCM_Packet (SM4_GCM<'k, Dir>, SM4_CCM<'k, Dir, ..>); the engine implements ModeNames for all nine, so the mode types over it are named, but a key under CMAC, GMAC or ECB<Blocks> has no role (SM4 has no published CMAC or ECB-mode use). Keys and engines implement PartialEq / Eq in 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.
  • CLI subcommands sm4-{cbc,cfb,cfb8,ctr,gcm,ccm}, through the key object, with the aes128-* contract: sm4-cbc is CBC<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-sm4 workspace entry, the umbrella re-export, the memory bench) are additive. No behavioural change to AES or other existing ciphers.

Validation

  • draft-ribose-cfrg-sm4-10 Appendix A.1 vectors (all 6 examples, every round key and round output, the two 1,000,000-fold iterations under --release), A.2.1 ECB and BC Java's own SM4Test vectors, through the engine; A.2.2 CBC through SM4Key<CBC<Blocks>>, A.2.4 CFB through SM4Key<CFB>, A.2.5 CTR through a reference built from the Sec 8.7.1 equations which SM4Key<CTR> is held to, and the Sec 8.5.2 equations at s = 8 for SM4Key<CFB8>.
  • RFC 8998 Appendix A: the SM4-GCM and SM4-CCM vectors through SM4Key<GCM> and SM4Key<CCM<12, 16>>; Wycheproof sm4_gcm_test.json (65 of 104 cases dispatchable, the 96-bit-IV ones) and sm4_ccm_test.json (162 of 184, those within SP 800-38C A.1's nonce and tag sets) the same way. The CLI tests pin the A.1 GCM and A.2 CCM vectors and a Wycheproof case through the binary in both directions.
  • Shared frameworks over the key: TestFrameworkFromKeyMaterial for every mode, and the block, symmetric and stream suites.
  • cargo test --workspace: all passing; cargo test --workspace --all-targets --no-run builds the benches; cargo +nightly fmt --check clean. 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-sm4 after the review fixes: 395 mutants, 368 caught, 23 unviable, 4 missed (the XOR/OR equivalences in ortho::swap, sbox and tau); the key.rs misses of the review are gone.

AI Usage Statement

  • Yes, non-trivial code changes were generated by AI

Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-mythos-5-1

🤖 Generated with Claude Code

@ounsworth

ounsworth commented Sep 19, 2026 •

Copy link
Copy Markdown
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.

@ounsworth ounsworth closed this Sep 19, 2026
@dghgit dghgit reopened this Sep 19, 2026
@hubot
hubot deleted the feature/sm4 branch September 28, 2026 15:21
@hubot
hubot deleted the branch release/0.1.3alpha September 28, 2026 15:21
@hubot
hubot restored the feature/sm4 branch September 28, 2026 15:45
@dghgit
dghgit changed the base branch from feature/xof-cshake to feature/simple-ciphers September 28, 2026 15:49
@dghgit
dghgit changed the base branch from feature/simple-ciphers to feature/key-rework-memory October 25, 2026 05:55
@dghgit
dghgit force-pushed the feature/sm4 branch 2 times, most recently from 01c2fe1 to b907b3c Compare October 25, 2026 07:36
@dghgit
dghgit force-pushed the feature/key-rework-memory branch from 44855df to 9c343a5 Compare October 25, 2026 07:54
@dghgit
dghgit requested a review from roy-basmacier October 27, 2026 14:41
@roy-basmacier

Copy link
Copy Markdown

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

  1. Key roles (intended scope; cleanup incomplete). SM4Key carries only the symmetric and block
    pairs by design: commit 8df9e35 removed the AEAD, ECB and MAC pairs because they "were there for
    parity with AESKey only", and the PR description says so. So an inert key under GCM, CCM, CMAC
    or ECB<Blocks> is expected. Two things remain:
    • Leftover code (should fix). as_cipher_key and the _ => arm in from_key_material exist
      only for MAC-mode keys, which no longer have a role; all four missed key.rs mutants are there.
      Delete them (or test them if they are meant to stay).
    • Scope vs the Wycheproof rule. QUALITY_AND_STYLE requires Wycheproof tests;
      the only SM4 Wycheproof vectors in bc-test-data are sm4_gcm_test.json (104) and
      sm4_ccm_test.json (184), and those are the RFC 8998 TLS 1.3 suites the PR already proves through
      the hazmat traits. Accepting the four-mode scope means accepting that gap; restoring the AEAD
      roles (plus aliases, Wycheproof suites, CLI) closes it.
  2. CLI contract. sm4-cbc is CBC<NoPadding> (whole blocks only) with no --iv/--iv-file;
    on this base aes128-cbc is PKCS7, any length, with --iv/--iv-file, and the stream commands have
    --iv too. The sm4-cbc help says "See aes128-cbc for the IV convention, block-alignment requirement"
    — neither is true of aes128-cbc any more. The helpers already take Option<[u8; N]> (the SM4 commands
    pass None). Recommend matching AES; the CLI tests could then pin encryption against A.2.2/A.2.4.

Should fix (style guide)

  1. lib.rs:208 heading must be # 🚨 Security Considerations 🚨; fix the anchor at lib.rs:15.
  2. Memory Usage (lib.rs:191–206) has no measured per-entry-point stack table; AES tabulates new,
    encrypt_block, encrypt_2blocks, encrypt_4blocks. Measure and tabulate the same way.
  3. Unit tests in src that only test public behaviour (test_alg_name_and_strength, test_lanes_constant,
    test_engine_size_is_exactly_the_round_keys) duplicate alg_name_tests.rs; keep the per-round pins and
    the exhaustive S-box test, move/drop the rest.

Nits

  1. crypto/sm4/Cargo.toml:9 comment is stale (cipher is used by key.rs, ModeNames, all four aliases) and
    lines 11–12 are a dangling comment.
  2. schedule.rs:7 links crate::SM4::decrypt_block (no such item; private module, so rustdoc is silent).
  3. bitslice.rs:31–32 cites bouncycastle-camellia, which is not in the workspace.
  4. gbt32907_tests.rs:11 says src/sm4.rs; it is src/hazmat/sm4.rs.
  5. cli/tests/sm4_*: cite aes_cbc_cli_tests.rs, cli/src/cbc_cmd.rs, cli/src/stream_mode_cmd.rs;
    none exist (AES CLI tests are test_aes_*.sh, helpers are under cli/src/helpers/).
  6. key.rs:6–7 lists the symmetric pair as CFB/CFB8/CTR/CBC; ECB<PKCS7> also works and is tested.
  7. hazmat/sm4.rs:88–90, 121, 131, 139 say ElectronicCodeBook::encrypt_block etc.; those methods are on
    BlockPermutation on this base.
  8. Over-width doc lines: lib.rs:118 (183 cols), :119 (149), and ~25 others >100; rustfmt does not wrap
    comments, and commit c0b19d0 claims a reflow.
  9. PR description: mutants count is 406 after the port, not 374; the new misses are the key.rs path in (1).

Context, not findings

  • Single-block calls fill all four lanes with copies, so CBC and CFB encryption (serial) run at a quarter
    of the 4-block throughput. Inherent to the u16-plane "lowmemory" choice and documented.
  • The two unused dependency warnings in the test log are from base64 and cipher on the base branch.

dghgit added a commit that referenced this pull request Oct 28, 2026
…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
dghgit added a commit that referenced this pull request Oct 28, 2026
…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
dghgit added a commit that referenced this pull request Oct 28, 2026
…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
@dghgit

dghgit commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Review items addressed in d4df9254, a964b864 and 2868bba9 (branch head 2868bba9):

  1. Key roles. as_cipher_key and the _ => arm are gone, and check_key_type with them: the permutation's own validation is the same check for every mode that has a role, and keeping it left a surviving mutant. A scoped cargo mutants on key.rs is 0 missed (was 4). The four-mode scope is unchanged. The Wycheproof gap is closed on that scope: sm4_gcm_test.json (65 of 104 cases, the 96-bit-IV ones) and sm4_ccm_test.json (162 of 184, those within SP 800-38C A.1's nonce and tag sets) run through the GCM / CCM mode traits over the engine, as rfc8998_tests already did, and the rest are counted as unsupported as the AES suites do.
  2. CLI. sm4-cbc is CBC<PKCS7> at any length; all four commands take --iv/--iv-file and -d/--direction like every other cipher command. The CLI tests pin encryption now: sm4-cbc encrypt --iv reproduces A.2.2 (plus the PKCS7 block, computed from the permutation) and sm4-cfb encrypt --iv reproduces A.2.4 exactly.
  3. Heading is # 🚨 Security Considerations 🚨, anchor updated.
  4. Memory Usage tabulates measured per-entry-point stack (new 272 B, one- and four-block 96 B, two-block 112 B). The bench takes the AES bench's measure-closure shape and the figures are the closure frames from the release build's frame-layout remarks plus the return address, rustc 1.97 nightly on x86-64. Note: the AES table's figures do not reproduce by that method on this toolchain (its encrypt_block closure measures 40 B, not 208 B), so the SM4 table states its method rather than matching the AES numbers.
  5. The three public-behaviour unit tests moved to alg_name_tests.rs or went; the per-round pins stay.
    6–13. All fixed: manifest comment, schedule.rs link, camellia citation, src/hazmat/sm4.rs path, CLI test citations (cli/src/helpers/, test_aes_*.sh), ECB<PKCS7> in the key docs, BlockPermutation attributions, every doc line within 100 columns.
  6. cargo mutants -p bouncycastle-sm4 after the changes: 395 mutants, 368 caught, 23 unviable, 4 missed (the XOR/OR equivalences in ortho::swap, sbox and tau). PR description updated.

cargo test --workspace, --all-targets --no-run, rustdoc with warnings denied and rustfmt are clean.

dghgit added a commit that referenced this pull request Oct 28, 2026
…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
dghgit added a commit that referenced this pull request Oct 28, 2026
…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
dghgit added a commit that referenced this pull request Oct 28, 2026
…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
dghgit added 11 commits November 3, 2026 23:57
…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
…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
Roy's follow-up on PR #140: the table reported only each entry point's
`measure` closure frame, while the AES table it claims to follow sums the
deepest chain below the entry point. The disassembly of the remark build
shows the closure calling encrypt_4blocks (or new) out of line, which calls
tau, which calls ortho, so the chain is four frames: 704 B for new, 416 B
for the one- and four-block calls and 432 B for the two-block ones, return
addresses included, on rustc 1.97 nightly x86-64. The bench's method note
says to sum the chain and lists each frame.

Assisted-by: Claude:claude-mythos-5-1
…on does

Roy's follow-up on PR #140: d4df925 dropped the check against Mode::KEY_TYPE
on the grounds that the permutation's own check covered every mode with a
role, which left a key under a MAC mode declaring KEY_TYPE = MACKey while
its constructor took cipher material and refused MAC material. The check is
back, as AESKey has it: cipher material under CMAC is refused for its type,
MAC material by the permutation, so no material passes and generate fails
the same way; mode_key_tests pins all three. Scoped mutants on key.rs: 19
tested, none missed.

Assisted-by: Claude:claude-mythos-5-1
GCM over SM4 was tested on this branch but reachable only through the hazmat
mode traits, since the key carried no AEAD role. SM4Key now forwards the
AEAD role pair to the GCM mode impls as AESKey does, SM4_GCM names the
worker beside the four unauthenticated aliases, and the crate docs give the
mode as RFC 8998 Sec 3.5.1's AEAD_SM4_GCM, quoting its 12-byte nonce and
16-byte tag from the downloaded text. The RFC's A.1 vector and the Wycheproof
SM4-GCM suite now run through the key, the latter with the seeded-RNG
technique the AES suite uses; A.2 and the CCM suite stay on the mode traits.
mode_key_tests adds the construction policy for GCM, the shared AEAD and
tagged-layout suites, the fresh-nonce check and the key sizes the docs quote,
144 bytes with the hash subkey.

The CLI gains sm4-gcm on the aes128-gcm contract through the shared AEAD
helpers, and its tests hold it to the A.1 vector with the vector's nonce
supplied, to the library's own key, and to the tag on tampering and on a
changed or missing AAD. The 65 Wycheproof cases, the crate's doctests and
the workspace pass; rustdoc with warnings denied is clean for sm4 and cli.

Assisted-by: Claude:claude-mythos-5-1
…t on the items, and the CTR limit is pinned through a key

Two of the aria review's findings hold for this crate too. The cbc, cfb,
cfb8, ctr, gcm and key modules are private, so the docs each carried -- the
construction, the examples, the CTR nonce note, the key's roles and Memory
Usage -- never rendered; they now sit on `SM4_CBC`, `SM4_CFB`, `SM4_CFB8`,
`SM4_CTR`, `SM4_GCM` and `SM4Key`, the one path each has, and the module
headers say so. And no test reached `Ctr`'s counter limit through an SM4
key: stream_mode_tests now does, under `CTR<15>`, where the 256th block is
the last and the byte after it is refused.

Assisted-by: Claude:claude-mythos-5-1
… over the rebase onto its release-based history

This branch took feature/key-rework-memory by merge, and the merge commits carried the
adaptations to what arrived. The rebase onto the branch's new history (key-rework-memory
rebased onto release/0.1.3alpha, which carries feature/key-rework as one squash commit)
dropped those merges; this commit restores the affected files to the branch's pre-rebase
tip, so the tree is identical to it.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
…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
dghgit added a commit that referenced this pull request Nov 3, 2026
The same undercount Roy found on PR #140's SM4 table: the figures were each
entry point's `measure` closure frame alone. The disassembly of the remark
build shows the key-expansion closures calling the engine's new out of
line, which calls the schedule's expand, and the one-block closures calling
encrypt_block / decrypt_block; the two- and four-block entry points are
inlined into their closures, so those rows stand. Summing the chains with
return addresses, on rustc 1.97 nightly x86-64: new 1304 B for the
three-key engine and 920 B for the two-key one, the one-block calls 104 B.
The bench's method note says to sum the chain and lists each frame, and
its `[ENCRYPT]` is no longer read as a rustdoc link.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
As for SM4 on PR #140: 5f36789 dropped the check against Mode::KEY_TYPE,
leaving a key under a MAC mode declaring KEY_TYPE = MACKey while its
constructor took cipher material and refused MAC material. The check is
back on both bundle lengths, as AESKey has it; mode_key_tests pins the
constant, both materials and generate for the three-key and two-key forms.
Scoped mutants on key.rs: 27 tested, 5 caught, 22 unviable, none missed.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
…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
dghgit added a commit that referenced this pull request Nov 3, 2026
…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
dghgit added a commit that referenced this pull request Nov 3, 2026
The same undercount Roy found on PR #140's SM4 table: the figures were each
entry point's `measure` closure frame alone, while the disassembly of the
remark build shows the closure calling encrypt_4blocks (or the schedule's
expand) out of line, which calls f, which calls ortho. Summing the chain
with return addresses, on rustc 1.97 nightly x86-64: new 720 B for
Camellia-128 and 1120 B for the other two, 496 B for the one-block calls,
512 B for the two-block calls and decrypt_4blocks, 528 B for
encrypt_4blocks. The bench's method note says to sum the chain and lists
each frame.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
…utation does

As for SM4 on PR #140: 6327570 dropped the check against Mode::KEY_TYPE,
leaving a key under a MAC mode declaring KEY_TYPE = MACKey while its
constructor took cipher material and refused MAC material. The check is
back on all three key lengths, as AESKey has it; mode_key_tests pins the
constant, both materials and generate at 128 and 256 bits. Scoped mutants
on key.rs: 28 tested, 5 caught, 23 unviable, none missed.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
…modules are public, the stack table is measured, and the doc nits

PR #142's review, and the round PR #140 and #141 had before it. ARIAKey's
three constructors carried as_cipher_key and a `_ =>` arm for MAC-mode keys,
which lost their role when the AEAD, ECB and MAC pairs went; both go, and
check_key_type stays ahead of the permutation's own check so that a key under
a MAC mode refuses cipher material for its type and MAC material by the
permutation, which mode_key_tests pins at both key lengths. The key docs name
every mode that gives a key a role -- CTR at any nonce length, ECB<PKCS7> and
ECB<NoPadding> among them -- rather than the four aliased ones.

The mode and key modules are `pub`, as in bouncycastle-aes, so their docs
render; the three params types are exported beside their trait, so the
`ARIA*Key<M>` aliases name public types. The ModeNames comment says which
modes RFC 5794 Appendix B gives an OID (CFB's parameters carry the segment
size, so CFB8 is `id-aria*-cfb` with `k = 8`; GMAC has none). 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_aria_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 deepest frame chain under each closure from the release build's
frame-layout remarks and disassembly (closure, then encrypt_4blocks or
expand, fo/fe, substitution_layer, ortho), which puts a one-block call at
416 B, not the "near 100 bytes" the Design section foregrounded for the
working state alone; that sentence now points at the table.

The three unit tests in hazmat/aria.rs that tested public behaviour only (the
engine sizes, the algorithm names, the LANES constant) move to
alg_name_tests; the Sec 2.2 decryption-order pin and the Appendix A.1
intermediate values stay. stream_mode_tests pins the CTR counter limit
through an ARIA key under CTR<15>. Nits: the manifest comment, the sibling
citations in bitslice.rs, the BlockPermutation attributions for methods that
are on BlockPermutation, two clippy suggestions in the tests, and every doc
or comment line past 100 columns.

Assisted-by: Claude:claude-mythos-5-1
dghgit added a commit that referenced this pull request Nov 3, 2026
… and -d

As for sm4 and camellia on PRs #140 and #141: aria*-cbc was CBC<NoPadding>
with no --iv while aes*-cbc on this base is PKCS7 at any length with
--iv/--iv-file, and the aria help pointed at aes128-cbc for an alignment rule
it no longer has (PR #142 review). 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: aria*-cbc encrypt --iv reproduces the KISA CBC vectors followed by
the PKCS7 block (computed from the permutation of the key's length), and
aria*-cfb, -cfb8 and -ctr encrypt --iv reproduce the KISA stream vectors
exactly, CTR under the twelve-byte zero nonce they imply. 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
@dghgit
dghgit changed the base branch from feature/key-rework-memory to release/0.1.3alpha November 3, 2026 12:59
@hubot
hubot deleted the feature/sm4 branch November 3, 2026 13:17
@hubot
hubot deleted the branch release/0.1.3alpha November 3, 2026 13:17
@hubot
hubot restored the feature/sm4 branch November 3, 2026 14:53
…N, T>> signs and opens a message of any length as SM4Key<GCM> does -- d4fc1f9 had dropped it with the ARIA trim; RFC 8998 Appendix A.2 and the Wycheproof SM4-CCM suite (162 cases) now run through the key rather than over the permutation, SM4_CCM names the fixed-frame workers with CCM_NONCE_LEN and CCM_TAG_LEN for AEAD_SM4_CCM's 12 and 16, the mode-key suite adds the CCM_Packet key, and the docs say the key covers GCM, CCM and CCM_Packet

Assisted-by: Claude:claude-fable-5-1
…nd its key table are shared with the AES file), with RFC 8998 Appendix A.2 and a Wycheproof known answer in both directions, tag-check and round-trip tests

Assisted-by: Claude:claude-fable-5-1
…feature/sm4: the CLI dispatch keeps this branch's SM4 arms beside the release's rewritten SP 800-185 arms, the shared CCM runner stays pub(crate) under the release's clippy allowance, and the memory-bench manifest and module list carry both new binaries

Assisted-by: Claude:claude-fable-5-1
@dghgit

dghgit commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Merged release/0.1.3alpha at 029a17b (the squash of PR #190) into this branch as bc764f2: the CLI dispatch, the shared CCM runner's visibility and the memory-bench manifest were the conflicts; workspace suite passes.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants