Skip to content

Key object rework prototype: AESKey<P, M> and HmacKey<H> producing their workers through role traits - #170

Draft
dghgit wants to merge 41 commits into
feature/simple-ciphersfrom
feature/key-rework
Draft

dghgit wants to merge 41 commits into
feature/simple-ciphersfrom
feature/key-rework

Conversation

@dghgit

@dghgit dghgit commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Issue Link

No issue filed yet. This is the prototype behind the key object rework document circulated for review (AES128Key<GCM>, "Death to init()"), opened so the design can be discussed against working code alongside #167.

Summary

Prototype of mode-bound symmetric key objects: AESKey<P, M> and HmacKey<H> are built once from KeyMaterial and produce their encryptors, decryptors, generators and verifiers through role traits in core.

Description

  • What: core gains FromKeyMaterial<KEY_LEN> / FromAnyKeyMaterial, the AEAD and MAC role pairs (AeadEncryptionKey / AeadDecryptionKey, the packet pair, MacGenerationKey / MacVerificationKey) and the hazmat Mode<P> traits a mode implements over a permutation. cipher gets GCM, CCM, CMAC and GMAC as mode types over a key state. aes gets one AESKey<P, M> with AES128Key<M> / AES192Key<M> / AES256Key<M> aliases, the forwarding role impls, and a binding check of KeyMaterial's algorithm name against the mode's. hmac gets HmacKey<H> holding the two pad states, and hkdf one HmacKey per PRK. The aes*-ccm CLI commands and the CCM memory bench run through the key path.
  • Why this shape: validation and schedule expansion happen once per key rather than per message; one key under two modes is a compile error; mode-derived state (GCM's H, CMAC's subkeys) lives in the key; and a token-backed key can implement the same role traits, which no constructor taking key bytes allows. The reasoning and the alternatives considered are the document's numbered decisions.
  • Alternatives: keeping the static constructors behind the key path, permutation-level keys with the mode as a value, and typed newtypes over KeyMaterial with the constructors kept on the traits (the shape of SymmetricCipherKey: dedicated key types per algorithm #167). Each is discussed in the document.
  • Scope of the prototype: AES under GCM, CCM, CMAC and GMAC, plus HMAC. Not yet covered: CBC, CFB, CFB8, CTR and ECB, the other block ciphers, Ascon and ChaCha, KMAC, and removal of the old do_*_init constructors, which stay in place so both routes coexist on this branch.
  • Tests: key tests per mode in crypto/aes/tests, CCM and GCM one-shot and packet tests in cipher, HMAC key tests covering keys shorter than, equal to and longer than the block, and the binding-check refusals. Every commit builds and passes cargo test --workspace; 1187 tests at the tip.

Scope and Risk

  • Packages: core, cipher, aes, hmac, hkdf, sha2, sha3, sm3, core-test-framework, cli, mem_usage_benches. 59 files, +7333 / -1630.
  • Runtime behaviour: additive. The existing constructors and one-shots are unchanged; the key path is a second route to the same workers. The HMAC worker no longer carries a key buffer, so HMAC<H> is two hash states.
  • Regression likelihood: low on existing APIs, since they are not modified; the new path is covered by the tests above and the existing vectors run through it where the CLI does.
  • Worst case: a key constructed on the new path accepting material the old route would have refused. The key-type, strength and algorithm-binding checks are tested for the refusals and acceptances.

Validation

cargo build --workspace
cargo test --workspace
cargo test -p bouncycastle-aes aes_key          # key construction, binding and role tests
cargo test -p bouncycastle-hmac hmac_key_tests  # any-length keys
cargo run --release -p mem_usage_benches --bin bench_ccm_mem_usage

Up to date with feature/simple-ciphers: its tip (the CCM do_*_inplace rename, 7084adb) is merged in as 69e24ba, with the rename carried onto the reworked packet type here, and 1185 tests passing on the result.

AI Usage Statement

Did you use AI in creating this pull request:

  • No
  • Yes, indirectly - no submitted code was generated by AI (e.g., answering questions, performing a review, suggestions, etc.)
  • Yes, trivial code changes were generated by AI (e.g., autocompletion of a single line, reformatting, or spell-checking)
  • Yes, non-trivial code changes were generated by AI

Assisted-by: Claude:claude-mythos-5-1

dghgit and others added 30 commits October 10, 2026 16:58
…de traits (prototype)

The key-object layer of the symmetric key rework, built beside the existing constructors.
symmetric_key.rs holds FromKeyMaterial<KEY_LEN> (from_key_material plus a provided generate(rng)
that tags the draw with the implementor's KEY_TYPE), FromAnyKeyMaterial for constructions that
take a key of any length (the only trait with a weak-key form), the AeadEncryptionKey /
AeadDecryptionKey pair with the one-shots provided over create_encryptor(rng) /
create_decryptor(nonce), and the MacGenerationKey / MacVerificationKey pair with mac, mac_out
and verify provided over create_generator() / create_verifier(). hazmat gains Mode<P> (KeyState
and KEY_TYPE) and the AeadEncryptMode / AeadDecryptMode constructors the role impls forward to.

No method instantiates a DRBG for the caller: everything that draws randomness takes
&mut dyn RNG, and the create_* names carry no _rng suffix because they have no twin.
KeyMaterial::from_rng accepts an unsized RNG so the provided generate can use it.

Two departures from the issue draft, both forced by the tree as it stands: the role and mode
traits carry KEY_LEN while the cipher traits still do, so that Encryptor and Decryptor can be
bounded by them; and the MAC role pair bounds its workers on MAC rather than on a MACGenerator /
MACVerifier split, because putting the same final names on two traits while MAC still has them
makes every call site with both in scope ambiguous. Both resolve when the constructors leave the
worker traits.

Assisted-by: Claude:claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er a key state (prototype)

GCM<TAG_LEN = 16> is the mode type a mode-bound key names, AES128Key<GCM>. GcmKeyState<P> is
what such a key stores: the keyed permutation and the hash subkey H = CIPH_K(0^128) (SP 800-38D
Algorithm 4 step 1), derived once per key rather than once per message. Gcm's setup is split so
that the per-message half (J0, CIPH_K(J0), the counter start) runs over an existing state, and
Gcm<P, Encrypting, ..>::from_key_state(state, rng) / Gcm<P, Decrypting, ..>::from_key_state(state,
nonce) are the constructors a permutation crate's mode impls call; the decrypt side is infallible.
do_encrypt_init_rng and do_decrypt_init are unchanged in behaviour and still expand per message.

The previous prototype's GcmMode marker, GCM constant and default-RNG from_perm are gone: the
mode is a type, not a value, and the key path offers no form that instantiates a DRBG.

Assisted-by: Claude:claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he packet bench (prototype)

AESKey<P: AESParams, M: Mode<AESInternal<P>>> stores M::KeyState and nothing else; AES128Key<M>,
AES192Key<M> and AES256Key<M> are the aliases, so AES128Key<GCM> and AES128Key<GCM<12>> are
distinct key types over the same material and a key under a second mode is a second
from_key_material call. FromKeyMaterial<16 | 24 | 32> is implemented per key length (the
KeyMaterial<N> argument cannot be sized from P::KEY_LEN on stable), checks M::KEY_TYPE and then
runs the existing AESInternal validation and expansion once. The AeadEncryptionKey /
AeadDecryptionKey impls forward to whichever mode traits M implements, so the mode decides the
role. No Debug, Clone written out so M need not be Clone.

The Mode / AeadEncryptMode / AeadDecryptMode impls for GCM over the three AES permutations live
here, one set per key length, because a generic impl in cipher over P: ElectronicCodeBook<KEY_LEN,
16> leaves KEY_LEN unconstrained on Mode<P> (E0207) and the per-P form needs a const expression;
they collapse into cipher once KEY_LEN leaves the cipher traits.

key_tests pins that create_encryptor(rng) produces the same nonce, ciphertext and tag as
do_encrypt_init_rng from the same material and RNG, that the role-trait one-shots match the
cipher traits' key-taking one-shots byte for byte and zeroize on a failed tag, interoperation of
the two paths at every key length, the GCM<12> key type, generate (including refusal of an RNG
below the key strength), the construction-time errors, and Clone; a compile_fail doctest shows a
wrong-length nonce refused by the type. aes_gcm_packet_benches compares the two paths over
16 KiB packets, with the OS-DRBG-per-packet arms now explicit on both sides.

Assisted-by: Claude:claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… buffer (prototype)

HmacKey<H> is the key object: H after absorbing K XOR ipad and H after absorbing K XOR opad,
the precomputation RFC 2104 Section 4 describes ("can be precomputed only once at the time of
generation of the key K, or before its first use", saving "the application of the compression
function of H on two B-byte blocks" per message). It is built once from key material of any
length through FromAnyKeyMaterial (from_key_material, from_key_material_allow_weak, and
generate(rng), which draws the hash's output length), implements MacGenerationKey and
MacVerificationKey, and holds no key bytes. create_generator() and create_verifier() clone the
two states into a fresh HMAC<H>, so any number of workers can be in flight from one key.

HMAC<H> becomes { hasher, outer }: the KEY_BUF_LEN parameter, the key buffer and the key length
go, and the aliases in sha2, sha3 and sm3 are HMAC<SHA256> and so on. MAC::new and
new_allow_weak_key build an HmacKey and take one worker from it, so every existing caller and
vector suite runs through the new pads unchanged; do_final finishes the inner hash into the
stored outer state, and from_suspended rebuilds the outer state from the re-supplied key with
the state bytes unchanged. The padded key block is built in a Secret scratch rather than a vec,
which removes two of the three heap allocations per message the old path made.

Sizes (size_of, x86-64): HMAC<SHA256> and HmacKey<SHA256> 224 (worker was 184), the SHA-512
family 416 (was 344), the SHA-3 family 880 for all four (was 520 to 592); HKDF_SHA256 256 (was
296), HKDF_SHA384/512 448 (was 392). The memory tables in the alias modules and sha2::hkdf are
updated to these figures. hmac_key_tests pins key-against-constructor agreement for short,
block-length and over-length keys on SHA-256, SHA-512 and SHA3-256, independent workers from one
key, the one-shots, the key-type and strength policy, generate, suspend/resume and Clone.

Assisted-by: Claude:claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Expand builds HmacKey<H> from the PRK once and calls create_generator() for each T(i), which is
the many-messages-one-key case the pad cache is for; do_extract_init builds the salt key with
from_key_material_allow_weak, as it used new_allow_weak_key before. Output bytes and the
suspended-state layout are unchanged, and the HKDF vector suites pass as before.

Assisted-by: Claude:claude-fable-5-1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e keys (prototype)

AeadEncryptMode and AeadDecryptMode gain encrypt_detached_in_place(state, rng, aad, buf) and
decrypt_detached_in_place(state, nonce, aad, buf, tag), provided over encrypt_init /
decrypt_init; AeadEncryptionKey and AeadDecryptionKey gain the same pair over create_encryptor /
create_decryptor. The provided bodies (in_place.rs) copy the message through a 64-byte stack
buffer and encrypt each chunk back into the bytes it came from, which needs no second buffer the
size of the message because a streaming worker never emits more than it has consumed. The
decryptor zeroizes the buffer on any error, as the _out forms do.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…ed (prototype)

CCM<NONCE_LEN, TAG_LEN, AAD_LEN, DATA_LEN> is the M of a mode-bound CCM key; the frame is on the
mode because the streaming worker a key produces is the fixed-frame CcmEncryptor / CcmDecryptor.
CcmPacket::from_perm and random_nonce, and CcmEncryptor::from_perm / CcmDecryptor::from_perm,
build from an already-expanded schedule; the existing do_*_init forms now go through the latter.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…cket (prototype)

The CCM Mode / AeadEncryptMode / AeadDecryptMode impls for the three AES permutations, per key
length as for GCM. The role impls on AESKey forward the in-place one-shots to the mode, and the
CCM impls override them to run a CcmPacket committed to the message's own length, so a CCM key
takes any length in place while its streaming worker and the provided _out one-shots over it
stay fixed-frame. key_tests covers both paths at lengths around the chunk size, the fixed-frame
refusal, agreement with the packet API, and zeroization on a bad tag.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…e mode traits (prototype)

in_place.rs becomes provided.rs and gains encrypt_inline / encrypt_detached over an
already-built worker; the role keys' two _out one-shots call them. No behaviour change: this is
so the supplied-nonce forms that follow can share the bodies with the drawing forms.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…(prototype)

AeadEncryptMode gains encrypt_init_nonce(state, nonce), infallible as decrypt_init is, and a
provided encrypt_detached_in_place_nonce over it. AeadEncryptionKey gains create_encryptor_nonce
and the _nonce twins of its one-shots (encrypt_with_aad_nonce[_out], encrypt_detached_nonce[_out],
encrypt_detached_in_place_nonce), provided over the shared bodies; the trait docs say the nonce
is drawn by default and what a caller of a _nonce form takes on.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…CCM encryptors (prototype)

Gcm::from_key_state_nonce and CcmEncryptor::from_perm_nonce, the constructors a permutation
crate's encrypt_init_nonce impl calls; the drawing from_perm now goes through the CCM one.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… the in-place nonce form (prototype)

AESKey forwards create_encryptor_nonce and encrypt_detached_in_place_nonce to the mode. The CCM
impls run the in-place nonce form through CcmPacket at any A.1 nonce length, so a key such as
AES128Key<CCM<8, 8, 0, 0>> is usable through the supplied-nonce forms while the drawing forms,
which carry the 12-byte floor, are never instantiated for it. key_tests checks every _nonce
form against the drawing form's bytes on GCM and CCM, and the short-nonce key against the
packet API.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
The command builds an AES128Key<CCM<N, T, 0, 0>> (and the 192/256 forms) and runs
encrypt_detached_in_place_nonce / decrypt_detached_in_place on the input it read, so it no
longer names CcmPacket. A small CcmKeys trait with a generic associated type lets `run` stay
generic over the key length while choosing the nonce and tag lengths at run time. The AAD is
read whole now, since the one-shots take it as a slice; the declared-length streaming of a
regular --aad-file goes, along with its help text. The q-limit message computes the limit
itself. test_aes_ccm.sh passes as before.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…_LEN on the mode type (prototype)

Mode<P> for CCM needs no KEY_LEN (the key state is the schedule itself), and the two AEAD mode
traits carry it, so the impls live here once rather than per key length in each permutation
crate. The in-place one-shot overrides move with them. CCM::MAX_PAYLOAD_LEN is the A.1 limit
for the nonce length, for callers that no longer see the packet type.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…rototype)

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…fixed-frame stream (prototype)

The structural tests no longer name CcmPacket: the one-shots are CCM::encrypt_detached_in_place_nonce
/ decrypt_detached_in_place over the toy permutation, and the chunked and frame-holding checks run
through CcmEncryptor / CcmDecryptor at the message's length. The toys derive Clone, which the mode
impls ask of a key state. The suspend tests are untouched for now.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… the trait pair (prototype)

sp800_38c_tests, ccm_bc-test-data, ccm_wycheproof, key_tests and the criterion benches no longer
name CcmPacket. The Appendix C vectors go through CCM::encrypt_detached_in_place_nonce /
decrypt_detached_in_place and the pair's any-length inline one-shots, with the chunking sweep on
the pair's streams at each example's frame; the progressive-AAD check feeds the pair's
do_update_aad in pieces, with C.4's 64 KiB AAD as its capacity. The test of new_with_lengths'
declared-AAD accounting goes with that constructor, which has no public counterpart; the pair's
own AAD and frame bounds are already pinned. The memory table keeps the pair's sizes.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… one-shot (prototype)

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…/ CcmDecryptor (prototype)

The packet type is now reachable only through the pair and the CCM mode's in-place one-shots,
so its SuspendableKeyed impl moves to the pair: the adapter's state is the packet's plus the
held-back AAD, its length and the phase flag, and the decryptor adds the held-back tag. A
resumed state must owe no more than the frame, and one that says the header is not yet
formatted must have absorbed nothing. new_with_lengths and do_update_aad on the packet had no
caller left and go. The module docs and the modes overview are rewritten over the pair, the
A.1 shape compile_fail cases move to CcmEncryptor, and the suspend tests run over the pair,
with the AAD buffer, phase flag and tag bounds pinned alongside the packet's.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…de-bound key (prototype)

The module docs describe one family, AES_CCM_128 and friends, with the supplied-nonce and
in-place examples; the memory tables are re-measured (key schedule, pair sizes, massif peaks
for the in-place, streaming and one-shot paths). The alg-name and suspend tests use the pair.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…ototype)

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… lengths up front (prototype)

AeadEncryptWorker / AeadDecryptWorker are the cipher traits' streaming methods without their
constructors; the role and mode traits bound their workers on them, so a worker a key produces
need not implement a constructor it cannot have. AeadPacketEncryptMode / AeadPacketDecryptMode
and AeadPacketEncryptionKey / AeadPacketDecryptionKey are the forms whose constructors take the
AAD and payload lengths, for a mode that must commit to them before its first byte; their
one-shots have the lengths in hand and are provided over the shared bodies, which now include
the decrypting _out forms and the inline-layout length check.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…/ CcmPacketDecryptor as its workers (prototype)

A CCM key's constructors now take the AAD and payload lengths, so the frame leaves the mode
type and the workers hold nothing back: the encryptor is the packet state alone and the
decryptor adds only the inline tag it holds past the declared payload. The in-place one-shots
run the packet directly. Gcm's workers forward to the cipher traits, named by path since the
method names coincide. The declared-AAD update returns to the packet, crate-private.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…e (prototype)

AES128Key<CCM<12, 16>> and friends implement AeadPacketEncryptionKey / AeadPacketDecryptionKey
by forwarding to the mode. ccm_key_tests covers every form at any length against the pair, the
streams holding to the declared lengths, the up-front refusals, zeroization and the short-nonce
key, with the worker sizes the docs quote pinned. key_tests keeps GCM, with its generic helpers
naming the worker traits by path.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…ype)

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…th of the caller's key, so a MAC keyed by a block cipher can pass it (prototype)

Ported from feature/cmac-gmac 0d004a9e: the weak key takes the caller's key length when it fits the buffer, raised to the shortest length that can carry the lowered strength; keys that are empty or longer than 64 bytes keep the old per-strength lengths.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… MAC role keys forward to (prototype)

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…AC_ALG_NAME on ModeNames (prototype)

Ported from feature/cmac-gmac 572c4d09 and reshaped for the key objects: the keyed permutation
and the two subkeys are a CmacKeyState, derived once per key, and a Cmac worker is built from
it by from_key_state or, for one message, by MAC::new from the material as before. The mode
reports ModeNames::CMAC_ALG_NAME like the others; the AES permutations' and the benches'
ModeNames impls gain the constant here so that the tree builds at this commit. The known-answer
suites move to the aes crate, since cipher tests only over toys.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
cmac.rs holds the aliases and the Mode / MacMode impls for CMAC over the three permutations,
per key length as GCM's are. AESKey forwards MacGenerationKey / MacVerificationKey to the mode,
and its constructors hand a MAC mode's MACKey material to the permutation as a retagged copy at
the same strength, since the permutation accepts only a SymmetricCipherKey. Tests: the NIST
CSRC examples through TestFrameworkMAC, ACVP CMAC-AES.4014544 (756 cases) and Wycheproof
aes_cmac_test.json (311, every valid tag also through the key object), the key route against
MAC::new at every key length, key-type and strength refusals, and the sizes the docs quote
(208/240/272 B keys, 248/280/312 B workers). The modes bench gains both routes at 16 KiB.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
dghgit and others added 11 commits October 10, 2026 23:37
…C_ALG_NAME on ModeNames (prototype)

Ported from feature/cmac-gmac ab31119d and reshaped for the key objects: Gmac is still a Gcm
fed only AAD, but is now built from a GcmKeyState by from_key_state (nonce drawn from the RNG
it is given) or from_key_state_nonce on the generating side and from_key_state(nonce) on the
verifying side, so a GMAC key shares GCM's cached H. The material route (new, new_nonce) stays
for one message and takes MACKey material of exactly KEY_LEN bytes, retagged for the
permutation as CMAC's is, so a GMAC key cannot also be a GCM key. Gcm's absorb_aad and the two
finish methods go pub(crate) so the wrapper drives them directly rather than through the
cipher traits' detached finals. No default-DRBG constructor, per the key-rework rule; the
mac/verify one-shots consume a worker, since the nonce has to come from somewhere first. The
mode reports ModeNames::GMAC_ALG_NAME like the others; the AES permutations' and the benches'
ModeNames impls gain the constant here so that the tree builds at this commit. Toy tests:
every route gives Gcm's AAD-only tag, split points, tag/nonce/message sensitivity, a fresh
nonce per draw, and key validation.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…r/verifier pair (prototype)

gmac.rs holds the aliases and the Mode impls for GMAC over the three permutations, per key
length as GCM's are, with GcmKeyState as the key state and MACKey as the key type. The pair is
the MAC roles' names with the nonce added -- create_generator(rng) returning the nonce,
create_generator_nonce(nonce), create_verifier(nonce) -- inherent on the key rather than a role
trait in core, since GMAC is the only nonce-based MAC in the tree (issue draft, decision 15);
AESKey's state goes pub(crate) for it. Tests: the key object against Gmac::new and AES_GCM over
AAD alone at every key length, the 96-bit-tag prefix, independent workers, key-type and
strength refusals both ways against GCM, generate, and the sizes (192/224/256 B, a GCM key's);
the ACVP AES-GMAC (270) and Wycheproof aes_gmac (207 96-bit-IV) suites now run every case
through the key object as well as through Gcm. The modes bench gains both routes at 16 KiB,
about 44 us each.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
… for the key's binding check (prototype)

KeyMaterial's opt-in algorithm binding (set_algorithm / check_algorithm) is honoured by the
cipher traits' constructors but was invisible to the key objects, which had no name to check it
against: a Mode<P> impl carried the key type but not the ALG_NAME its workers report. The
constant is set from the permutation's ModeNames entry -- P::CCM_ALG_NAME in cipher's generic
CCM impl, which gains the ModeNames bound, and the matching GCM/CMAC/GMAC names in the aes
crate's nine per-permutation impls -- so that the tree builds at this commit. The check itself
lands in the aes key constructor next.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
…(prototype)

from_key_material now calls check_algorithm(M::ALG_NAME) after the key-type check, so material
bound to AES_CBC_128 is refused by AES128Key<GCM>, material bound to AES_GCM_128 by
AES256Key<GCM>, and a MACKey bound to AES_CMAC_128 by AES128Key<GMAC>; material bound to the
mode's own name, or unbound, is accepted as before. This closes the gap the cipher traits'
supplied-nonce constructors leave open (GCM's and CCM's do_encrypt_init_nonce never check the
binding), since on the key route every worker comes from a key that did. Test: the refusals
and acceptances above for both key types, and Mode::ALG_NAME against the alias's
Algorithm::ALG_NAME. cargo test --workspace: 1187 passed.

Assisted-by: Claude:claude-mythos-5-1
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
# Conflicts:
#	cli/src/aes_ccm_cmd.rs
#	crypto/aes/src/ccm.rs
#	crypto/aes/tests/ccm_bc-test-data.rs
#	crypto/aes/tests/sp800_38c_tests.rs
#	crypto/aes/tests/suspend_tests.rs
#	crypto/cipher/src/modes/ccm.rs
#	crypto/cipher/tests/modes/ccm_tests.rs
#	crypto/cipher/tests/suspend_tests.rs
#	mem_usage_benches/src/bench_ccm_mem_usage.rs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant