Skip to content

feat(aead): fuse the AES-CCM CBC-MAC and CTR passes with aesni_ccm64 - #147

Merged
cathaysia merged 1 commit into
masterfrom
feat/ocb-ccm-fused
Oct 7, 2026
Merged

cathaysia merged 1 commit into
masterfrom
feat/ocb-ccm-fused

Conversation

@cathaysia

Copy link
Copy Markdown
Owner

What

Item 1 of the follow-up list from #144: wire the AES-NI fused bodies for the modes that were still on portable loops.

Done: CCM. CCM ran two passes over the message (CBC-MAC, then CTR) even
though aesni-x86_64.pl exports aesni_ccm64_{encrypt,decrypt}_blocks, which
interleave the two AES chains over whole blocks.

  • Aes now implements Ccm directly — the same split the other modes use
    (impl Ctr for aes::Aes / impl Gcm for aes::Aes alongside a
    marker-bounded generic impl) — and drives the assembly routine the way
    CRYPTO_ccm128_{encrypt,decrypt}_ccm64 does: the fused body covers whole
    blocks, while the partial tail, the ctr64_add counter advance and the tag
    mask stay in Rust. CcmMarker is new; every cipher that already implements
    BlockCipherMarker keeps the portable driver, and so does a CPU without
    AES-NI (Aes::enc_schedule reports whether the schedule is in the AES-NI
    format the body needs).
  • B0/AAD/length/counter/tag helpers moved to module-level functions so both
    drivers share one copy (MacBlocks replaces the byte-at-a-time MAC loop).
  • The decrypt body pairs the CTR decryption with the CBC-MAC of the
    decrypted plaintext, in one pass, exactly like the C driver.

Not done: OCB3. Its assembly routine takes the OCB state machine with it
(offset_i in/out, the 64-entry L_i table, checksum, start_block_num),
so the mode's seal/open have to be factored first — AAD offset handling, tail
and tag are reusable as-is, but the full-block loop is currently inline in the
generic Ocb3Impl and there is no way to swap it for one concrete cipher
without that split (the same reason Ctr/Gcm have dedicated Aes impls).
The mapping itself is understood: crown's l[i] = L_i (i in 0..64) matches
the table the assembly indexes by ntz(block) and start_block_num would be
1 for crown's one-shot seal. It is a self-contained next step; I stopped
rather than land it half-verified, since unlike CCM it cannot be validated by
dropping it into the existing implementation.

Verification

  • OpenSSL evpciph_aes_ccm.txt vectors (the integration test drives AES-CCM,
    ARIA-CCM, Camellia-CCM, SM4-CCM and SEED-CCM; the non-AES ciphers exercise
    the still-generic path).
  • A differential test compares the fused and portable drivers over message
    lengths 0, 1, 15, 16, 17, 31, 32, 33, 64 and 100, plus a round trip. It
    caught the first version skipping the tail whenever the message was shorter
    than one block.
  • CARGO_INCREMENTAL=0 cargo test --all (the CI command, asm enabled through
    feature unification): green. cargo clippy -Dwarnings with and without
    --features asm, and cargo fmt --all --check.

Next

OCB3 as described above, then the two items that need AVX512-IFMA hardware
(aes-gcm-avx512 and the rsaz x2 driver).

CCM ran two passes over the message (CBC-MAC, then CTR) even though
`aesni-x86_64.pl` exports `aesni_ccm64_{encrypt,decrypt}_blocks`, which
interleave the two AES chains over whole blocks.

`Aes` now implements `Ccm` itself — the same split the other modes use, a
concrete impl for AES plus a marker-bounded generic impl — and drives the
assembly routine the way `CRYPTO_ccm128_{encrypt,decrypt}_ccm64` does: the
fused body covers whole blocks and the partial tail, the `ctr64_add`
counter advance and the tag mask stay in Rust. A CPU without AES-NI falls
back to the portable driver, and every other cipher keeps it too.

B0/AAD/length/tag helpers moved to module-level functions so both drivers
share them. A differential test walks a range of message lengths (0, 1, 15,
16, 17, 31, 32, 33, 64, 100) comparing the fused and portable paths; it
caught a first version that skipped the tail when the message was shorter
than one block.
@cathaysia
cathaysia merged commit 6066b40 into master Oct 7, 2026
8 checks passed
@cathaysia
cathaysia deleted the feat/ocb-ccm-fused branch October 7, 2026 06:56
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