Uh oh!
There was an error while loading. Please reload this page.
Add Padding trait and bouncycastle-padding crate (PKCS7, Padded{En,De}cryptor) - #97
Open
dghgit wants to merge 1 commit into
Open
Add Padding trait and bouncycastle-padding crate (PKCS7, Padded{En,De}cryptor)#97dghgit wants to merge 1 commit into
dghgit wants to merge 1 commit into
Conversation
…}cryptor)
core:
- Add `Padding<const BLOCK_LEN>` trait with in-place `pad(block, data_len)`
and constant-time `unpad(block) -> data_len`.
- Add `PaddingError { DataLengthTooLong, InvalidPadding }` and a
`PaddingError` variant (with From) on `SymmetricCipherError`.
crypto/padding (new crate, no_std, no unsafe):
- `PKCS7`: RFC 5652 §6.3 padding for any block length 1..=255, enforced
at compile time. `unpad` examines every byte with `Condition<i64>`
mask arithmetic and has a single public decision point, so it does
not leak a padding oracle through timing or error detail.
- `PaddedEncryptor<E, P>` / `PaddedDecryptor<D, P>`: adapt a
block-aligned BlockCipherEncryptor / BlockCipherDecryptor to
arbitrary-length data. Streaming `do_update_out` / `do_final(self)`
plus one-shot `encrypt_out` / `decrypt_out`, with exact output-length
helpers. The buffered partial plaintext block is held in a `Secret`.
The decryptor withholds one complete block until `do_final`, since
only the last block carries padding.
- Tests derived from the RFC 5652 rule for PKCS7; adapter tests drive
the code with a toy XOR-CBC cipher implementing the new block cipher
traits, covering every length, ten chunkings in both directions,
tampering, malformed lengths, and buffer sizing. Criterion bench.
- Registered in the workspace and re-exported as `bouncycastle::padding`.
core-test-framework:
- Fix the security-strength loops in the symmetric/block/AEAD suites:
`set_security_strength` refuses strengths the key length cannot
support even inside `do_hazardous_operations`, so the previous
unwrap panicked for any key shorter than 32 bytes. Skip those
strengths instead and correct the misleading comment.
Co-Authored-By: Claude Fable 5 <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 freeto 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.
Note this also includes a mock cipher to test with. We should replace that with AES when the actual implementation arrives.
Claude Summary follows:
Stacked on #96 (
feature/block-cipher-to-multi); will retarget torelease/0.1.3alphaonce that merges. Adds the padding layer that the block-alignedBlockCipherEncryptor/BlockCipherDecryptortraits deliberately leave out.corePadding<const BLOCK_LEN>trait: in-placepad(block, data_len)and constant-timeunpad(block) -> data_len.PaddingError { DataLengthTooLong, InvalidPadding }, plus aPaddingErrorvariant (withFrom) onSymmetricCipherError.crypto/padding(new cratebouncycastle-padding,no_std, nounsafe)PKCS7— RFC 5652 §6.3 padding for any block length1..=255, enforced at compile time via an inlineconstassert.unpadvisits every byte withCondition<i64>mask arithmetic frombouncycastle-utilsand has a single public decision point, so neither timing nor error detail leaks a padding oracle. Spec text was taken from the downloaded RFC, not recall.PaddedEncryptor<E, P>/PaddedDecryptor<D, P>— adapt anyBlockCipherEncryptor/BlockCipherDecryptorto arbitrary-length data. Streamingdo_update_out/do_final(self)with exactupdate_out_lenhelpers, and one-shotencrypt_out/decrypt_out. Whole blocks go to the inner cipher in groups viaas_chunks(no unwraps, no copies); the buffered partial plaintext block lives in aSecret. The decryptor withholds one complete block untildo_final, since only the last block carries padding.KeyType— and run it throughTestFrameworkBlockCipher.bouncycastle::padding.core-test-frameworkfixRunning the framework against the toy cipher exposed a latent bug: the security-strength loops in the symmetric/block/AEAD suites
unwrappedset_security_strength, whose comment claimeddo_hazardous_operationsbypasses the key-length guard. It doesn't (key_material.rs), so any key under 32 bytes panicked the suite. The loops now skip strengths the key cannot carry and the comment is corrected.Not included
No CLI subcommand — there is no real block cipher to wire it to yet; it belongs with the first AES/CBC crate.
Verification
cargo test --workspace,cargo doc(no broken links),cargo fmt --check,cargo bench -p bouncycastle-padding --no-runall clean.quality_stats.sh ./crypto/padding: 322 code / 122 doc / 347 test lines.