Repository navigation
crypto: RSA R^2 by squarings, NIST curves as compile-time types, Ed25519 constants, a register barrier (parity items 1-4) - #990
Conversation
… products Key construction made R^2 mod n by 2 * 64 * words modular doublings (4096 for a 2048-bit key), each returning a 512-byte array by value. It now starts from 2^(bits-1) < n, doubles in place up to 2^words * R and finishes with six Montgomery squarings, each of which takes 2^t * R to 2^(2t) * R. The exponentiation walked all 32 bits of e and converted in and out of Montgomery form. It now raises s to e - 1 left to right from its top bit and finishes with one Montgomery multiplication by the unencoded s, which also leaves the Montgomery domain: 18 multiplications for 65537 instead of 36. The Montgomery multiplication interleaves each product row with its reduction step (CIOS, Koc, Acar and Kaliski 1996), writes into a caller's array, and carries with wrapping u128 and overflowing u64 operations, so the shipped profile's overflow checks and the per-row assertion are gone. All RSA data is public, so the final subtraction stays a branch. A unit test holds R^2 and s^e against num-bigint for moduli of 256 to 512 bytes, partial top limbs included, and odd exponents up to 2^32 - 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every p256()/p384() call ran Curve::new: two Mod::new, each 2 * N * 64 modular doublings for R^2, and three conversions to Montgomery form. Key parsing, generation and public_key each paid it, and public_key encoded its point and parsed it again. Each modulus is now a type implementing `Modulus<N>`, whose N0, R mod p and R^2 mod p are associated constants evaluated at compile time, and each curve is a type implementing `Curve<N>` with b and G already in Montgomery form. A `Residue<M, N>` is a Montgomery residue modulo M alone, so a field element cannot meet a residue modulo the order, nor a P-384 value a P-256 one; a `Scalar<C, N>` is an integer below n of one curve. This is stage 4's typed domains for these curves. The arithmetic carries with wrapping u128 and overflowing u64 operations, so the shipped profile's overflow checks and Mod::mul's per-row assertion are gone. The multiplication interleaves each product row with its reduction step (CIOS). Doubling is dbl-2001-b for a = -3 with the small multiples formed by additions, 3M + 5S instead of 5S + 11M; infinity doubles to Z = 0 without a select. Inversion squares and multiplies only where the public exponent p - 2 has a set bit. Generation draws its four candidates in one fill and keeps the same fixed schedule. public_key keeps the point it computed. The integer-oracle test now holds each modulus's constants, from_integer's refusal of p, from_wide's single reduction and squaring against num-bigint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ments Every point addition decoded d from bytes bit by bit, a 255-step loop, and multiplied by F::small(2) twice; every use of the base point decoded it with an inversion and a square root. F::from_bytes is now a const fn, so d, 2d, sqrt(-1) and B's extended coordinates (RFC 8032 section 5.1) are constants. The addition multiplies T1 T2 by 2d once and doubles Z1 Z2 by an addition. A unit test holds the constants to their definitions: B to the decoding of its encoding, 2d to d + d, d to -121665/121666 and sqrt(-1) squared to -1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ct::opaque was a volatile read of a stack local: a store and a load on every mask and select. It is now an assembly block whose template is a comment and which takes the word inout(reg) with pure, nomem, nostack and preserves_flags. It emits no instruction, and LLVM still cannot see that the output equals the input, so it cannot recover a mask's provenance and turn a masked select into a branch. The block lives in the arch module beside the crate's other assembly. Miri executes no assembly, so under Miri the volatile read stands in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #990, round 1 (head f5fbc03)I read the four commits, the full diff against the base 2661b9e, each changed file at the head, the callers in ArchitectureThis PR fits the approved plan (parity items 1–4) and keeps the erasure contract unchanged, as it says. Each modulus and curve is now a type with compile-time constants, which deletes the per-call Arithmetic, checked by hand
BarrierIt is FindingsBLOCKER
SUGGESTION
Tests and evidence
Context outside this PR
Verdict: SEND BACK (one header clause, item 1). After that fix, this PR is fit to LAND. |
- arch's module header scopes its dispatch contract to the dispatch functions; the value barrier dispatches nothing. - RSA uses montgomery's mac and n0 instead of its own copies. - A unit test holds generation's choice of the first valid of four candidates, and its refusal when none is valid; the selection loop's index no longer shadows the candidate's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #990, round 2 (head 78fdac6)I read the round-1 commit 78fdac6 against f5fbc03, the updated PR body, and the RSA and NIST call sites it touches. I ran nothing. Round-1 items
Evidence
No new findings. Verdict: LAND. |
Problem
toyos-crypto's asymmetric rows are 5–70x behind ring on both benches (#932). Part of that is setup and constants recomputed on every call, and arithmetic that the shipped profile's overflow checks and per-row assertions slow down. This PR takes the cheap, safe wins of the parity analysis's top ten (items 2, 3, 4 and the non-erasure parts of item 1). The erasure contract is unchanged: every field element, residue and workspace is still erased as it is on main. The owner has since conditionally approved boundary-only erasure; that change comes with the 5x51 field in the next PR, with its M1 measurement and a written contract.What changes
Modulus<N>, with N0, R mod p and R^2 mod p as compile-time associated constants; each curve is a type implementingCurve<N>with b and G in Montgomery form at compile time.Residue<M, N>is a residue modulo one modulus only, andScalar<C, N>an integer below one curve's order, so a field element cannot meet an order residue or a P-384 value a P-256 one (stage 4's typed domains for these curves). Nothing is constructed per call;public_keykeeps its point instead of encoding and re-parsing it; generation draws its four candidates in one fill on the same fixed schedule. Doubling is dbl-2001-b for a = -3 with small multiples as additions (3M + 5S, was 5S + 11M). Inversion multiplies only where the public exponent p - 2 has a set bit. The arithmetic carries with wrapping and overflowing operations, with no assertion per reduction row.F::from_bytesis aconst fn; d, 2d, sqrt(-1) and B's extended coordinates are constants. The addition multiplies T1 T2 by 2d once and doubles Z1 Z2 by an addition, instead of a bit-serial decode of d and two multiplications by 2 per addition, and the base point is no longer decoded with a square root per use.ct::opaqueis an emptyasm!block taking the wordinout(reg)withpure, nomem, nostack, preserves_flags, inarch/mod.rsbeside the crate's other assembly, instead of a volatile stack read. Under Miri, which runs no assembly, the volatile read stands in.Not in this PR: the 5x51 field rewrite, combs, windows, verify algorithms, and any change to per-temporary erasure.
Checks
All at f5fbc03 on the development Mac (aarch64-apple-darwin host). Logs: https://github.com/ToyOSOrg/evidence/tree/ae26e1f2ef9a28501c8d72472c14aa1c19229fac/pr-990/r1
cargo run -- --ci host: exit 0, 87 steps green. (An earlier run at b618d31f0 went red on clippy'sneedless_range_loopandchunks_exact_to_as_chunksin the new modules; fixed, and the rerun is this one.)cargo test -p toyos-crypto --releasein the three CI forms (dispatched features,--all-features, default): exit 0 each. Also--target x86_64-apple-darwin(Rosetta) withsign: exit 0.cargo build -p toyos-crypto --release --target x86_64-unknown-uefiandaarch64-unknown-uefi: exit 0.RUSTFLAGS="--cfg miri" cargo check --features portable: exit 0 (the Miri arm compiles).rsa::tests::power_and_r2_match_integer_oracle(R^2 and s^e against num-bigint, 7 modulus lengths from 256 to 512 bytes, e in {3, 17, 65537, 2^31+1, 2^32-1}, plus the s = n refusal);ed25519::tests::constants_match_their_definitions;nist_montgomery_domains_and_carries_match_integer_oraclenow also holds N0, R mod p, R^2, squaring,from_integer's refusal of p andfrom_wide.--features sign --release, and reverted in the same run (all builds exit 0; all tests exit 101):rsa::tests::power_and_r2_match_integer_oraclersa::tests::power_and_r2_match_integer_oraclemalformed_public_boundariesnist_montgomery_domains_and_carries_match_integer_oraclenist_montgomery_domains_and_carries_match_integer_oraclenist_curve_boundary_scalarsnist_curves_reference_ecdh_and_ecdsa,nist_curve_boundary_scalarsed25519::tests::constants_match_their_definitionsed25519_reference_verificationReview round 1 (78fdac6)
arch/mod.rs's header scopes its dispatch contract to the dispatch functions (the blocker).montgomery::macandmontgomery::n0instead of its own copies.nist::tests::generation_takes_the_first_valid_candidate: candidates [n, 0, k1, k2] give k1; four invalid giveEntropy. The selection loop's inner index is renamed.-D warnings) andcargo test -p toyos-crypto --releasein the three CI forms, exit 0 each.cargo run -- --ci hostran on crypto: a 5x51 field with typed bounds and erasure at the boundary (parity items 1 and 5) #992's head 41e89b9, which contains this commit unchanged (result in crypto: a 5x51 field with typed bounds and erasure at the boundary (parity items 1 and 5) #992).Measurements
The parity rows need the benches; the orchestrator runs them. Readback prepared:
cargo test --test toyos-build -- --metal --metal-readback <dir> crypto_instrumentat f5fbc03 staged thecryptocaseimage (exit 2, the harness's Staged verdict; no machine touched). No run was made of this image. The T14 probe boot at fb93764 measured this PR's NIST and RSA code unchanged, beneath #992; its rows are in #992.Risks
--cfg miricheck) but not run under Miri here; the stage-4 Miri run covers it.🤖 Generated with Claude Code