Skip to content

sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in - #1005

Merged
Japabu merged 2 commits into
mainfrom
wt/p384-bignum
Oct 22, 2026
Merged

Japabu merged 2 commits into
mainfrom
wt/p384-bignum

Conversation

@Japabu

@Japabu Japabu commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The owner ruled on 2026-10-21 that fiat-crypto is a dependency of no kind. russh's p384 0.14.0-rc.8 depends on fiat-crypto 0.3.0 unless cfg(p384_backend = "bignum") is set:

[target.'cfg(not(p384_backend = "bignum"))'.dependencies.fiat-crypto]

Nothing in the tree set that cfg. So fiat's P-384 field and scalar arithmetic was compiled into system/sshserver: into the guest image, and on every host into --ci host's sshserver test and its Linux, macOS and Windows app builds.

Change

  • .cargo/config.toml sets --cfg=p384_backend="bignum" in [build] rustflags. Cargo uses those flags for any triple with no [target] table: the host and the host-app triples.
  • The two ToyOS guest tables carry the same flag, because a table's flags replace [build]'s. This follows the UEFI targets, which already set curve25519_dalek_backend.
  • Only russh reaches p384 0.14 (through russh and internal-russh-forked-ssh-key), so the setting goes with russh. The SSH track's stage 2 exit now says .cargo/config.toml sets no p384_backend.

Cargo.lock still lists fiat-crypto, because a lock records every optional target dependency whether or not a build selects it. toyos-crypto's own fiat dev-dependency goes with #993.

Trade-off

Until stage 2, sshserver's P-384 runs on the backend that p384's documentation calls "experimental ... might also have bugs", where fiat's is formally verified. The SSH track issue records this, with its owner and exit.

sshserver uses P-384 in two places:

  • It verifies a client's ecdsa-sha2-nistp384 publickey signature, which is untrusted input before authentication.
  • It signs with a P-384 host key, when the operator's host-key file holds one. A minted key is Ed25519.

It doesn't negotiate ecdh-sha2-nistp384, because russh's default order leaves it out.

On timing, both backends are constant-time by design for the field and scalar operations, and point arithmetic comes from primeorder either way (review r1's analysis). Neither is machine-checked for timing.

Checks

  • Guest image, cargo run -- --build-only on main at 27ab916 and at 075db4185, both exit 0:

    • before, it shows Compiling fiat-crypto v0.3.0, and the x86_64-unknown-toyos p384 fingerprint lists fiat_crypto;
    • after, no fiat compile happens and no fiat artifact exists.

    The guest tables have not changed since.

  • Host CI, cargo run -- --ci host at 177651f: exit 0, 87 steps green.

  • Upstream suite, p384 0.14.0-rc.8's own, with its lock aligned to ToyOS's: crypto-bigint 0.7.3, and every shared package at ToyOS's version, num-bigint 0.3.3 aside, which no build reaches. Under the cfg, cargo test --all-features exits 0: 34 + 6 + 11 tests (Wycheproof ECDSA among them) and 2 doctests. The log records cargo tree -i crypto-bigint.

No guest test exercises sshserver's P-384. The change relies on p384's own suite on that backend.

Evidence:

Risks

🤖 Generated with Claude Code

https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q

…compiles in

The owner ruled on 2026-10-21 that ToyOS takes fiat-crypto as a
dependency of no kind. russh's `p384` 0.14.0-rc.8 depends on
`fiat-crypto` 0.3.0 unless `cfg(p384_backend = "bignum")` is set
(`[target.'cfg(not(p384_backend = "bignum"))'.dependencies]`), and nothing
set it, so fiat's P-384 arithmetic was compiled into sshserver. The guest
targets now set it, as the UEFI targets already set
`curve25519_dalek_backend`. sshserver is the only guest crate that reaches
`p384`, and the setting goes with russh at stage 2 of the SSH track, whose
exit now says so.

`Cargo.lock` still lists fiat-crypto: the lock records every optional
target dependency whether or not a build selects it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Japabu

Japabu commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #1005, round 1 (head 57ea881)

Architecture

The approach fits. p384 0.14.0-rc.8 offers cfg(p384_backend = "bignum") as its supported way to drop fiat-crypto. Setting that cfg in .cargo/config.toml follows the existing curve25519_dalek_backend precedent, and it goes away with russh at stage 2. No new mechanism is added, and no production code or test changes. The diff is +8/−2 across .cargo/config.toml and the SSH track issue.

The change does not finish the job, though. It covers only the two guest triples, while ToyOS's own gate still compiles sshserver, and so fiat, for every host.

Constant time. On the bignum backend, FieldElement and Scalar come from primefield::monty_field_arithmetic! on crypto-bigint's ConstMontyForm:

  • add, sub, neg and Montgomery mul are constant-time;
  • invert is SafeGcdInverter::invert, Bernstein–Yang with VARTIME = false (crypto-bigint-0.7.3/src/modular/const_monty_form/invert.rs:25);
  • sqrt uses fixed public exponents;
  • the *_vartime helpers (pow_vartime, sqn_vartime, from_hex_vartime) only take public constants.

Point arithmetic and scalar multiplication come from primeorder and do not depend on the backend. So for secret-dependent work (ECDSA signing with a P-384 host key: nonce multiplication and k⁻¹), the backend is constant-time by design, the same as fiat. Neither backend is machine-checked for timing.

What actually changes is functional-correctness assurance. Upstream calls fiat "formally verified" and bignum "experimental ... might also have bugs" (p384-0.14.0-rc.8/src/lib.rs:28-31). That matters most where sshserver feeds P-384 untrusted input (see finding 3).

Who uses P-384 in sshserver.

  • It verifies client ecdsa-sha2-nistp384 publickey signatures. These are untrusted inputs on the authentication path.
  • It signs only if the host-key file holds a P-384 key. host_key() (system/sshserver/src/main.rs:72) accepts any OpenSSH key type, but a minted key is Ed25519.
  • It does not negotiate ecdh-sha2-nistp384. sshserver uses russh's default Preferred, and SAFE_KEX_ORDER (russh/src/negotiation.rs:103-117 at the pinned 389804d) leaves it out.

Findings

  1. BLOCKER — .cargo/config.toml:39-48: fiat-crypto still compiles for sshserver on every host. sshserver declares no [package.metadata.toyos.host], so build/userlandhost.rs treats it as an app. --ci host then runs cargo test on it and checks it for Linux and Windows, and none of those builds gets the cfg. The PR's own p384-ci.log at 075db4185 shows it:

    • line 9864: Compiling fiat-crypto v0.3.0, inside === [ci] system/sshserver (line 9829);
    • line 10332: Checking fiat-crypto v0.3.0 in "the apps for linux";
    • line 10617: the same in "the apps for windows".

    The macOS build at line 10447 builds sshserver too.

    This contradicts the title ("no fiat-crypto compiles in") and the owner's "dependency of no kind" ruling. It also means sshserver's host tests run a different P-384 backend from the one the guest ships. toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 removes toyos-crypto's host dev-dependency, and this host path needs the same treatment.

    Required: apply the cfg to host builds as well, or take the question explicitly to the owner and record the answer. A [build] rustflags entry is one option. It applies wherever no [target.<triple>] table matches, which covers the host test and the linux-gnu, windows-msvc and macOS app builds; verify that. The file header's "a host build takes no flags from here" and the issue line's "guest targets'" would then change too.

    Evidence: --ci host with no Compiling/Checking fiat-crypto line in the sshserver and host-app steps. Until toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 lands, the workspace step's toyos-crypto dev-dependency still compiles fiat, so attribute each line to its step.

  2. BLOCKER — .cargo/config.toml:41-42: the comment cites issues/toyos-crypto-is-in-the-tree-on-par-with-ring-and-secure.md for the ruling. At this head and on main, that issue (lines 314-317) says the opposite: toyos-crypto is "differentially tested against fiat-crypto … as a dev-dependency". The ruling is written down only in toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993's version of the issue.

    toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993's version also says (its lines 326-329) that p384_backend = "bignum" is a cfg "which nothing sets: fiat code is compiled into sshserver today". That becomes false once this PR lands.

    Required: whichever of toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 and sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in #1005 lands second must reconcile both records. If sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in #1005 lands first, it must not cite a record that contradicts its comment: drop or adjust the citation. Whoever lands after it must then fix the stale "nothing sets" passage.

  3. BLOCKER — issues/the-ssh-server-is-toyos-own-and-russh-is-gone.md:62-63 and the PR's Risks: the trade-off is not recorded honestly. The Risks section says only that bignum is "the backend p384 itself offers and tests". It omits that upstream calls it experimental and possibly buggy, compared with the formally verified fiat. It also omits that sshserver runs it on untrusted client signatures during authentication.

    Required: record the compromise in the SSH track issue, with its owner and its exit (stage 2): sshserver's P-384 (client ecdsa-sha2-nistp384 verification, and signing with an operator-supplied P-384 host key) runs on the backend upstream labels experimental until russh is gone.

    For the orchestrator: the PR text should say the same. It should also drop ecdh-sha2-nistp384 as a P-384 use, since sshserver does not offer it.

  4. BLOCKER — evidence pr-1005/p384-bignum-tests.log: the upstream suite did not test the arithmetic that ships. It ran in a copy of the registry source, under the crate's packaged Cargo.lock, which is byte-identical to ~/.cargo/registry/.../p384-0.14.0-rc.8/Cargo.lock. That lock resolves crypto-bigint 0.7.1. ToyOS's lock gives elliptic-curve 0.14.0-rc.29 and primefield 0.14.0-rc.8 crypto-bigint 0.7.3 (Cargo.lock:1176, :3110).

    On this backend, the field and scalar arithmetic is crypto-bigint's ConstMontyForm and SafeGcdInverter. The PR's only correctness evidence therefore covers a different implementation from the one in the image.

    Cheapest sufficient check: in the scratch copy, run cargo update -p crypto-bigint --precise 0.7.3 and align any other overlapping version with ToyOS's lock. Then run cargo test --all-features again under the cfg (the suite includes Wycheproof ECDSA), and record cargo tree -i crypto-bigint with the log.

Verified, no finding

  • Guest targets. At 075db4185 the x86_64-unknown-toyos p384 fingerprint carries the cfg, and its dependencies are sha2, primefield, ecdsa_core, elliptic_curve and primeorder, without fiat. The base build compiles fiat and the fixed build does not (p384-fiat-proof.txt, the base and fix build logs). The rebase changed nothing: 075db4185 and 57ea881 carry an identical patch.
  • aarch64. aarch64-unknown-toyos has the identical entry, which inspection suffices for. No guest build passes RUSTFLAGS/CARGO_ENCODED_RUSTFLAGS over sshserver: build/build.rs:2194 sets RUSTFLAGS only for tests/guest cdylib consumers, and none of their locks contains p384.
  • Kernel and loader. They do not reach fiat. The two curve25519-dalek entries name fiat only behind cfg(curve25519_dalek_backend = "fiat"), which the tree never sets.
  • Cargo.lock. Correctly unchanged. A lock resolves cfg-target dependencies whatever the platform, so p384 0.14.0-rc.8 still lists fiat-crypto 0.3.0 (Cargo.lock:2804).
  • NOTICE and build/licence.rs. No fiat file is committed, and neither names fiat, so neither needs a change.
  • Issue exit line. The new stage 2 exit line is accurate and concise for the guest-only setting. It needs rewording if finding 1 extends the cfg.

SEND BACK

…ecorded

Review r1 of #1005. `--ci host` compiled sshserver, and so fiat-crypto,
for the host test and checked it for Linux and Windows: a build for a
triple with no `[target]` table took no flags from the config. `[build]
rustflags` is what cargo applies to exactly those builds, and the ToyOS
tables, whose flags replace it, carry the cfg as well.

The SSH track records the compromise: until stage 2, sshserver's P-384
(client `ecdsa-sha2-nistp384` verification before authentication, and an
operator's P-384 host key) runs on the backend upstream calls
experimental. The config comment no longer cites the crypto issue, whose
record of the ruling lands with #993.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
@Japabu Japabu changed the title sshserver: russh's p384 on its bignum backend, so no fiat-crypto compiles in sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in Oct 22, 2026
@Japabu

Japabu commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Response to review r1, head 177651f. Evidence: https://github.com/ToyOSOrg/evidence/tree/43e01aa96e4d8fc0d115d08ef827f434fbaebe02/pr-1005/r1

  1. Host builds. Fixed with [build] rustflags, which cargo applies to any triple without a [target] table. --ci host at 177651f shows no fiat line in system/sshserver or in the apps for Linux, macOS and Windows (fiat-lines-by-step.txt). The two lines left are main's toyos-crypto dev-dependency, which toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 removes. The config header no longer says a host build takes no flags.
  2. Crypto issue record. The config comment no longer cites the crypto issue. toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993's record now says sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in #1005 sets the cfg for every build, host and guest, and that fiat compiled into sshserver before it. That stays true in either landing order.
  3. Trade-off. Recorded in the SSH track issue's preamble with owner and stage-2 exit. The stage-2 exit line is now ".cargo/config.toml sets no p384_backend". The PR's Risks section is corrected, and ecdh-sha2-nistp384 is dropped.
  4. Upstream suite on 0.7.3. crypto-bigint 0.7.3, with every shared package aligned to ToyOS's lock (num-bigint 0.3.3 aside, which no build reaches). cargo test --all-features under the cfg: exit 0, 34 + 6 + 11 tests and 2 doctests. cargo tree -i crypto-bigint opens the log.

Japabu added a commit that referenced this pull request Oct 22, 2026
…measure

russh's `p384` is on its `bignum` backend under #1005, so the fiat record
names the dependency as one no guest build selects. The T14's P-256
finding is re-measured on #1000's arithmetic, now on main, with a boot
that sets DOITM beside the one that does not.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqpxbTfpvcbx1FJEy3Eny9
Japabu added a commit that referenced this pull request Oct 22, 2026
#1005 sets `p384_backend = "bignum"` for host and guest builds alike, so
the record says that and what held before it, true in either landing
order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Japabu added a commit that referenced this pull request Oct 22, 2026
…measure

russh's `p384` is on its `bignum` backend under #1005, so the fiat record
names the dependency as one no guest build selects. The T14's P-256
finding is re-measured on #1000's arithmetic, now on main, with a boot
that sets DOITM beside the one that does not.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqpxbTfpvcbx1FJEy3Eny9
Japabu added a commit that referenced this pull request Oct 22, 2026
#1005 sets `p384_backend = "bignum"` for host and guest builds alike, so
the record says that and what held before it, true in either landing
order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
@Japabu

Japabu commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #1005, round 2 (head 177651f)

Focused on round 1's four blockers and the new commit, 177651f (+22/−8 across .cargo/config.toml and the SSH track issue).

Architecture

The fix is now a single flag in two places. [build] rustflags covers every triple that has no [target] table, which means the host and the host-app checks. The two ToyOS tables repeat the flag because a target table's flags replace [build]'s. The kernel, UEFI and musl tables don't get the flag, and none of them reaches p384 0.14. No harness step sets RUSTFLAGS or CARGO_ENCODED_RUSTFLAGS over sshserver. The only setter, build/build.rs:2194, is for tests/guest cdylib consumers, and none of those uses p384.

Round 1 blockers

  1. Resolved. Host builds no longer compile fiat. I checked p384-ci-r1.log at 177651f, which exits 0:

    • p384 v0.14.0-rc.8 now builds without fiat in the system/sshserver host test (lines 10839/10847), the Linux check (11309/11316) and the Windows check (11579/11587).
    • The macOS step (11350) reuses the host-test p384 artifact, which carries the same flags.
    • Only two fiat lines remain. Line 6936 is in "toyos-crypto, dispatched", next to p384 v0.13.1. Line 7874 is in "clippy, warnings denied", a --workspace shape that excludes the guest packages, sshserver among them. Both come from toyos-crypto's dev-dependency fiat-crypto = "0.3" (lib/crypto/Cargo.toml:41), which toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 removes.
    • The config header now describes [build] accurately.
  2. Resolved. The comment no longer cites the crypto issue. toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993's current head (9217793) now says at lines 326-328 that sshserver: russh's p384 on its own bignum backend, host and guest, so no fiat-crypto compiles in #1005 sets the cfg for every build, so the two records agree. The landing-order note in the PR covers the interval between the two merges.

  3. Resolved. The SSH track records the compromise:

    • it calls the backend experimental, with upstream's wording;
    • it lists the two P-384 uses: verifying untrusted client signatures before authentication, and signing with an operator-supplied P-384 host key;
    • it says sshserver negotiates no ecdh-sha2-nistp384;
    • its owner is the track's owner, and its exit is stage 2.

    The stage 2 exit line (".cargo/config.toml sets no p384_backend") is checkable. The PR's Trade-off and Risks sections are corrected, and ECDH nistp384 no longer appears as a use.

  4. Resolved. The scratch lock (p384-scratch-073.Cargo.lock) pins crypto-bigint 0.7.3, primefield 0.14.0-rc.8 and elliptic-curve 0.14.0-rc.29, the same versions as ToyOS's lock. The log opens with cargo tree -i crypto-bigint, which shows 0.7.3 under elliptic-curve and primefield. Under the cfg, cargo test --all-features exits 0: 34 + 6 + 11 tests and 2 doctests, with ecdsa::tests::wycheproof::wycheproof ... ok. The log contains no fiat line. The one version that differs, num-bigint 0.3.3, is in no build.

New changes

The new comments are accurate and in proportion. I have no new findings.

LAND

@Japabu
Japabu marked this pull request as ready for review October 22, 2026 12:36
@Japabu
Japabu added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 5a99540 Oct 22, 2026
8 checks passed
@Japabu
Japabu deleted the wt/p384-bignum branch October 22, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant