Skip to content

perf(aes-gcm): run aws-lc's eight-way AES-192-GCM on aarch64 with SHA3 - #460

Merged
kixelated merged 6 commits into
mainfrom
quest/m2/speed/aes192-gcm-aarch64
Nov 7, 2026
Merged

kixelated merged 6 commits into
mainfrom
quest/m2/speed/aes192-gcm-aarch64

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

s2n-bignum's stitched aarch64 AES-GCM kernels take only 128- and 256-bit keys, so AES-192-GCM ran AES-CTR and GHASH in separate passes past short's lengths. From 1200 B up, that took 1.04-1.09x aws-lc-rs's time on Neoverse N2 and 1.35-1.68x on Apple M2.

Approach

  • admin/import-aws-lc imports aws-lc's aesv8_gcm_8x_enc_192 and aesv8_gcm_8x_dec_192 (from aesv8-gcm-armv8-unroll8.pl) as naked functions, in both the linux and Apple flavors. The importer now strips "openssl/arm_arch.h" too and defines __ARM_MAX_ARCH__=8, which that source guards on. Every other import regenerates byte for byte, also in the regenerate shell.
  • low::aarch64::aes_gcm::cipher runs them for AES-192 whenever PmullTable::eight_way (SHA3, for EOR3) is set, as the 128/256 kernels pick their eight-way variants. They take the same round keys (13), the same 12-entry H table, and the same Xi and counter layout as the s2n-bignum kernels, so no new key or table state is needed. Without SHA3, AES-192 keeps the composed path.
  • A naked function can't take #[target_feature] on stable, so the wrapper starts with .arch_extension aes. The extension only adds to what the assembler accepts afterwards.
  • kernels_match_composed gains a 400 B AAD, so the kernels also see one to a few blocks.
  • The aes192-gcm bench rows gain rind-dit-held, as the other AES-GCM groups have.
  • Valgrind hides SHA3, so ctgrind never reached any eight-way kernel, these or s2n-bignum's. admin/ct-qemu gains --target aarch64-unknown-linux-gnu: qemu-aarch64 -cpu max, the A64 hint #82/#83 marks, 64-bit trace records, and address randomization off. A ct-qemu-aarch64 dev shell and an aarch64-qemu job in the nightly ctgrind.yml, on ubuntu-24.04-arm, run it. A new ct_qemu_aes_gcm test asserts the eight-way path and traces key and table setup, seal, open, and a non-96-bit IV for every key size, at lengths through the short path and the kernels' loop and tails. The armv7 marks module (low/arm/ct_qemu.rs) is shared with aarch64.
  • DIT stays per rind call, as for every other aarch64 kernel.

Measurements

rind's time over aws-lc-rs's, interleaved A/B in one job per host (base = main, head = this branch), from 1200 B up. Both sides time the same bench rows.

Host seal open seal before open before
GitHub arm64, Neoverse N2 0.74-0.80x 0.80-0.84x 1.04-1.08x 1.03-1.07x
GitHub macOS, Apple M1 (virtual), DIT held 0.90-1.00x 0.97-1.02x 1.21-1.48x 1.23-1.45x
GitHub macOS, Apple M1 (virtual), per-call DIT 1.03-1.21x 1.01-1.16x 1.48-1.55x 1.38-1.45x
kixtop, Apple M4, DIT held 0.92-0.99x 0.93-0.99x 1.13-1.23x 1.06-1.15x
kixtop, Apple M4, per-call DIT 1.02-1.20x 1.01-1.17x 1.26-1.43x 1.17-1.27x

At 16 KB on Apple, both libraries run the same aws-lc routine and measure 1.00-1.03x. What remains without the DIT hold is the per-call DIT toggle (about 45 ns on M4). benchmarks/gaps.toml already records that gap for AES-128/256-GCM on kixtop (decided from moq-dev/rind#387). At 32 B nothing changed (short still runs).

AES-128 and AES-256 stay on s2n-bignum. The latest kixtop report (benchmarks/data/kixtop.json) puts them at 1.01-1.03x aws-lc-rs at 16 KB, per-call DIT included, so aws-lc doesn't beat the verified kernels by ~10%.

Validation

  • kixtop (Apple flavor): cargo test --release for aes_gcm unit tests, Wycheproof test_aes_gcm, and the aws-lc-rs crosschecks (aes_gcm, aes_gcm_any_iv, aes_gcm_every_length, which runs AES-192 in place at every length to 527 B). All pass.
  • just ci and just check pass locally; the linux flavor assembles for aarch64-unknown-linux-gnu.
  • ctgrind (admin/ctgrind unit --release aes_gcm) passes on GitHub's N2 runner but reaches only the composed fallback, since valgrind reports "sha3": 0 to rind.
  • admin/ct-qemu --target aarch64-unknown-linux-gnu on GitHub's arm64 runner (run): both planted leaks are caught, and ct_qemu_aes_gcm traces 81,100 records identically for both seeds. The armv7 admin/ct-qemu still passes locally with the new plugin and script.
  • admin/import-aws-lc regenerates every file byte for byte in the regenerate shell.

Impact

  • No public API change. AES-192-GCM on aarch64 cores with SHA3 runs aws-lc's unverified eight-way routine, recorded in ALGORITHMS.md's AES-GCM row and the aws-lc source row.
  • CI: a new read-only aarch64-qemu job in the nightly ctgrind workflow. Dev shell ct-qemu-aarch64. The admin/ct-qemu trace record grows from 8 to 12 bytes.

Choices made without asking

  • Only the eight-way routine is imported, for SHA3 cores, which covers N2 and every Apple M chip (the quest's targets). Cores without SHA3 (Neoverse N1, Cortex-A72/A76) keep the composed path rather than adding aws-lc's four-way aes_gcm_*_kernel. That kernel reads rounds at offset 240, which rind's keys lack, and no runner without SHA3 is available to measure it.
  • .arch_extension aes in the naked wrapper, rather than a nightly #[target_feature] on a naked function.
  • After review round 1, the constant-time gap is closed here by admin/ct-qemu on aarch64, which also covers s2n-bignum's eight-way kernels, rather than by reading the routines.

Alternatives

  • Sending every AES-192 length through the one-pass Rust short path was slower on N2 (1.14-1.32x), per the quest.
  • aws-lc's four-way kernel everywhere: aws-lc-rs itself runs it on N2, where the eight-way one measured 0.74-0.84x its time.

Follow-ups

  • AES-192-GCM on aarch64 cores without SHA3, via aws-lc's four-way kernel. Recommend: drop. AES-192 is rare, those cores are old, and nothing can measure it today.

Models

Size: M
Implementer: Claude Code, claude-opus-5-5, high effort

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, high effort
  • Session: 01a1b4f6-4f9d-78d0-b0b1-13bfa62f8c00, round 2
  • Head: b5e74e8
  • Base: origin/main at 6dbdebd
  • Score: 10/10, The new QEMU validation closes the constant-time coverage gap, and no remaining defects were identified.
  • Findings: none
  • The last round's findings, at 0f583a1:
    • Fixed: medium, rind/src/low/aarch64/aes_gcm.rs:310: [P2] Exercise both SHA3 kernels in a constant-time test; rind/src/low/aarch64/aes_gcm.rs:501 exercises both SHA3-gated AES-192 routines under QEMU in CI; the identical test and harness passed [the verified run](https://github.com/moq-dev/rind/actions/runs/42353088723), detecting both planted leaks and comparing 81,100 matching records.

kixelated and others added 5 commits November 6, 2026 23:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title AES-192-GCM on aarch64 is level with aws-lc-rs for large messages perf(aes-gcm): run aws-lc's eight-way AES-192-GCM on aarch64 with SHA3 Nov 7, 2026
…ch64

Valgrind hides SHA3, so ctgrind never reached the eight-way kernels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 08:01
@pullfrog

pullfrog Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Your OpenCode Go account is out of credit. Top it up, then re-trigger Pullfrog.

OpenCode Go's response
provider error: Insufficient account funds

Top up OpenCode Go →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using glm-5.3

@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 74421e78-7f42-42cf-a45f-f0a0cbaf8a74
Commits

Reviewing files that changed from the base of the PR and between ff50397 and b5e74e8.

Files selected for processing: 17

  • .github/workflows/ctgrind.yml
  • ALGORITHMS.md
  • CONTRIBUTING.md
  • admin/ct-qemu
  • admin/ct-qemu-plugin.c
  • admin/import-aws-lc
  • flake.nix
  • quest/m2/speed/README.md
  • quest/m2/speed/aes192-gcm-aarch64.md
  • rind-bench/benches/aes_gcm.rs
  • rind/src/low/aarch64/aes_gcm.rs
  • rind/src/low/aarch64/aws_lc/aesv8_gcm_8x_dec_192.apple.s
  • rind/src/low/aarch64/aws_lc/aesv8_gcm_8x_dec_192.linux.s
  • rind/src/low/aarch64/aws_lc/aesv8_gcm_8x_enc_192.apple.s
  • rind/src/low/aarch64/aws_lc/aesv8_gcm_8x_enc_192.linux.s
  • rind/src/low/aarch64/mod.rs
  • rind/src/low/arm/ct_qemu.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 210d910 Nov 7, 2026
12 of 13 checks passed
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