Part 5: AES tweaks: AES_CBC_* aliases, flat-array one-shots and streaming, simpler CLI - #109
Open
dghgit wants to merge 6 commits into
Open
Part 5: AES tweaks: AES_CBC_* aliases, flat-array one-shots and streaming, simpler CLI#109dghgit wants to merge 6 commits into
dghgit wants to merge 6 commits into
Conversation
One alias per AES key length over bouncycastle_modes::Cbc, generic in the direction marker, so callers never spell out the KEY_LEN / BLOCK_LEN const parameters. They live in the AES crate (new cbc.rs module) because the modes crate is deliberately cipher-agnostic; this adds bouncycastle-modes as a dependency of aes-lowmemory, leaving modes' dev-dependency on aes-lowmemory as a Cargo-permitted dev-dep cycle. Each alias carries a doctest exercising both directions, since Rust only checks an alias's bounds at a use site, and the crate docs gain a CBC usage example. Naming follows the HMAC_SHA256 / HKDF_SHA256 convention, with the same non_camel_case_types allowance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…provided The one-shots on BlockCipherEncryptor / BlockCipherDecryptor -- encrypt, encrypt_rng, encrypt_out, encrypt_out_rng and decrypt, decrypt_out -- now take a `[u8; LEN]` instead of an array of blocks. They replace the block-shaped encrypt_blocks / encrypt_blocks_rng / encrypt_blocks_out / encrypt_blocks_out_rng and decrypt_blocks / decrypt_blocks_out from #96, which nothing outside tests and docs used and which the flat form makes redundant (0.1.3 is unreleased, so nothing shipped changes). LEN must be a whole number of blocks and this is enforced at compile time: an inline `const { assert!(LEN % BLOCK_LEN == 0) }` fails at the call site that instantiates a misaligned LEN, so no runtime length check and no error variant. Inside, LEN / BLOCK_LEN is not nameable without generic_const_exprs, so the shared helpers walk the buffer with as_chunks -- pairs first, so a mode's two-block path is used, then the at-most-one remaining block. The by-value streaming methods do_{en,de}crypt_blocks are now provided in terms of their _out forms, shrinking the implementor contract to init[_rng] and do_*_blocks_out. Tests and docs updated to the flat form: the core-test-framework suite covers the single-block flat forms (all it can form generically); modes/tests/cbc_tests checks 3- and 4-block flat arrays agree byte-for-byte with the streaming API in both directions and via _out; sp800_38a_tests runs the F.2 decrypt vectors through the flat one-shot; the AES_CBC_* docs gain flat and streaming examples and a compile_fail doctest for a 47-byte array. Release note updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lify the CLI
BlockCipherEncryptor / BlockCipherDecryptor gain provided flat streaming
methods -- do_encrypt<LEN> / do_encrypt_out<LEN> and do_decrypt<LEN> /
do_decrypt_out<LEN> over a `[u8; LEN]`, with the same compile-time
LEN % BLOCK_LEN == 0 assertion as the one-shots, which now delegate to them.
The by-value do_{en,de}crypt_blocks<N> are removed. The one block-shaped
method left is the implementor hook do_{en,de}crypt_blocks_out<N> over
[[u8; BLOCK_LEN]; N]: it is what guarantees an implementation never sees a
partial block and that in/out lengths agree at compile time, and it serves
runtime-count tails one block at a time. An implementor now writes only
init[_rng] and that hook; Cbc and the padding test toy drop their by-value
wrappers, and the padding adapter's two single-block calls use do_encrypt /
do_decrypt.
CLI: aes*-cbc stream stdin into a flat 1 KiB buffer and hand full chunks to
do_*_out::<1024>, the whole-block tail to do_*::<16>; the block staging
buffer and partial-block carry logic are gone (reads simply accumulate until
the buffer is full). Behaviour and error messages unchanged; the 16 CLI
subprocess tests still pass.
Tests: framework, modes (cbc, sp800-38a, acvp) and benches moved to the
flat methods; block-structured tests use two small helpers over the hook.
Release notes updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
BlockCipher declared only MAX_SECURITY_STRENGTH, which Algorithm already has, so every implementor of BlockPermutation (which also implemented Algorithm) had two copies of the same constant and had to qualify every use of it. BlockPermutation, BlockCipherEncryptor and BlockCipherDecryptor are now bounded on Algorithm instead, and Cbc implements Algorithm with its permutation's ALG_NAME and MAX_SECURITY_STRENGTH. Raised in review of PR #107. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A block cipher mode never changes the length of its data, so a separate output buffer was only ever a copy, and a copy of plaintext is one more thing to scrub. The one-shots, the flat streaming methods and the implementor hook now all take a single `&mut [u8; LEN]` / `&mut [[u8; BLOCK_LEN]; N]` and transform it in place; `encrypt` and `encrypt_rng` return just the init data. The `_out` variants and the `usize` byte counts (always LEN) are gone. The compile-time `LEN % BLOCK_LEN == 0` check is unchanged. The data methods keep a `Result` for modes with a per-initialization data limit (counter-based modes); CBC never fails them, and the docs say so. Cbc, PaddedEncryptor/PaddedDecryptor, the test framework, the modes tests and benches, the doc examples and the CLI follow. The padding layer pads and encrypts the final block inside its `Secret`, so only ciphertext is ever copied out of it. Raised in review of PR #107. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A pure reordering (verified: the sorted non-blank lines are identical before and after). Each trait keeps its doc comment and any todo notes attached to it; SecurityStrength keeps its two impl blocks. Sorted case-insensitively by item name. Requested in review of PR #107. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Follow-ups to the block-cipher stack, stacked on #97 (padding). Three commits:
aes-lowmemory:AES_CBC_128/AES_CBC_192/AES_CBC_256type aliases overbouncycastle_modes::Cbc, generic in the direction marker, so callers never spell out theKEY_LEN/BLOCK_LENconst parameters (AES_CBC_128::<Encrypting>::encrypt(&key, &msg)). They live in the AES crate because the modes crate is deliberately cipher-agnostic;aes-lowmemorygains a dependency onmodes(modes keeps its dev-dependency onaes-lowmemory-- a Cargo-permitted dev-dep cycle, verified by the modes test suite). Naming followsHMAC_SHA256/HKDF_SHA256. Each alias has a two-direction doctest, since Rust only checks an alias's bounds at a use site.core: the block-cipher one-shots take a flat[u8; LEN].encrypt/encrypt_rng/encrypt_out/encrypt_out_rnganddecrypt/decrypt_outreplace the block-shapedencrypt_blocks*/decrypt_blocks*from core: split BlockCipher into block-aligned BlockCipherEncryptor/Decryptor #96.LEN % BLOCK_LEN == 0is enforced at compile time by an inlineconstassertion at the instantiating call site -- no runtime length check and no error variant; a 47-byte array is acompile_faildoctest. InternallyLEN / BLOCK_LENis not nameable withoutgeneric_const_exprs, so the buffer is walked withas_chunks: pairs first (so a mode's two-block path is used), then the at-most-one remaining block.core: flat streaming methods replace the by-value block methods; simpler CLI.do_encrypt<LEN>/do_encrypt_out<LEN>anddo_decrypt<LEN>/do_decrypt_out<LEN>are provided; the by-valuedo_*_blocks<N>are removed. The one block-shaped method left is the implementor hookdo_*_blocks_out<N>over[[u8; BLOCK_LEN]; N]-- it guarantees an implementation never sees a partial block and that in/out lengths agree at compile time, and serves runtime-count tails. An implementor now writes onlyinit[_rng]and that hook. The CLI'saes*-cbccommands stream stdin into a flat 1 KiB buffer (do_*_out::<1024>for full chunks,do_*::<16>for the whole-block tail); the block staging buffer and partial-block carry logic are gone. Behaviour and error messages unchanged (16 CLI subprocess tests).None of this has shipped (0.1.3 is unreleased), so the removals are not breaking changes; the #96 release-note bullet is updated to describe the final API. Verified:
cargo fmt --checkclean, full workspace green (92 suites / 586 tests), benches compile, zero rustdoc warnings.Merge after #97. Land by pushing to
origin(GitHub is a mirror).