Repository navigation
Add KeyWrapper/KeyUnwrapper traits for SP 800-38F key wrap (#150) - #160
Draft
roy-basmacier wants to merge 9 commits into
Draft
roy-basmacier wants to merge 9 commits into
roy-basmacier wants to merge 9 commits into
Conversation
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
dghgit
force-pushed
the
feature/150-key-wrap
branch
from
October 26, 2026 18:39
339868e to
80c25ff
Compare
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?
# Conflicts: # crypto/aes/src/key.rs # crypto/cipher/tests/common/toy_key.rs # crypto/core/src/hazmat/mod.rs # crypto/core/src/hazmat/mode.rs
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
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
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.
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 #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:
core/errors.rs:
core-test-framework:
Assisted-by: Claude:claude-opus-5-5