Skip to content

sha2: one SHA-2, toyos-crypto's; toyos-sha2 and toyos-sha2-hw gone - #1016

Merged
Japabu merged 3 commits into
mainfrom
wt/toyos-crypto-adopt
Oct 22, 2026
Merged

Japabu merged 3 commits into
mainfrom
wt/toyos-crypto-adopt

Conversation

@Japabu

@Japabu Japabu commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The owner asked (2026-10-22): "Use our own crypto where possible." ToyOS hashed with two crates of its own besides toyos-crypto's SHA-2:

  • toyos-sha2, scalar;
  • toyos-sha2-hw, SHA-NI on x86-64 and scalar on AArch64.

The build, pkg, lib/swap, system/update, lib/update and the UEFI loader hashed with them.

On main after #993 and #1015.

Change

  • One SHA-2. Every SHA-256 and SHA-512 is toyos-crypto's: the build's digests (now one helper, toyos_build::sha256), pkg, lib/swap, sdk/icon's test, lib/update (SHA-512 over the signed header too), the loader and system/update. toyos-sha2 and toyos-sha2-hw are deleted.
  • The loader keeps SHA-NI. The loader's target is soft-float, where the crate's intrinsic path cannot compile. toyos-sha2-hw's single asm block, which saves and restores every XMM register it uses, moves into the crate as its UEFI SHA-256 path (arch/x86_64/sha_saving.rs). On any x86-64 host with the instructions it is held to the portable rounds. Hosted x86-64 keeps the crate's existing intrinsic path.
  • AArch64:
    • The Armv8 SHA instructions are used where the target promises them at compile time: sha2 for SHA-256 and sha3 for SHA-512, as Rust names FEAT_SHA256 and FEAT_SHA512 (arch/aarch64.rs).
    • That is the development Mac, where the build hashes every image.
    • ToyOS's own AArch64 targets promise neither and nothing detects them at run time, so they hash portably. That is recorded as a weakness, with an exit.
  • Refusals. The crate's refusal past FIPS 180-4's 2^61-byte bound stays.
    • A stream with an error path carries it: pkg's Read and update's stream_root.
    • A hash of an in-memory or signed-length input expects it and names why.
  • Licence gate.
    • toyos-crypto now ships in an image, so the gate counts files under its directory as shipped.
    • NIST's ACVP vectors (NIST-Software, a licence the allowlist does not name) move to tests/acvp/, the way tests/cavp/ was placed. Their NOTICE section and COMMITTED_FILES rows move with them.
    • The ML-KEM unit test now reads its keygen vectors at run time instead of embedding them. Miri still runs it, with MIRIFLAGS=-Zmiri-disable-isolation, which the issue's Miri record now names.
  • Loader lint. The loader's clippy step lints undocumented_unsafe_blocks, which now reaches the crate's code built for UEFI. Those unsafe blocks and impls get SAFETY: comments.

Checks

  • cargo run -- --ci host at e5414b9 (review r1): exit 0, 93 steps green. Before r1, the same at be8ac5c (on main 18a3873).
    • A first run, before the SAFETY: comments, was red on the loader's clippy step alone.
  • cargo test -p toyos-crypto in all three forms (portable, all features, default): exit 0. The crate's SHAVS tests (toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993) run the dispatched path.
  • AArch64 paths on the Mac. hash::tests::dispatched_compression_matches_the_portable_rounds compares the instructions with the portable rounds over 0–9 blocks. Negative controls:
    • SHA256H2 fed the new ABCD instead of the old: red;
    • SHA512H's (D, E) operand swapped: red;
    • both restored: green.
  • Consumer tests of toyos-update (with sign), toyos-swap, pkg, toyos-icon and update-copies: all pass. The tests that pin fixed digests also pass: the icons' pixels, the wallpaper, ROOT's UUID and NOTICE's records. Those are byte-for-byte oracles that the SHA-256 is unchanged.
  • Images. cargo run -- --build-only: exit 0 for x86-64 and for AArch64. toyos-crypto is compiled for both UEFI targets and the guest.
  • QEMU. a_bent_supervisor_is_refused_and_the_other_slot_boots passes, on images built for both architectures. The loader's SHA-256 refuses the bent slot's section and boots the intact one.
  • Licence gate. licence::judge passes: only its six standing exceptions remain. Before the vectors moved, it refused the six NIST-Software files.
  • Mac speed (exploratory, M4 Pro, under load), 1 MiB messages:
    • SHA-256: 2.6 GB/s on the instructions against 0.52 GB/s portable;
    • SHA-512: 1.36 against 0.83 GB/s.

Evidence: https://github.com/ToyOSOrg/evidence/tree/f1de6e6eba9952b7c461c2fa3844ad65e7d9c471/pr-1016

The speed requirement holds

The owner required the crate to be no slower than toyos-sha2-hw. A never-landed probe image timed both SHA-256s against the same ring in crypto_parity, one boot on each bench. Mean times; lower is faster.

bench size toyos-crypto toyos-sha2-hw
T14 64 B 99.7 461.8
T14 16 KiB 9,660.6 10,620.2
T14 1 MiB 615,425.0 653,261.5
AMD 64 B 84.8 235.1
AMD 16 KiB 7,763.3 7,926.6
AMD 1 MiB 493,656.2 493,821.5

The crate is no slower at any size, and 2.8–4.6× as fast at 64 B. Readbacks (extracts, no machine identifiers): https://github.com/ToyOSOrg/evidence/tree/c186d4592d98b86e5fef3ea45e570f05279ddb4b/pr-1016/r1

Risks

  • update's streamed ROOT hash is still untimed on any compression (issues/no-t14-reading-of-updates-root-hash-before-and-after-sha-ni.md, reworded).
  • ToyOS's own AArch64 targets hash portably, as recorded above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q

Japabu and others added 2 commits October 22, 2026 15:47
The owner (2026-10-22): use our own crypto where possible. Every SHA-256
and SHA-512 in the tree is now toyos-crypto's: the build's digests, pkg,
lib/swap, sdk/icon's test, lib/update, the UEFI loader and system/update.

- The loader keeps SHA-NI. Its target is soft-float, so the crate's
  intrinsic path does not compile there. toyos-sha2-hw's one asm block,
  which saves and restores every XMM register it uses, moves into the
  crate as its UEFI SHA-256 path (`arch/x86_64/sha_saving.rs`), held
  against the portable rounds on any x86-64 host with the instructions.
- AArch64 hashes on the Armv8 SHA instructions where the target promises
  them at compile time (`sha2` for SHA-256, `sha3` for SHA-512, as Rust
  names FEAT_SHA256 and FEAT_SHA512): the development Mac's, where the
  build hashes every image. ToyOS's own AArch64 targets promise neither
  and nothing detects them at run time; the issue records that.
- Consumers keep the crate's refusal past FIPS 180-4's 2^61-byte bound: a
  stream with an error path carries it, and a hash of an in-memory or
  signed-length input expects it, naming why. The build has one helper,
  `toyos_build::sha256`, in place of release's `sha256_hex`.
- toyos-crypto now ships in an image, so the licence gate counts files
  under its directory as shipped. NIST's ACVP vectors, under the
  NIST-Software licence the allowlist does not name, move to
  `tests/acvp/`, as `tests/cavp/` was placed for the same reason. The
  ML-KEM unit test reads its vectors at run time rather than embedding
  them, so Miri skips that one test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
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, round 1: #1016 at 55ecebe (code be8ac5c), against main 18a3873

Architecture

This is the right shape. One SHA-2 now lives in the crate that owns ToyOS's crypto, and both superseded crates are gone. Nothing in the tree outside issues/ still names toyos-sha2 or toyos-sha2-hw, and the workspace member list and every manifest were updated. Hardware selection stays inside the crate's arch/ modules:

  • The UEFI loader keeps SHA-NI through the register-saving block. Apart from its signature and the CPUID gate, which now comes from the crate's features(), the block is byte-for-byte toyos-sha2-hw's.
  • AArch64 uses the Armv8 instructions where the target guarantees them at compile time.

Errors are handled in two ways. Streams with an error path (pkg, stream_root) carry the 2^61 refusal. In-memory hashes call expect and name the reason. The build now has one helper instead of four hand-rolled hex digests.

Net change: +358/−789. Production code shrinks: about 300 lines of sha2 and sha2-hw go, and 124 lines of AArch64 paths and the moved 138-line block arrive. On the test side, the two crates' SHAVS drivers and differentials go. The crate's existing SHAVS tests and the dispatched-against-portable test cover what they covered. The register-saving block gets its own host test.

