Skip to content

ChaCha20 on PPC64LE: verified block function and keystream XOR - #109

Draft
alex wants to merge 3 commits into
claude/cool-hamilton-crn96s-tcbfrom
claude/cool-hamilton-crn96s-chacha20
Draft

alex wants to merge 3 commits into
claude/cool-hamilton-crn96s-tcbfrom
claude/cool-hamilton-crn96s-chacha20

Conversation

@alex

@alex alex commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Part of #55. Stacked on #108 (the PPC64LE TCB), which is stacked on #97 (CI). There are no TCB or spec changes. This PR adds implementations proven against the existing Spec.ChaCha20.blockContract, Spec.ChaCha20.xorContract and Spec.Zeroize.zeroizeContract, plus the proof framework they need.

Commits

  1. Proof: PPC64LE framework (symbolic execution, taint tracking, calls): Proof/Framework/PPC64LE/{Exec,Taint,Inline,Call}.lean, ported from the AArch64 framework:

    • The runBlock_cons/runStep_some/runBlock_nil stepping lemmas.
    • Proofs that sp is unchanged, and LR too in code without calls.
    • The taint analysis for VG.Taint.constantTime … (by taint_decide).
    • Running verified code with wider permissions (Exec.widen), registers that code never writes (Exec.gpr), calls of verified functions (WP.call) and the ELFv2 frames that save LR (WP.frameReg).

    32-bit values are tracked as the low word of a register, since add/subf/logical instructions act on all 64 bits; lo32_add and friends push the low word through them. The byte-reversed loads use the target-independent Proof/Framework/Bswap.lean lemmas, so no PPC64LE module imports another target's (ci/check_lean_imports.py).

  2. ChaCha20 on PPC64LE:

    • vg_chacha20_block keeps the 16 state words in the low words of r5–r12 and r14–r21. It saves the nonvolatile r14–r21 in the working half of buf (bytes 64–127) and restores them at the end; r0 is the temporary for adding the input state. Rotations are rotrwi (rlwinm), which read the low word and ignore the carries the 64-bit adds leave in the high word.
    • vg_chacha20_xor follows the AArch64 implementation. It calls vg_chacha20_block once per 64 bytes of data and XORs min(64, remaining) bytes of the output into the data, a byte at a time. It then increments the counter word. state and buf stay in r3/r4, which the block function never writes. The data pointer and remaining length are kept in the nonvolatile r22/r23. Our caller's r22/r23, and the return address (mflr r0), are saved in buf[256..280), so no stack is used (stack := 0, as on the other targets). The proof tracks r14–r21 across the calls through the block function's ABI guarantee.
    • Proofs are in Proof/ChaCha20/PPC64LE/. Both artifacts are registered (in Api form) in Artifacts/ChaCha20/PPC64LE.lean, and src/asm/powerpc64le/ is regenerated.
  3. Zeroization on PPC64LE: since main added vg_zeroize (crate::zeroize, used by ChaCha20's Drop), every architecture that builds ChaCha20 needs it. It is the AArch64 algorithm, in Impl/Zeroize/PPC64LE.lean: std of zero over len / 8 words, then stb over len % 8 bytes. The proof in Proof/Zeroize/PPC64LE.lean follows Proof/Zeroize/AArch64.lean and its shared Proof/Zeroize/Common.lean. The artifact is registered in Artifacts/Zeroize/PPC64LE.lean. A small crun tactic (Proof/Framework/PPC64LE/Run.lean) steps short blocks, as the AArch64 proofs' does.

Rust

README

The table is generated. ci/algorithms_table.py now knows PPC64LE (ARCHES gets "powerpc64", and ASM_DIRS maps it to src/asm/powerpc64le/), and the ✅ definition includes it. ChaCha20 is ✅ on PPC64LE; every other algorithm is ❌ there for now.

Testing

On #97's head (main at 48dfb92), these all pass:

  • lake build and Emit.lean --check
  • ci/check_lean_imports.py, check_lean_speed.py, check_vectors.py, check_arch_gates.py, check_variants.py, algorithms_table.py --check
  • cargo fmt, and cargo clippy -D warnings on the host and for ppc64le
  • cargo test on x86-64, and cross-built for powerpc64le-unknown-linux-gnu under qemu-ppc64le (the unit tests, including zeroize's, and the Wycheproof ChaCha20 vectors)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE

Copy link
Copy Markdown
Member

This PR still uses the old layout that the conflict restructure (#144, #147, #148, #150) replaced on main. Moving it over makes it add files instead of editing shared lists, so it stops conflicting with other PRs.

What to move:

  • Artifacts.lean entries (vg_chacha20_block on PPC64LE): move them out of lean/VerifiedGarbage/Artifacts.lean into registration files, one per algorithm and target: lean/VerifiedGarbage/Artifacts/ChaCha20/PPC64LE.lean. Each defines VG.Artifacts.<Alg>.<Target>.artifacts : List Artifact (see Artifacts/Selftest/X86_64.lean or Artifacts/Scrypt/X86_64.lean); Emit.lean finds them automatically. The legacy SHA/HMAC/ChaCha20/MD5/SHA-1/SHA-3 entries are also moving into such files in a pending PR, so if a file you need already exists by then, append to its list.
  • README table: if README: generate the algorithm table from the code #151 lands, the table is generated; add a docs/algorithms/<alg>.toml and run python3 ci/algorithms_table.py instead of editing README.md by hand.

This PR is stacked on another branch, so apply the same moves on that base first.

See the "Keeping parallel PRs from conflicting" section of CLAUDE.md and step 4 of "Adding a primitive".


Generated by Claude Code

@alex
alex force-pushed the claude/cool-hamilton-crn96s-tcb branch from ec8a97f to 02073e3 Compare September 28, 2026 22:10
@alex
alex force-pushed the claude/cool-hamilton-crn96s-chacha20 branch from 109b8e5 to 93ee3bf Compare September 28, 2026 22:10

alex commented Sep 28, 2026

Copy link
Copy Markdown
Member Author

Done: the whole stack (#97 → #108 → #109 → #110 → #111 / #117) is rebuilt on current main (d0c1a92) in the new layout, and force-pushed.

In this PR (93ee3bf):

  • vg_chacha20_block on PPC64LE is registered in lean/VerifiedGarbage/Artifacts/ChaCha20/PPC64LE.lean; Artifacts.lean is untouched.
  • src/chacha20.rs and tests/wycheproof/chacha20.rs name PPC64LE in their own inner #[cfg(...)]; lib.rs and tests/wycheproof/main.rs are untouched.
  • The README table is regenerated by ci/algorithms_table.py, which now knows PPC64LE (ARCHES gets "powerpc64", and src/asm/powerpc64le/ for its assembly). Since ✅ now includes PPC64LE, the SHA-256 and HMAC rows read "x86-64, ARM64, ARMv7, x86" until SHA-256 on PPC64LE: verified compress/init/update/finalize #110 and HMAC-SHA-256 on PPC64LE: verified init/finalize #111.

lake build, Emit.lean --check, the Lean/vector/table checks, fmt/clippy (host and ppc64le) and cargo test (host, and ppc64le under qemu) pass on every branch of the stack.


Generated by Claude Code

@alex
alex force-pushed the claude/cool-hamilton-crn96s-tcb branch from 02073e3 to 6625c2d Compare September 29, 2026 23:02
@alex
alex force-pushed the claude/cool-hamilton-crn96s-chacha20 branch from 93ee3bf to c28f602 Compare September 29, 2026 23:16
@alex alex changed the title ChaCha20 on PPC64LE: verified block function ChaCha20 on PPC64LE: verified block function and keystream XOR Sep 29, 2026
@alex
alex force-pushed the claude/cool-hamilton-crn96s-chacha20 branch from c28f602 to fb99738 Compare October 1, 2026 02:42
@alex
alex force-pushed the claude/cool-hamilton-crn96s-tcb branch 2 times, most recently from 649e7eb to 5197baf Compare October 1, 2026 13:14
@alex
alex force-pushed the claude/cool-hamilton-crn96s-chacha20 branch from fb99738 to c8470e8 Compare October 1, 2026 13:14
claude added 3 commits October 1, 2026 13:18
The rewrite lemmas for running PPC64LE blocks symbolically (Exec.lean,
including that the stack pointer, and in code without calls the link
register, are unchanged), and the taint analysis that proves constant time
(Taint.lean). 32-bit values are tracked by the low word of a register, since
add, subf and the logical instructions act on all 64 bits; lo32_add and
friends push the low word through them.

Running verified code from a state that permits more memory, the registers
code never writes, calls of verified functions (WP.call, WP.callF) and the
48-byte ELFv2 frames that save the link register (WP.frameReg), ported from
the AArch64 framework (Inline.lean, Call.lean).

Refs #55

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE
vg_chacha20_block keeps the 16 words in the low words of r5-r12 and
r14-r21, saving the nonvolatile r14-r21 in the working space of buf
(bytes 64-127) and restoring them at the end; r0 is the temporary for
adding the input state. The proof carries the words as the low 32 bits of
the registers, since the 64-bit adds leave carries in the high words.

vg_chacha20_xor follows the AArch64 implementation: it calls the block
function for each 64 bytes, keeping state and buf in r3 and r4 (which the
block function never writes) and the data pointer and remaining length in
the nonvolatile r22 and r23. Our caller's r22 and r23 and our return
address (moved from the link register to r0) are saved in buf[256..280),
so no stack is used.

The ChaCha20 API and its tests (Wycheproof) now build on little-endian
powerpc64, and pass under qemu-ppc64le; its benchmark is no longer gated
off there. ChaCha20 chooses its implementation with the CPU detection in
cpu.rs, so its allowance for going unused on PPC64LE is dropped. The
algorithm table gets a PPC64LE column.

Refs #55

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE
ChaCha20's key objects wipe themselves with vg_zeroize (crate::zeroize),
which needs an implementation on every architecture that uses it: a
verified PPC64LE one, the same algorithm as AArch64's (words, then bytes).
Also a shared crun tactic for short PPC64LE blocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE
@alex
alex force-pushed the claude/cool-hamilton-crn96s-tcb branch from 5197baf to 9c74d3f Compare October 1, 2026 14:21
@alex
alex force-pushed the claude/cool-hamilton-crn96s-chacha20 branch from c8470e8 to 2f7ff6a Compare October 1, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants