Repository navigation
AES Key Wrap (KW) and Key Wrap with Padding (KWP), SP 800-38F, on the key-object path (#150) - #160
Closed
roy-basmacier wants to merge 18 commits into
Closed
roy-basmacier wants to merge 18 commits into
roy-basmacier wants to merge 18 commits into
Conversation
dghgit
force-pushed
the
feature/150-key-wrap
branch
from
October 26, 2026 18:39
339868e to
80c25ff
Compare
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Oct 26, 2026
…KeyWrapMode/KeyUnwrapMode and the KeyWrappingKey/KeyUnwrappingKey roles The traits from PR bcgit#160 took the KEK as a KeyMaterial on every call. On the key path the KEK is validated once, when the key object is made, so the pair becomes worker traits on the shape of MAC: `&self` methods over a worker that borrows the keyed permutation, with no key parameter and no KeyMaterialError. The mode traits (hazmat) hand the worker out from a key state, and the role traits forward to them with the one-shots provided, as the MAC roles do. The length helpers take `&self` so the role can forward them through a worker. Assisted-by: Claude:claude-mythos-5-1
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Oct 26, 2026
… the keyed permutation The two structs from PR bcgit#160 held a PhantomData and keyed the permutation on every call. They become workers on the shape of Cmac: `Kw<'k, P>` over a `&'k P`, with `KW` / `KWP` as the mode markers a key is bound to, their key state the permutation alone, and KeyWrapMode / KeyUnwrapMode handing the worker out. ModeNames gains KW_ALG_NAME and KWP_ALG_NAME. The toy key gets the two roles, and the structural tests run over it; the framework's tamper checks need diffusion, so they run in `aes`. Assisted-by: Claude:claude-mythos-5-1
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Oct 26, 2026
…ild key objects The six commands load `AES*Key<KW>` / `AES*Key<KWP>` through load_key_object and call the key's one-shots. The direction takes the CipherDirection flag every other cipher command takes, so `-d encrypt` wraps and `-d decrypt` unwraps, instead of PR bcgit#160's positional wrap|unwrap. The Rust subprocess test becomes cli/tests/test_aes_kw.sh on the shell harness the other commands use, with the same cases: the RFC vectors both ways, framing, the length rules, rejection with no output, and key loading. Assisted-by: Claude:claude-mythos-5-1
ounsworth
requested changes
Oct 27, 2026
ounsworth
left a comment
Contributor
There was a problem hiding this comment.
Why is this +65,000 lines, changing 354 files?
I suspect there's a bit branching issue here. Once we finish the release/0.1.3alpha, and fork a new release/0.1.4alpha branch and point this at it, maybe the diff will get smaller?
dghgit
changed the base branch from
feature/simple-ciphers
to
feature/key-rework-memory
October 31, 2026 05:55
dghgit
self-requested a review
October 31, 2026 13:22
dghgit
force-pushed
the
feature/key-rework-memory
branch
from
November 3, 2026 02:36
3c170c9 to
81cef74
Compare
dghgit
added a commit
that referenced
this pull request
Nov 3, 2026
…KeyWrapMode/KeyUnwrapMode and the KeyWrappingKey/KeyUnwrappingKey roles The traits from PR #160 took the KEK as a KeyMaterial on every call. On the key path the KEK is validated once, when the key object is made, so the pair becomes worker traits on the shape of MAC: `&self` methods over a worker that borrows the keyed permutation, with no key parameter and no KeyMaterialError. The mode traits (hazmat) hand the worker out from a key state, and the role traits forward to them with the one-shots provided, as the MAC roles do. The length helpers take `&self` so the role can forward them through a worker. Assisted-by: Claude:claude-mythos-5-1
dghgit
added a commit
that referenced
this pull request
Nov 3, 2026
… the keyed permutation The two structs from PR #160 held a PhantomData and keyed the permutation on every call. They become workers on the shape of Cmac: `Kw<'k, P>` over a `&'k P`, with `KW` / `KWP` as the mode markers a key is bound to, their key state the permutation alone, and KeyWrapMode / KeyUnwrapMode handing the worker out. ModeNames gains KW_ALG_NAME and KWP_ALG_NAME. The toy key gets the two roles, and the structural tests run over it; the framework's tamper checks need diffusion, so they run in `aes`. Assisted-by: Claude:claude-mythos-5-1
dghgit
added a commit
that referenced
this pull request
Nov 3, 2026
…ild key objects The six commands load `AES*Key<KW>` / `AES*Key<KWP>` through load_key_object and call the key's one-shots. The direction takes the CipherDirection flag every other cipher command takes, so `-d encrypt` wraps and `-d decrypt` unwraps, instead of PR #160's positional wrap|unwrap. The Rust subprocess test becomes cli/tests/test_aes_kw.sh on the shell harness the other commands use, with the same cases: the RFC vectors both ways, framing, the length rules, rejection with no output, and key loading. Assisted-by: Claude:claude-mythos-5-1
dghgit
force-pushed
the
feature/150-key-wrap
branch
from
November 3, 2026 02:41
6c866ff to
020dbe6
Compare
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Nov 5, 2026
…KeyWrapMode/KeyUnwrapMode and the KeyWrappingKey/KeyUnwrappingKey roles The traits from PR bcgit#160 took the KEK as a KeyMaterial on every call. On the key path the KEK is validated once, when the key object is made, so the pair becomes worker traits on the shape of MAC: `&self` methods over a worker that borrows the keyed permutation, with no key parameter and no KeyMaterialError. The mode traits (hazmat) hand the worker out from a key state, and the role traits forward to them with the one-shots provided, as the MAC roles do. The length helpers take `&self` so the role can forward them through a worker. Assisted-by: Claude:claude-mythos-5-1
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Nov 5, 2026
… the keyed permutation The two structs from PR bcgit#160 held a PhantomData and keyed the permutation on every call. They become workers on the shape of Cmac: `Kw<'k, P>` over a `&'k P`, with `KW` / `KWP` as the mode markers a key is bound to, their key state the permutation alone, and KeyWrapMode / KeyUnwrapMode handing the worker out. ModeNames gains KW_ALG_NAME and KWP_ALG_NAME. The toy key gets the two roles, and the structural tests run over it; the framework's tamper checks need diffusion, so they run in `aes`. Assisted-by: Claude:claude-mythos-5-1
dghgit
force-pushed
the
feature/150-key-wrap
branch
from
November 5, 2026 01:35
020dbe6 to
3983972
Compare
dghgit
added a commit
to roy-basmacier/bc-rust
that referenced
this pull request
Nov 5, 2026
…ild key objects The six commands load `AES*Key<KW>` / `AES*Key<KWP>` through load_key_object and call the key's one-shots. The direction takes the CipherDirection flag every other cipher command takes, so `-d encrypt` wraps and `-d decrypt` unwraps, instead of PR bcgit#160's positional wrap|unwrap. The Rust subprocess test becomes cli/tests/test_aes_kw.sh on the shell harness the other commands use, with the same cases: the RFC vectors both ways, framing, the length rules, rejection with no output, and key loading. Assisted-by: Claude:claude-mythos-5-1
dghgit
changed the base branch from
feature/key-rework-memory
to
release/0.1.4alpha
November 5, 2026 01:40
Adds the core API for the key-wrap algorithms of NIST SP 800-38F (KW, KWP, TKW; RFC 3394 / RFC 5649), following the discussion on bcgit#150 and the conventions of the cipher traits on this branch. Traits only: an AES-KW/KWP implementation over ElectronicCodeBook in bouncycastle-modes is to follow once the API is agreed. core/traits.rs: - KeyWrapper<KEK_LEN> and KeyUnwrapper<KEK_LEN> as separate traits, so a policy can allow unwrapping existing ciphertexts while forbidding new wrappings, as with the *Encryptor/*Decryptor pairs. - Fixed-length one-shots wrap_key/unwrap_key are provided over the required wrap_key_out/unwrap_key_out. Implementations must reject invalid (KEY_LEN, CT_LEN) pairs with an inline const assertion, so a wrong length is a compile error rather than a runtime Err. - Runtime-length wrap_out/unwrap_out cover KWP over arbitrary data, sized by wrap_out_len (exact) and unwrap_out_max_len (upper bound), mirroring encrypt_out_len/decrypt_out_max_len. OutputBufferTooSmall is checked before any work is done. - Wrap input is plain bytes, since wrapping strips KeyMaterial metadata. Unwrap output is a Secret, so it is zeroized on drop without claiming a KeyType or provenance. - Every unwrap authenticity failure (wrong KEK, tampering, ICV mismatch, bad padding, wrong recovered length) is the same DecryptionFailed, per SP 800-38F Algorithm 6 steps 4, 7 and 8; output buffers are zeroized on error. - The KEK must be a SymmetricCipherKey at least as strong as MAX_SECURITY_STRENGTH. App. A.2's advice that the KEK *should* be as strong as the wrapped key is documented as the caller's responsibility. core/errors.rs: - SymmetricCipherError::InvalidInputLength, for plaintext or ciphertext lengths the algorithm is not defined on (variable-length APIs only). core-test-framework: - New key_wrap module: TestFrameworkKeyWrap::test() exercises round trips, determinism, the length helpers, buffer-size and input-length errors, tamper and wrong-KEK rejection, output zeroization, and KEK type and strength checks. test_kat() is ready for the RFC 3394/5649 vectors. - Checked against an in-file mock (not cryptography) with KWP's length behaviour. Adds a bouncycastle-utils dependency for Secret. Assisted-by: Claude:claude-opus-5-5
Implements the KeyWrapper / KeyUnwrapper traits from the previous commit for the two 128-bit-block key-wrap algorithms of NIST SP 800-38F, KW (Sec 6.2; RFC 3394) and KWP (Sec 6.3; RFC 5649), generically over any ElectronicCodeBook permutation, and publishes the AES instantiations. modes: - kw.rs: Kw<P, KEK_LEN>. The wrapping function W and its inverse (Algorithms 1 and 2) run in place on the caller's buffer, visiting the registers in the order the spec's shift register would present them, which RFC 3394 Sec 2.2.1 gives as the equivalent index-based form; the one block of scratch is a Secret. KW-AE/AD are Algorithms 3 and 4. - kwp.rs: Kwp<P, KEK_LEN>. KWP-AE/AD are Algorithms 5 and 6, including the single-block path for plaintexts of at most one semiblock. The ICV, length-field and padding checks of Algorithm 6 steps 4, 7 and 8 are all made, combined bitwise, and reported as the one DecryptionFailed. - The fixed-length API rejects invalid (KEY_LEN, CT_LEN) pairs at compile time via const assertions on the Table 1 limits; the run-time API returns InvalidInputLength outside them. Output buffers are zeroized on every failure. TKW (TDEA) is not included: it needs a 64-bit-block engine (PR bcgit#139). aes: - kw.rs: AES_KW_128/192/256 and AES_KWP_128/192/256 aliases, with the RFC 3394 Sec 4.1 and RFC 5649 7-octet vectors as doctests. No OIDs yet, in line with the other AES mode aliases. tests: - modes/tests/kw_tests.rs: all six RFC 3394 Sec 4 vectors and both RFC 5649 Sec 6 examples via TestFrameworkKeyWrap::test_kat, the shared framework for every AES key length at 26 payload lengths (every padding length of the single-block case included), and pins for the properties that distinguish KW, KWP and a raw block encryption. - modes/tests/acvp_kw_tests.rs: the NIST ACVP-AES-KW and ACVP-AES-KWP sets from bc-test-data, 1800 cases each, through both APIs, including the 171 + 178 forged ciphertexts that must be rejected. - aes/tests/kw_alias_tests.rs: the aliases name the right permutation and pass the framework. benches: KW/KWP wrap and unwrap of a 256-bit key, and KWP over 4 KiB. cli: aes{128,192,256}-kw and -kwp with wrap|unwrap, reading all of stdin (key wrap is one-shot), sharing key loading with the other AES commands; cli/tests/aes_kw_cli_tests.rs replays the RFC vectors in both directions and pins the length rules, the fail-closed unwrap and the help text. Assisted-by: Claude:claude-fable-5-1
…KeyWrapMode/KeyUnwrapMode and the KeyWrappingKey/KeyUnwrappingKey roles The traits from PR bcgit#160 took the KEK as a KeyMaterial on every call. On the key path the KEK is validated once, when the key object is made, so the pair becomes worker traits on the shape of MAC: `&self` methods over a worker that borrows the keyed permutation, with no key parameter and no KeyMaterialError. The mode traits (hazmat) hand the worker out from a key state, and the role traits forward to them with the one-shots provided, as the MAC roles do. The length helpers take `&self` so the role can forward them through a worker. Assisted-by: Claude:claude-mythos-5-1
… roles and its workers `test` and `test_kat` take the key type and build it from the seed through FromKeyMaterial; the KEK type and strength checks leave, since the construction suite covers them, and the wrong-KEK case is a second key. The mock becomes a key with a borrowed worker. Assisted-by: Claude:claude-mythos-5-1
… the keyed permutation The two structs from PR bcgit#160 held a PhantomData and keyed the permutation on every call. They become workers on the shape of Cmac: `Kw<'k, P>` over a `&'k P`, with `KW` / `KWP` as the mode markers a key is bound to, their key state the permutation alone, and KeyWrapMode / KeyUnwrapMode handing the worker out. ModeNames gains KW_ALG_NAME and KWP_ALG_NAME. The toy key gets the two roles, and the structural tests run over it; the framework's tamper checks need diffusion, so they run in `aes`. Assisted-by: Claude:claude-mythos-5-1
… aliases name the workers The key gets the two key-wrap roles, forwarded to the mode as the others are, and the internal permutations get their KW and KWP names. The aliases become `Kw<'k, AES128Internal>` and friends, the shape of AES_CMAC_128. Tests: the RFC 3394 and RFC 5649 vectors and the shared key-wrap framework through the keys, the ACVP KW and KWP sets from bc-test-data (1800 cases each, with the forgeries counted), the construction suite, and the alias names alongside the other aliases. The modes bench gains the KW/KWP measurements the old modes crate's bench had. Assisted-by: Claude:claude-mythos-5-1
…ild key objects The six commands load `AES*Key<KW>` / `AES*Key<KWP>` through load_key_object and call the key's one-shots. The direction takes the CipherDirection flag every other cipher command takes, so `-d encrypt` wraps and `-d decrypt` unwraps, instead of PR bcgit#160's positional wrap|unwrap. The Rust subprocess test becomes cli/tests/test_aes_kw.sh on the shell harness the other commands use, with the same cases: the RFC vectors both ways, framing, the length rules, rejection with no output, and key loading. Assisted-by: Claude:claude-mythos-5-1
Kw::from_perm and Kwp::from_perm were pub, which the key-rework-memory merge just made the odd ones out: 534d182 took every other worker's bare constructor to pub(crate) under QUALITY_AND_STYLE's hazmat rule, since the key object is the route and the mode impls that call them live in this crate. The two follow. The doc examples build their KEK through the test suite's toy key, as the other modes' now do, and kw_tests compares the key route against the hazmat key-wrap mode traits rather than the constructors. Assisted-by: Claude:claude-mythos-5-1
…, carried 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
… rebase from feature/key-rework-memory The release reads a key file as raw bytes unless --hex-input is given, so the aes*-kw and aes*-kwp help says so and the key-file test passes --hex-input for its hex form; kw_tests allows the shared toy key's unused helpers, as the other mode tests do. The KW bullet moves to the 0.1.4 release notes in the first commit. Assisted-by: Claude:claude-opus-5-5
dghgit
force-pushed
the
feature/150-key-wrap
branch
from
November 5, 2026 03:57
3983972 to
38427f5
Compare
…rker traits are The pair had `Algorithm` as its supertrait and no `Self: Sized` fence on the four const-generic one-shots, so `&dyn KeyWrapper` was rejected (E0038) where every other cipher and MAC worker trait can be held as a trait object. The supertrait becomes `AlgorithmInfo` and the const-generic methods are fenced, leaving `wrap_out`, `wrap_out_len`, `unwrap_out` and `unwrap_out_max_len` dispatchable; dyn_compatible_tests pins the pair. The `_out` docs now say the buffer is zeroized before anything else, which the cipher crate's implementations do from the next commit. Also drops a stray first line on KeyUnwrapper's docs, the KeyStream header a merge had glued on. Assisted-by: Claude:claude-mythos-5-1
…efore any check `wrap_out` never zeroized its buffer and `unwrap_out` did so only after the OutputBufferTooSmall check, so a refused call could leave stale bytes behind. Both now `fill(0)` first, before any error checking, as the `_out` rule in QUALITY_AND_STYLE and GCM's do_encrypt_out have it. Assisted-by: Claude:claude-mythos-5-1
…call left the buffer zeroed The too-small-buffer and invalid-length cases pre-fill the output with 0xFF and assert it is all zeros after the error; the mock zeroizes first, as the implementations must. Run against the previous cipher commit's predecessor, the new asserts fail on both KW and KWP. Assisted-by: Claude:claude-mythos-5-1
…oc matches SP 800-38F Sec 3.1 The six `kw_*`/`kwp_*` length predicates and helpers were public and used only by this crate's own tests. They become pub(crate): the lower bounds, the alignment rule and the length helpers are now pinned through a key's run-time API in kw_tests, and the 2^54, 2^32 and 2^29 upper bounds of Table 1, which no buffer can reach, are a unit test at the bottom of each mode file. The modes overview said KW and KWP "are not a mode for data"; SP 800-38F Sec 3.1 approves them for general data as well as keys. It now says so, and points at the determinism note in `kw` for why an AEAD is the better fit for data that may repeat. Assisted-by: Claude:claude-mythos-5-1
…ots under `std` The roles had the fixed-length `wrap_key` / `unwrap_key` and the buffer-taking `wrap_out` / `unwrap_out`, but not the allocating form every other role trait provides beside its `_out` method. `wrap` and `unwrap` are provided over the worker's length helper and `_out` method, gated on `std` as `encrypt_with_aad` is. `unwrap` returns a `Vec`, which `Secret` cannot wrap, so its doc points at `unwrap_key` for a known length and asks the caller to zeroize otherwise. Assisted-by: Claude:claude-mythos-5-1
…/ `unwrap` They are checked against the fixed-length answers in `test` and `test_kat`, and for the invalid-length and modified-ciphertext errors. Assisted-by: Claude:claude-mythos-5-1
Assisted-by: Claude:claude-mythos-5-1
The command no longer sizes and truncates its own output buffer. Assisted-by: Claude:claude-mythos-5-1
dghgit
approved these changes
Nov 5, 2026
dghgit
left a comment
Contributor
There was a problem hiding this comment.
I rebased this first. After that the only thing I found it seemed short was the lack of unwrap/wrap methods that returned the plain-test/cipher-text. I've added these.
This one is now approved and ready to go.
Contributor
|
Merged as squash 27b83ed on release/0.1.4alpha (author royb). |
dghgit
pushed a commit
that referenced
this pull request
Nov 5, 2026
…W) and Key Wrap with Padding (KWP), NIST SP 800-38F Sec 6.2 and 6.3 (RFC 3394 / RFC 5649), over any 128-bit block permutation on the key-object path: the KeyWrapper / KeyUnwrapper worker traits in core on the shape of MAC (AlgorithmInfo supertrait and Self: Sized on the const-generic one-shots so the run-time-length half is dyn-compatible; fixed-length wrap_key / unwrap_key whose (KEY_LEN, CT_LEN) pair is checked at compile time; run-time-length wrap_out / unwrap_out returning InvalidInputLength outside Table 1, every _out method zeroizing its whole buffer first; unwrap output a Secret rather than a KeyMaterial; every authenticity failure the one DecryptionFailed), the hazmat KeyWrapMode / KeyUnwrapMode and the KeyWrappingKey / KeyUnwrappingKey roles with the one-shots and the allocating wrap / unwrap provided under std; a key_wrap suite in core-test-framework checked against an in-file mock; the KW / KWP mode types and the Kw / Kwp workers in cipher, with W and W⁻¹ (Algorithms 1 and 2) in place in the RFC 3394 Sec 2.2.1 index form, KW-AE/AD and KWP-AE/AD as Algorithms 3 to 6 including the single-block path, the three KWP-AD checks made unconditionally and combined bitwise, and ModeNames gaining KW_ALG_NAME / KWP_ALG_NAME; AES*Key<KW> / AES*Key<KWP> and the AES_KW_* / AES_KWP_* aliases in aes, tested against all six RFC 3394 Sec 4 vectors, both RFC 5649 Sec 6 examples, the ACVP-AES-KW and -KWP sets from bc-test-data (1800 cases each, forgeries counted) and the shared suites, with criterion benches; the aes{128,192,256}-kw and -kwp CLI commands (-d encrypt|decrypt, not streaming) and their shell test. TKW is not included. Mutants on the changed files: cipher kw.rs + kwp.rs 151 (111 caught, 38 unviable, 2 equivalent), core traits and roles 46 (18 caught, 28 unviable), aes key roles 2 unviable.
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-fable-5-1
Assisted-by: Claude:claude-mythos-5-1
hubot
pushed a commit
that referenced
this pull request
Nov 5, 2026
…into feature/aria: KW_ALG_NAME / KWP_ALG_NAME on the ARIA permutations Assisted-by: Claude:claude-mythos-5-1
hubot
pushed a commit
that referenced
this pull request
Nov 5, 2026
…into feature/sm4: KW_ALG_NAME / KWP_ALG_NAME on the SM4 permutation Assisted-by: Claude:claude-mythos-5-1
hubot
pushed a commit
that referenced
this pull request
Nov 5, 2026
…into feature/camellia: KW_ALG_NAME / KWP_ALG_NAME on the Camellia permutations Assisted-by: Claude:claude-mythos-5-1
hubot
pushed a commit
that referenced
this pull request
Nov 5, 2026
…into feature/tdes: ModeNames gains KW_ALG_NAME / KWP_ALG_NAME, which the 64-bit TDEA permutations leave empty as they do the 128-bit-block modes' names (KW and KWP need a 128-bit block, SP 800-38F Sec 5.1; TKW is not implemented) Assisted-by: Claude:claude-mythos-5-1
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.
AES Key Wrap (KW) and Key Wrap with Padding (KWP), NIST SP 800-38F Sec 6.2 and 6.3 (RFC 3394 / RFC 5649), over any 128-bit block permutation, on the key-object path of release/0.1.4alpha. Closes #150.
The first two commits are Roy's original traits and implementation. The rest is the port onto the key objects, the mode traits and the borrowed workers that landed in the meantime, one commit per crate, plus the review fixes.
core
KeyWrapper/KeyUnwrapperare worker traits on the shape ofMAC:&selfmethods over a worker that borrows the keyed permutation, no key parameter. They are separate so a policy can allow unwrapping without wrapping.AlgorithmInfosupertrait andSelf: Sizedfences on the const-generic one-shots keep the run-time-length half dyn-compatible, like every other worker trait.wrap_key/unwrap_key(provided overwrap_key_out/unwrap_key_out) reject an invalid(KEY_LEN, CT_LEN)pair with an inline const assertion, so a wrong length is a compile error. Run-time-lengthwrap_out/unwrap_out, sized bywrap_out_len(exact) andunwrap_out_max_len(upper bound), returnInvalidInputLengthoutside SP 800-38F Table 1. Every_outmethod zeroizes its whole buffer first. The roles also provide the allocatingwrap/unwrapunderstd, as the cipher roles do beside their_outforms.Secret, not aKeyMaterial: unwrapping says nothing about the type or provenance of the bytes. Every authenticity failure is the oneDecryptionFailed.KeyWrapMode/KeyUnwrapModehand the worker out from a key state; the role traitsKeyWrappingKey/KeyUnwrappingKeyinsymmetric_keyforward to them with the one-shots provided.SymmetricCipherError::InvalidInputLength.core-test-framework
key_wrap::TestFrameworkKeyWrap:testruns a key type through both roles and both workers (round trips, determinism, length helpers, buffer-too-small and invalid-length errors with the buffer checked for zeroing, tamper and wrong-KEK rejection);test_kattakes a published vector. An in-file mock (not cryptography) keeps the suite honest.cipher
KW/KWPmode types whose key state is the permutation;Kw<'k, P>/Kwp<'k, P>workers on the shape ofCmac. W and W⁻¹ (Algorithms 1 and 2) run in place in the RFC 3394 Sec 2.2.1 index form; KW-AE/AD are Algorithms 3 and 4, KWP-AE/AD Algorithms 5 and 6 with the single-block path, and the three KWP-AD checks are made unconditionally and combined bitwise.ModeNamesgainsKW_ALG_NAME/KWP_ALG_NAME(the open cipher PRs will need the two consts when this lands).aes
AES128Key<KW>,AES128Key<KWP>and the 192/256 forms implement the two roles;AES_KW_128…AES_KWP_256name the workers. Tests: all six RFC 3394 Sec 4 vectors and both RFC 5649 Sec 6 examples throughtest_kat, the framework at 26 payload lengths, the ACVP-AES-KW and -KWP sets from bc-test-data (1800 cases each, forgeries counted), the construction suite, alias names, and KW/KWP benches.cli
aes{128,192,256}-{kw,kwp} -d encrypt|decrypt, stdin to stdout, key loading shared with the other AES commands. Not streaming: all of stdin is read, then written in one go.cli/tests/test_aes_kw.shreplays the RFC vectors both ways and pins the length rules, the fail-closed unwrap and key loading.TKW (TDEA) is not included; it needs the 64-bit-block engine of #139.
Mutants (apollo, scoped to the changed files, cipher + aes tests):
bouncycastle-cipher,modes/kw.rs+modes/kwp.rs: 151 mutants, 111 caught, 38 unviable, 2 missed. Both misses replace2 * SEMIBLOCK_LENwith2 + SEMIBLOCK_LENin a minimum-length check that runs after the 8-byte alignment test, so>= 16and>= 10agree on every length that reaches it: equivalent.bouncycastle-core, the key-wrap traits and roles intraits.rs+symmetric_key.rs(tests from cipher, aes and the framework): 46 mutants, 18 caught, 28 unviable, 0 missed.bouncycastle-aes, the two role impls inkey.rs: 2 mutants, both unviable.Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-fable-5-1
Assisted-by: Claude:claude-mythos-5-1