I checked the AArch64 rounds against DDI 0487's operand conventions:

  • SHA256SU0/SU1 operand order, and SHA256H/H2 using the pre-round ABCD.
  • SHA512SU0, and SHA512SU1 with ext(w[i+4], w[i+5]) = (W[2i−7], W[2i−6]).
  • SHA512H taking gh + swap(kw), (F, G) and (D, E). SHA512H2 taking (C, D) and (A, B). The rotation (a, ab, cd + t1, ef).

All of these are correct. The two negative controls in the evidence (H2 operand, SHA512H operand) show that the dispatched-against-portable test catches errors at this level.

Required

  1. issues/toyos-crypto-is-in-the-tree-on-par-with-ring-and-secure.md:53-54 and the PR body: the SHA-512 figure is wrong. Both say "SHA-512 at 1.7 against 0.83" for 1 MiB messages. mac-shabench-instructions.txt records 1360 MB/s at 1 MiB; 1684 MB/s is the 16 KiB row. A committed record should not misquote its own evidence. Change it to 1.36, or label the figure as 16 KiB.

  2. lib/crypto/src/mlkem.rs:394: dead code left by this change. The test is now #[cfg_attr(miri, ignore)], so .take(if cfg!(miri) { 1 } else { usize::MAX }) can never select 1. Delete the take.

    Separately, this change removes the only Miri run of ML-KEM key generation. The toyos-crypto stage 4, the test side: vectors, integer oracle, differential, fuzz, controls, Miri and the constant-time harness #993 record (issue, "Miri over every portable unit test … 25 pass and ML-KEM's exhaustive reduction is ignored") is now stale for future Miri runs. Either:

    • state that the Miri evidence run uses MIRIFLAGS=-Zmiri-disable-isolation so the test still reads its file under Miri; or
    • record the lost coverage where the Miri acceptance is described.
  3. lib/update/src/lib.rs:45-48: the comment justifying SHA_BOUND gives the wrong reason. It says an archive is "bounded by a u64 the owner's key signed", but a u64 does not stay below 2^61. What actually holds the streamed archive below the bound is repo.rs:505, which caps length at ARCHIVE_CAP (1 GiB), together with Archive::take's refusal past length. Name that cap, or shorten the comment to it.

  4. Speed evidence is pending. The owner's requirement is "no slower than toyos-sha2-hw", and it is not yet established (it is on the bench now). The PR, the issue row (line 66) and metal-request.txt frame it correctly. Two cases:

    • On x86-64 build hosts and the guest, the hosted path is the crate's intrinsics, which have never been compared with the asm they replace. Landing waits for the T14/AMD crypto_parity rows.
    • If those rows show the crate slower, the PR's stated remedy (the asm block on every x86-64 target) is part of this landing, not a follow-up.

    The Mac and the AArch64 build host gain clearly: 2.6 against 0.52 GB/s, where toyos-sha2-hw was scalar.

Suggestions

  • lib/crypto/src/arch/x86_64.rs:496-498: on a host without SHA-NI, the_register_saving_sha256_block_matches_the_portable_rounds prints a line and passes. The deleted toyos-sha2-hw tests failed loudly there. CI's runners have SHA-NI, so nothing is lost today. #[ignore] with a reason, or a failing expect, would stop a red-capable test reporting green unexercised.
  • NOTICE:621: the edited sentence is one unwrapped 120-column line. Reflow it.
  • lib/crypto/src/arch/aarch64.rs:438,448,451,470,495: these unsafe comments lack the SAFETY: prefix this PR adopts elsewhere. The arm is not linted under undocumented_unsafe_blocks today (only the Mac compiles it), so this is just consistency.

Evidence

SEND BACK: items 1–3 are cheap fixes. Item 4 is gated on the bench run.

@Japabu

Japabu commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

The speed requirement for #1016 has bench results. These come from the probe f0a2d1b56: #1019's head plus toyos-sha2-hw restored for comparison. One image, in the asserted parity case: a median of 21 samples, with ring measured in the same row.

SHA-256 T14: crate T14: toyos-sha2-hw AMD bench: crate AMD bench: toyos-sha2-hw
64 B 99.7 461.8 84.8 235.1
16 KiB 9,661 10,620 7,763 7,927
1 MiB 615,425 653,262 493,656 493,822

