Repository navigation
x86-64 AES-GCM: seal_gather copies a short text and seals it in place - #1350
Conversation
4e97b38 to
16dc7e5
Compare
|
I ported a fix into this PR in 7d57c54: the test formats the message from one value, so the assertion fits on one line. An instrumented run of the test now covers every line of its loop. If #1331 makes the same change, this commit becomes a no-op. #1354 is restacked on top. Generated by Claude Code |
|
Why are we adding a bunch of dispatch code to the rust, instead of just making the function faster? |
`vg_aes_gcm_seal_gather` now first compares the text's length with a threshold `t` of its variant. Below it, the slices are copied to the output, 64 bytes at a time through `xmm0`-`xmm3` (SSE2, so every variant can use it), then 16, then one, and encrypted there by one call of the same variant's `vg_aes_gcm_seal` (or `_seal_prepared`, for the prepared key context of #1331), which keeps the AVX-512 short path. Otherwise the streaming path runs as before. The threshold (`copyBelow`) is 768 bytes for variants with out-of-place interleaved loops (VAES + VPCLMULQDQ + AVX-512), about where the streaming path starts to pay for itself, and 2^31 - 1 for the others, whose streaming path copies the blocks too, one at a time. Proofs: the copy (`Gather/Copy.lean`, one loop lemma for all three widths), the slices gathered (`Gather/CopyLoop.lean`, ChaCha20-Poly1305's descriptor walk with this copy), the blocks of the path and the call of `vg_aes_gcm_seal` by its shared contract (`CopyPath.lean`, `SealCallee.lean`, `SealCall.lean`, `Prepared/GatherSeal.lean`), and constant time (`CopyCT.lean`; the length test compares with an immediate, which accesses no memory). No Spec or TCB change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
7d57c54 to
831394b
Compare
|
Answering this even though the PR has merged, since I missed it before: you're right. This PR has no Rust dispatch (its Rust changes are tests), but its copy-then-call- The follow-ups make
I'll answer review comments on each of those PRs as they come. Generated by Claude Code |
This builds on #1331 (now on
main) and also covers its prepared-context gather. #1354 is stacked on this PR.This comes from the rustls provider analysis in #991. The provider seals TLS records with
AesGcm::encrypt(vg_aes_gcm_seal_gather) from two pieces, the payload and the content-type byte. On x86-64 that is slower than copying into the output and callingencrypt_in_place(#1228). This is the first of the PRs addressing that.What changes
vg_aes_gcm_seal_gathernow compares the text's length with a threshold of its variant:dstand encrypts them there with one call of the same variant'svg_aes_gcm_seal(_seal_preparedfor Speed up AVX-512 AES-GCM TLS records with prepared keys and direct gather #1331's prepared context). That keepsseal's AVX-512 short path. The copy moves 64 bytes at a time throughxmm0–xmm3, then 16 bytes, then single bytes. It uses only SSE2, so every variant can use it.The threshold (
copyBelowinGeneric/AesGcm/X86_64/AesGcm.lean):There's no Spec or TCB change. The Rust changes are tests only:
backends_agreenow crosses the 768-byte threshold and adds the TLS shape (payload plus one byte).Proofs
Gather/Copy.lean: the copy. One generic chunk-loop lemma covers the 64-, 16- and 1-byte loops.Gather/CopyLoop.lean: the slices gathered. It reuses ChaCha20-Poly1305's descriptor walk (next/advanceandGatherPre/GInvfrom ChaCha20-Poly1305: out-of-place encryption from a list of slices (seal_gather) on every target #1324) with this copy.Gather/CopyPath.lean,SealCallee.lean,SealCall.lean,Prepared/GatherSeal.lean: the path's blocks, and the call ofvg_aes_gcm_sealthrough its shared contract, the same way the gather already calls the streaming functions.Gather/CopyCT.lean: constant time. The copy branches only on public lengths and descriptors. The length test compares with an immediate, so it accesses no memory (rel_cmpImm), and the proof holds for any threshold below 2³¹.Gather/Fn.lean,Gather/CT.lean,Gather/Verified.lean: recomposed. The streaming path's proofs are unchanged.The emitted change is only to the 12
vg_aes_gcm_seal_gather*functions insrc/asm/x86_64/gcm.rs. The other 155 GCM functions are byte-identical.Measurements
These are
encryptof two pieces (n bytes, then 1 byte) with a 13-byte AAD and a warmed key, on an Intel Xeon with VAES/VPCLMULQDQ/AVX-512 (Ice Lake class, 2.1 GHz, noisy cloud host). Each value is the minimum of 33 interleaved samples, in ns. "Before" is #1331's code without this PR. "copy+ip" copies the pieces into the output and callsencrypt_in_place. Smaller backends were selected withVG_CPU_FEATURES.VAES + VPCLMULQDQ + AVX-512
At and above the threshold the code is the same as before, so the differences there are noise.
AES-NI + PCLMULQDQ + AVX (
aes,pclmulqdq,ssse3,avx)VAES + VPCLMULQDQ, AVX2 (
…,vaes,vpclmulqdqwithout AVX-512)AES-NI + PCLMULQDQ (
aes,pclmulqdq,ssse3)What's left (follow-up PRs, in progress)
A fixed ~15–20 ns remains on every gather call: the descriptors, the gather's argument frame and work slots, and the extra call layer. With zero-length text, the gather costs 77 ns against copy+ip's 59 ns. AES-GCM on x86-64: encrypt copies a short text and seals it in place #1354, stacked on this PR, gives
AesGcm::encrypta Rust fast path for that: below a threshold it copies the pieces intooutand seals them in place.Out-of-place interleaved loops for AES-NI + AVX and VAES on 256-bit registers. These are planned as follow-up PRs. Their upper bound is the cost of the copy, measured as the gap between
encrypt_in_placewithout a copy and copy+ip:AVX-512 block remainder (follow-up PR). On AVX-512, the blocks left after the 16-block interleaved loop (1–15) go through separate
vg_aes_ctr32andvg_ghashcalls. Even in place, that costs about 25 ns plus 5 ns per block: 31 blocks take 221 ns against 135 ns for 32, and 47 take 235 ns against 155 ns for 48. Speed up AVX-512 AES-GCM TLS records with prepared keys and direct gather #1331's two-slice path pays the same cost whenever its payload is not a multiple of 16 blocks.AVX-512 pieces that end mid-block (follow-up PR, after the remainder fix). In the gather's streaming path, such a piece costs about 100 ns, because the partial block is buffered.
Validation
Rerun after restacking onto
main:lake build(full) andlake env lean --run Emit.lean(no changes to the generated code; axiom and compiler audits).cargo testwith Wycheproof on the default backend, and withVG_CPU_FEATURES=nonefor the AES-GCM tests. Before the restack, the AES-GCM tests also ran withaes,pclmulqdq,ssse3,avxand all eleven x86-64 backend configurations.cargo fmt --check,cargo clippy --all-targets -- -D warnings.ci/check_lean_imports.py,check_lean_speed.py,check_vectors.py,check_arch_gates.py,check_variants.py,check_mcdt.py,cpu_tests.py lint,algorithms_table.py.🤖 Generated with Claude Code
https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq