Repository navigation
Conversation
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. |
dghgit
force-pushed
the
feature/aria
branch
from
September 28, 2026 10:36
814cc36 to
d51a3d3
Compare
dghgit
changed the base branch from
feature/xof-cshake
to
feature/simple-ciphers
September 28, 2026 15:49
…from bc-java
New crate `crypto/aria` (`bouncycastle::aria`): the ARIA
block cipher (RFC 5794, KS X 1213) with 128-, 192- and 256-bit keys as a raw
keyed permutation implementing ElectronicCodeBook -- ARIA_128 / ARIA_192 /
ARIA_256 over a sealed ARIAParams -- ported from BC Java's ARIAEngine and
commented line by line against the RFC.
The four S-boxes are Boolean circuits over eight u16 bit-planes rather than
the Java engine's 256-byte tables, so there is no secret-indexed memory access
in the cipher or the key schedule. SB1 is the AES S-box and uses the 113-gate
Boyar-Peralta program verbatim from aes; SB2 (119 gates), SB3 (124)
and SB4 (122) are GF(2^8) inversion between affine maps -- decompositions found
by exhaustive search against the RFC's tables, each with exactly one admissible
input constant (0x00, 0x63, 0xe2) -- around the same Boyar-Peralta non-linear
section, with generated affine layers chosen among the 2040 equivalent forms
for the fewest gates. A substitution layer sends four bytes of each block
through each S-box (one column of the 4x4 byte matrix, transposed out with two
masked-swap stages) and each circuit substitutes 16 bytes, so four blocks fill
the four passes exactly: encrypt_4blocks/decrypt_4blocks are the natural unit,
the trait's blocks8 is two passes and blocks2 uses two lanes. The per-call
working state is about 100 B, which is the "lowmemory" in the name.
The diffusion layer A is computed in the word-level form of the 32-bit
implementations (per-word byte parity, six word XORs, three byte permutations,
six word XORs), a decomposition verified against the RFC's sixteen equations on
every unit vector and 1000 random blocks rather than recalled. One stored
schedule (13/15/17 round keys, 208/240/272 B, in a Secret) serves both
directions: the Sec 2.2 decryption keys dk_i = A(ek_{n+2-i}) are derived from
the stored encryption keys as each round needs them.
Also: ARIA_CBC_128/192/256<Dir> aliases over bouncycastle-modes; aria128-cbc /
aria192-cbc / aria256-cbc CLI subcommands sharing cbc_cmd.rs; criterion bench;
mem_usage_benches harness; umbrella re-export; release notes.
Verified against RFC 5794 Appendix A (the three ciphertexts in every lane of
the four-block path, every slot of the eight-block path and both pair slots;
for the 128-bit key all thirteen round keys ek1..ek13 and all eleven
intermediate values P1..P11 against a literal round-by-round transcription of
Sec 2.3.1.1); KISA's published test vectors as carried in OpenSSL's
evpciph_aria.txt (a 10-block message per key length, ECB through every batching
and CBC through the aliases, both directions); the vector, round-trip and
S-box-inverse checks of BC Java's ARIATest; a transcription of ARIAEngine kept
in the tests (byte arrays, 64-bit halves, table S-boxes fused with the
diffusion layer by multiply-broadcast), which the engine must agree with on
thousands of keys and blocks of every length in every lane and both directions;
ElectronicCodeBook conformance for all three types; and the CLI against the
KISA vectors.
cargo mutants: 1213 mutants, 1191 caught, 6 unviable, 16 missed -- all sixteen
proven |/^ equivalences on disjoint bits or the documented Boyar-Peralta t37
gate in each of the four circuits, commented at the site.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…Mode projection, and the block-aligned vectors move onto modes::Cbc
…valid, driving the padding crate's constant-time unpad with adversarial input
…rmutation, and Block and the concrete params types are no longer exported
Doc comments scattered per-file citations to BC Java's ARIAEngine (its table-driven S-boxes, its per-direction key layout, the C constant array) throughout lib.rs, schedule.rs, aria.rs and sbox.rs. The crate's Provenance section already names it as the source engine this crate is ported from and describes what it reproduces; the scattered comparisons are replaced with self-contained descriptions of the same points 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-aria passes (16 passed). 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>
…r aria{128,192,256}-{cfb,cfb8,ctr} CLI commands, so ARIA 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 one line of type plus the const parameters ARIA fills in (16/24/32-byte keys, a 16-byte block, 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). RFC 5794 publishes only single-block values (Appendix A), but KISA's own mode vectors -- the ARIA-*-CFB, ARIA-*-CFB8 and ARIA-*-CTR entries of OpenSSL's evpciph_aria.txt (3.6.2), which the existing ECB and CBC tests already draw on -- cover all three new modes at all three key lengths, and unlike Camellia's and SM4's they line up with these aliases exactly: the CFB128 and CFB8 vectors share the IV 0f1e2d3c..f0 that the CBC vectors use, and the CTR vectors start from an all-zero counter block whose NextIV is ..0a after the ten-block message, which is precisely what a twelve-byte zero nonce and a counter starting at zero produce. All nine are therefore straight known-answer tests through the aliases, each encrypted under the vector's own init data via a FixedSeedRNG (there is no API for supplying one, and the test asserts what came back before comparing any ciphertext), in one call, block by block, and decrypted back seven bytes at a time. stream_mode_alias_tests.rs covers the wiring the vectors do not: that each of the nine aliases 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 nine 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, and their tests replay the same KISA vectors end to end through the pipe. No new mutants: `cargo mutants -p bouncycastle-aria --list` reports the same 1211 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 aria goes 2 -> 46, 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>
…ipe on the error paths -- the identical harness on feature/sm4 failed the Rust Tests workflow, and these three suites carry the same latent race. `a_key_of_the_wrong_length_is_rejected` 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; it happened to win the race on this branch's CI run and lose on sm4's. 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. Because the race is timing-dependent, aria_cfb8_cli_tests.rs also gains a deterministic guard: a 4 MiB payload to `aria128-cfb8` with a rejected key cannot fit in a pipe buffer, so the write is certain to get EPIPE rather than merely likely to. No behaviour outside the test harness changes. Assisted-by: Claude:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tream-mode suites got in 3ee8c7b, 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 `aria128-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 aria_cfb_cli_tests.rs, aria_cfb8_cli_tests.rs and aria_ctr_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>
…which 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>
…absorbed 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 814cc36 with 2d4d038 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s tests, stream-mode tests, KISA raw-Cbc vector tests 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 ARIA_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>
ARIA and its three key-length aliases move to bouncycastle_aria::hazmat with BLOCK_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>
…:Select Follows aes 160ac18; the padding crate's PaddedMode copy goes with it. Type aliases 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 CLI 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 aria crate, its CLI commands and tests and its memory bench exactly as the branch tip 1c2b7da had them, and the twelve aria subcommands wired back into main.rs, whose conflicts the replay resolved to the base. Assisted-by: Claude:claude-mythos-5-1
…s, the engines become ARIA128Internal / ARIA192Internal / ARIA256Internal The engines are renamed for what they are under the key objects of feature/key-rework-memory: ARIA<P> -> ARIAInternal<P> with ARIA128Internal, ARIA192Internal and ARIA256Internal for the three key lengths, Clone, each implementing ElectronicCodeBook (new) and BlockPermutation (the block methods) separately, and ModeNames for all nine modes, since RFC 5794 Appendix B assigns ARIA an identifier in each of them. ARIAKey<P: ARIAParams, M: Mode<ARIAInternal<P>>> mirrors AESKey: the key holds the mode's key state, FromKeyMaterial<16> / <24> / <32> build it per key length, 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. ARIA128Key<M> and friends name the key lengths. The crate aliases CBC, CFB, CFB8 and CTR as before, now with the key-state lifetime (ARIA_CBC_128<'k, Dir, Pad>, ...); the other modes are reached by naming them, and GCM is exercised through the AEAD framework. 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 keys; the KISA, Wycheproof and stream-mode vectors drive the keys and the RFC 5794, bc-java and reference suites the engines. CLI: the twelve aria commands load an ARIA*Key and keep their contract -- whole blocks under NoPadding for CBC, the IV or nonce in band, no --iv -- and their integration tests replay the KISA vectors through the binary unchanged. Assisted-by: Claude:claude-mythos-5-1
dghgit
changed the base branch from
feature/simple-ciphers
to
feature/key-rework-memory
October 24, 2026 12:47
hubot
force-pushed
the
feature/aria
branch
2 times, most recently
from
October 24, 2026 17:46
1c2b7da to
f778457
Compare
…and MAC pairs go ARIAKey<P, 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, checks against a published vector 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 ARIA_* 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. ModeNames keeps all nine names, since RFC 5794 Appendix B assigns ARIA an identifier in each and the mode types over the engine name themselves by it. 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
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-aria: a constant-time, table-free, low-memory ARIA block cipher (RFC 5794), ported from Bouncy Castle Java'sARIAEngine, on the key objects of #172:ARIA128Key<M>,ARIA192Key<M>andARIA256Key<M>over the enginesARIA128Internaland friends.Rebased onto
feature/key-rework-memory(#172) on 2026-10-24 and ported to its key-object design; the base is now that branch.Description
ARIAKey<P, M>mirrorsAESKey: 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 (ARIA_CBC_128<'k, Dir, Pad>,ARIA_CFB_128<'k, Dir>, ...); the engines implementModeNamesfor all nine modes, since RFC 5794 Appendix B assigns ARIA an identifier in each, so the mode types over the engines are named; the keys carry the symmetric and block role pairs only, which cover those four modes, and 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.aria{128,192,256}-{cbc,cfb,cfb8,ctr}, through the key objects, keeping their contract: whole blocks underNoPaddingfor CBC, the IV or nonce in band, no--iv.Scope and Risk
New crate only; the shared changes it carries (the
bouncycastle-ariaworkspace entry, the umbrella re-export, the memory bench) are additive. No behavioural change to AES or other existing ciphers.Validation
ARIATestvectors, through the engines; OpenSSL'sevpciph_aria.txt(KISA's published vectors) for ECB through the engines and CBC, CFB128, CFB8 and CTR through the keys; Wycheproof CBC-PKCS5 (216 cases, 144 invalid) throughARIA*Key<CBC<PKCS7>>.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 replay the KISA vectors through the binary.cargo mutants -p bouncycastle-ariaon the pre-port revision: 1211 mutants, 1189 caught, 6 unviable, 16 missed (XOR/OR equivalences). Not re-run after the port; the engine code is unchanged by it.AI Usage Statement
Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-mythos-5-1
🤖 Generated with Claude Code