The units are the parity row's, the same for both columns. Lower is faster.

The crate is no slower than toyos-sha2-hw at any size on either machine. It is 2.8–4.6× faster at 64 B and equal at 1 MiB on the AMD bench. The "no slower" requirement holds, so the asm block does not need to run beyond the UEFI path.

#1019's loader signature verification took 253,975 counter ticks on the T14 and 122,724 on the AMD bench. The AMD loader states no counter rate. Readbacks are kept by the orchestrator, for the implementer to publish.

- The issue's SHA-512 figure is its evidence's: 1.36 GB/s at 1 MiB on the
  Mac, not 1.7, which was a run before.
- ML-KEM key generation stays under Miri: the keygen-vector test reads
  its file from `tests/acvp/` with Miri's isolation off, which the
  recorded Miri run now names, rather than being ignored there.
- `SHA_BOUND` names `ARCHIVE_CAP`, which holds a streamed archive to
  1 GiB; a signed `u64` does not.
- The register-saving SHA-256 block's host test fails on a host without
  the instructions, as toyos-sha2-hw's did, rather than passing.
- `SAFETY:` on the AArch64 path's unsafe blocks; NOTICE's sentence
  reflowed.
- The speed requirement holds: on both benches the crate's SHA-256 is no
  slower than toyos-sha2-hw's at 64 B, 16 KiB and 1 MiB, and 2.8 to 4.6
  times as fast at 64 B. The issue's row says so.

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

Response to review r1. The head is e5414b9 (main hasn't moved).

  1. SHA-512 figure: fixed. It is 1.36 GB/s at 1 MiB, as the evidence records, in both the issue and the PR body.
  2. Miri: ML-KEM key generation stays under Miri.
    • The test no longer carries cfg_attr(miri, ignore). Miri reads the vectors with MIRIFLAGS=-Zmiri-disable-isolation, and the issue's Miri record names the flag.
    • That makes the take(if cfg!(miri) { 1 } …) live again, so it stays.
    • Run: exit 0, 1 passed (miri-keygen-without-isolation.log).
  3. SHA_BOUND: the comment now names repo::ARCHIVE_CAP (1 GiB) for streamed archives.
  4. Speed: it holds on both benches, at every size, and the crate is 2.8–4.6× as fast at 64 B. The table is in the body, and the issue's row records it. Readbacks: https://github.com/ToyOSOrg/evidence/tree/c186d4592d98b86e5fef3ea45e570f05279ddb4b/pr-1016/r1

Suggestions, all taken:

  • The register-saving block's host test now fails on a non-portable host without SHA extensions, rather than passing unexercised.
  • NOTICE is reflowed.
  • SAFETY: prefixes are on the AArch64 path; clippy's undocumented_unsafe_blocks is clean there.

cargo run -- --ci host at e5414b9: exit 0, 93 steps. All three crate forms and toyos-update pass.

@Japabu

Japabu commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 2: #1016 at e5414b9

All of round 1's required items are resolved.

  1. SHA-512 figure: now 1.36 GB/s at 1 MiB in both the issue (…-secure.md:54) and the body, which matches mac-shabench-instructions.txt.
  2. Miri: the ignore is removed, so the take(if cfg!(miri) …) is live again. The issue's Miri record names -Zmiri-disable-isolation. r1/miri-keygen-without-isolation.log shows the test passing under Miri (exit 0, 1 passed).
  3. SHA_BOUND: the comment (lib/update/src/lib.rs:45-47) now names repo::ARCHIVE_CAP. That is the bound that actually applies.
  4. Speed: I read the readbacks in pr-1016/r1/bench. The asserted probe sha-256 rows match the body's table on both benches:

Suggestions taken:

  • The SHA-NI host test now fails on a non-portable host without the instructions.
  • The AArch64 unsafe comments now carry the SAFETY: prefix.

One leftover nit, not blocking: NOTICE:622 is still about 110 columns after the reflow.

ci-host.rev is e5414b9, exit 0.

LAND

@Japabu
Japabu marked this pull request as ready for review October 22, 2026 19:33
@Japabu
Japabu added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit b1cc11d Oct 22, 2026
8 checks passed
@Japabu
Japabu deleted the wt/toyos-crypto-adopt branch October 22, 2026 20:14
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