Skip to content

PPC64LE brainpoolP512r1 ECDH: [d]P by three-bit windows - #4769

Closed
alex wants to merge 4 commits into
mainfrom
claude/ppc64le-brainpool512-ecdh-v3
Closed

alex wants to merge 4 commits into
mainfrom
claude/ppc64le-brainpool512-ecdh-v3

Conversation

@alex

@alex alex commented Nov 8, 2026

Copy link
Copy Markdown
Member

Part of #4141 (the PPC64LE audit, finding 1). Stacked on #4722 (P-521 ECDH, three-bit windows), so it stays a draft until #4722 merges. It replaces #4763 (and #4761 before it), restacked onto #4722's latest head (5f5d77cc). That head takes Z^(p-2) by #4762's addition chain, so this PR's win_mulPowOk does too, the same change #4722 made for P-521.

Known red until #4722 is fixed: #4722's committed ecdh_p521.rs still declares P-521's internal field modules that ec_p521.rs already declares since #4762. The full emitter on #4722's head regenerates ecdh_p521.rs without them. I've left that file to #4722 and told its session. Any failure in that file or its clippy run is #4722's, not this PR's.

vg_ecdh_brainpoolp512r1 on PPC64LE computed [d]P with the Montgomery ladder. It now uses three-bit windows with the formulas for any a.

Why three bits, and why start from the top two bits

  • Four-bit windows don't fit. brainpoolP512r1's coordinates are 8 words, so an entry is 192 bytes. Sixteen entries from the end of the tables of bits (5632) would end at 8704, past the 8192-byte working space. Eight entries end at 7168, as for P-521 (PPC64LE P-521 ECDH: [d]P by three-bit windows #4722).
  • The top window would read past the table of bits. The scalar's 512 bits are 3 · 170 + 2. So PPC64LE P-521 ECDH: [d]P by three-bit windows #4722's loop of 171 windows from R = O would read bit 512 in its top window. That is one byte past the table of bits, the first entry of the next table (p − 2's bits, which MulPowOk still provides), which is 1. Instead, R starts at the table's entry for the top two bits (top2, then the usual masked scan, then R = D), and the 170 windows below follow.

What changes

No TCB, Spec or Rust change. The only crate change is ecdh_brainpoolp512r1.rs and its assembly.

Testing

Benchmark

Benchmarks run 42529524175, on #4761's branch, the same windows before the addition chain. It used arches=powerpc64, modules=ecdh_brainpoolp512r1, and base bd7184fc (main with the ladder and the old power):

Benchmark Base Head Change
ecdh_brainpoolp512r1/64 3.84 ms 2.11 ms 45.0% faster

Generated by Claude Code

P-521's sixteen entries of four-bit windows do not fit the working space past
the tables of bits, so its windows take three bits (Impl/Ecdsa/PPC64LE/Window3.lean):
a table of [e]P for e < 8, then for each of the 176 windows three Jacobian
doublings and one complete addition of the entry selected by a constant-time
scan. The proof is P-256's (Proof/Ecdsa/PPC64LE/P256/Window.lean) on P-521's
field and nine-word layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKUFHH4GefHP3pmPRSL4N4
brainpoolP512r1's sixteen entries of four-bit windows (192 bytes each) do not
fit the working space past the tables of bits, so its windows take three
bits, as P-521's do (Window3.lean), with the complete addition and the
Jacobian doubling for any a (rcb, dblJa), as brainpoolP384r1's four-bit
windows do (WindowA.lean): Impl/Ecdsa/PPC64LE/Window3A.lean.

The scalar's 512 bits are 170 windows and two bits above them: a 171st
window from R = O would read a bit past the table of bits (the first bit of
the table of p - 2's). So R starts at the entry of the top two bits
(mul3top), and the 170 windows below them follow. The proof is P-521's on
brainpoolP512r1's field and eight-word layout, with the doublings and the
addition for any a.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKUFHFbSpgWBYWAsEnfUks
Regenerated with the full emitter on #4722.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKUFHFbSpgWBYWAsEnfUks

alex commented Nov 8, 2026

Copy link
Copy Markdown
Member Author

Rust: lints (library) (and the Lean emit check) fail here because of #4722, not this PR. #4722's ecdh_p521.rs declared P-521's internal field modules a second time; ec_p521.rs already declares them since #4762. #4722 has since fixed it with a fast-forward (eaad41a0, regenerated by the full emitter), so this PR's diff is still only its own two commits. This branch's head is on 5f5d77cc, just before that fix. I'll restack it onto #4722 when #4722 merges, as this draft needs anyway, and CI will go green then.


Generated by Claude Code

Base automatically changed from claude/ppc64le-ecdh-p521 to main November 8, 2026 23:19
@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

This pull request has conflicts with main, found after 66a3329 landed on main: rebase it to resolve them. Before rebasing, see CLAUDE.md on checking how much Lean main changed, and on restacking a stacked pull request.

Posted by .github/workflows/conflicts.yml, once per push to a conflicting pull request.

alex commented Nov 8, 2026

Copy link
Copy Markdown
Member Author

Replaced by the PR for claude/ppc64le-brainpool512-ecdh-main: the same two commits, restacked onto main now that #4722 has merged. The full build and emit pass there.


Generated by Claude Code

@alex alex closed this Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants