Repository navigation
crypto: Ed25519 verify by one joint multiplication, scalars by Barrett (parity items 6, E3, E4) - #997
Conversation
Review of #997, round 1 (head 1d0f66a, stacked on #992)I read the commit against 41e89b9 and each changed file in full at the head ( ArchitectureThis is V1–V4 plus E3/E4 as the spec states them.
Correctness, checked by hand
|
1d0f66a to
cd7960d
Compare
Review of #997, round 2 (head cd7960d)I read the round-1 commit cd7960d. A range-diff confirms that cd2d860 is 1d0f66a rebased unchanged onto d633558. I also read Round-1 items
Suggestions
FindingBLOCKER R1.
Evidence
Verdict: SEND BACK (R1 only, a two-line change or one reworded clause). |
…uce by Barrett Verification ran two 256-step double-and-add multiplications, decoded R and the public key with an inversion and a power each, checked small order by three doublings and a projective compare, and reduced k with a 512-step shift-and-subtract loop. - Joint multiplication (Straus): [s]B - [k]A in one doubling chain over width-7 and width-5 NAFs of s and k (Hankerson, Menezes and Vanstone, algorithm 3.35). B's 32 odd multiples are a static table in affine form, built at compile time by const fn with one simultaneous inversion; A's 8 odd multiples are built per verification. Doublings run from projective coordinates into completed points and back (Hisil et al. 2008), additions take cached or affine operands. - R is not decoded: verification accepts when [s]B - [k]A encodes to R's bytes. A noncanonical R, one off the curve or a negative zero can never equal a canonical encoding, so the strict policy is unchanged; such an R is now refused as InvalidSignature rather than InvalidPoint. - Small order: R and the public key are refused when their encoding is one of the eight points of order dividing 8 (a decoded key's encoding is canonical, so the list is complete). - Decoding takes RFC 8032 section 5.1.3's single exponentiation, x = u v^3 (u v^7)^((p-5)/8). - Verification's scalars are `PublicScalar`, the only type the variable-time recoding accepts; the signing path's `S` does not convert to it. - Scalars mod L reduce 512-bit integers by Barrett (HAC 14.42, b = 2^64, k = 4) with one masked subtraction: the estimate is at most one short because 2^512/L - mu < 1/4, which a test asserts. Signing computes s = k a + r by one product and one reduction, and the signing key keeps its scalar reduced once. - The 2^255 - 19 field's arithmetic is const fn at its core, so the table is built from the same code that runs. New tests: Barrett's constant and bound, NAF digits for widths 2 to 8, the eight small-order encodings, the static table against repeated addition, noncanonical and negative-zero encodings refused, a small-order R that satisfies the cofactorless equation refused, and Barrett's edge values against num-bigint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- secret.rs states S's settled erasure: secret scalars are owners and their reduction erases its workspaces. - The scalar oracle's edge values are multiples of L and their predecessors below 2^512, as its comment now says; the false "short by up to two" is gone. - Tight::times is documented as the const form of Multiply::mul. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
barrett wiped q2, q3 L and r but not q1, the top five words of the secret input, nor q3, the quotient: both derive from the secret, and the contract says the reduction erases its workspaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5314dcb to
b824d27
Compare
Review of #997, round 3 (head b824d27, base main)I read the round-2 commit and checked the rebase. I ran nothing. Rebase
R1Fixed. Evidence
Verdict: LAND. |
Based on main (27ab916, after #990 and #992 merged; rebased unchanged). Three commits: round 0, a488389; round 1, b47ac40; round 2, b824d27.
Problem
Ed25519 verify was 3.38x ring on the T14 after #992 (179.7 us against 53.1), and sign 7.34x. Verification ran two separate 256-step double-and-add multiplications and decoded R and the public key with an inversion plus a power each. It checked small order by three doublings and a projective compare, and reduced k with a 512-step shift-and-subtract loop. Signing reduced three times bit by bit, multiplied mod L by 256 conditional additions, and re-reduced the static secret scalar on every signature.
This is the analysis's item 6 (V1–V4) with E3 and E4. Item 7's comb, which signing needs, is a later PR.
What changes
const fnwith one simultaneous inversion.InvalidSignatureinstead ofInvalidPoint. No caller in the tree matches on the variant.PublicScalar, which comes only fromcanonical(S) andreduce(k). The signing path's secretSdoes not convert to it.Secret. Both are erased on drop.const fn.product,squareandcarryareconst fn. The run-timeMultiplymethods call them, so the table is built from the same code that runs.Constant time: the sign path (
Point::muland the scalar arithmetic) keeps its fixed schedule and masked selects. All of verify's inputs are public, and its module contract says so.Review round 2 (b824d27)
barrettnow also wipesq1, the top five words of the secret input, andq3, the quotient, so the contract's "its reduction erases its workspaces" holds.cargo run -- --ci hostran on the rebased stack's head 2594dd3, which contains this commit unchanged; exit 0, 87 green: https://github.com/ToyOSOrg/evidence/tree/fca6eb8e7a4dc943b985d3b79eaeb263f704accb/pr-1000/r1-rebasedReview round 1 (cd7960d)
secret.rsstates the settled erasure ofS: secret scalars are owners, and their reduction erases its workspaces.Tight::timesis documented as theconstform ofMultiply::mul, tight by tight.PublicScalar::reducesharesbarrettwith the secret path and so wipes public temporaries. One reduction is simpler, and the wipes cost a few stores.cargo run -- --ci host: exit 0, 87 steps green. Logs: https://github.com/ToyOSOrg/evidence/tree/f3a54df6253a32c4ff3e3236ebe81b7a01664e1e/pr-997/r2scalar25519_reduction_and_signing_arithmetic_match_integer_oracle).Checks (round 0)
All at 1d0f66a. Logs: https://github.com/ToyOSOrg/evidence/tree/b76a3e5d3e80dff169164774dd61753ac77bf894/pr-997/r1
cargo run -- --ci host: exit 0, 87 steps green.cargo clippy -p toyos-crypto --all-targetswith the adopted lints and-D warnings, andcargo test -p toyos-crypto --release, in the three CI forms: exit 0 each. Rosetta x86_64 tests withsign: exit 0.x86_64-unknown-uefiandaarch64-unknown-uefibuilds: exit 0.cargo test --test toyos-build -- crypto_instrument_prints_its_table(QEMU x86_64): exit 0.ed25519all pass on the new verify and sign.--features sign --release --no-fail-fast, and reverted in the same run:barretts_constant_is_floor_of_2_512_over_lmul_addsquares a instead of multiplying by ba_small_order_r_satisfying_the_equation_is_refused(green before that test existed)noncanonical_encodings_and_negative_zero_are_refused(green before it existed)>=made>The first run also had "Barrett's second subtraction removed" green. The bound in "What changes" shows it unreachable; it is deleted, and the control above removes the one that remains.
Measurements
T14 (readback at 1d0f66a, judge exit 0): ed25519/verify/64 55.3 us against ring's 53.1, 1.041x (was 3.38x after #992, 34.5x at #932). ed25519/sign/64 86.4 us, 6.66x (was 7.34x); the comb is item 7. No other row moved materially.
Readback staged at 1d0f66a (
crypto_instrument,cryptocase; exit 2 = Staged, no machine touched). Its rows are this PR's full table; the Ed25519 rows are the ones this PR moves.Risks
W_B = 7(32 entries, 3.75 KiB) is the analysis's middle choice. M6 (widths 5, 7 and 8) is not measured. Verify is 1.04x on the T14, so the wider table (M6) is the first lever if it does not pass.🤖 Generated with Claude Code