Phase 8 A: virtio-rng entropy source for real Ed25519 signing

The kernel's ed25519_verify() is deliberately verify-only -- no signing,
no keygen, no entropy source. That conflicts with the on-device MINT
word vision (Zuse signing new user certs live at runtime), so this
reopens that constraint on request rather than reshaping MINT around
verify-only.

vm_uuid.h already found the real gap: amd64 has RDRAND, riscv64 has Zkr,
but QEMU's aarch64 CPU models have neither -- confirmed against QEMU
10.2.1. A deterministic PRNG (fine for VM UUIDs) is not safe for key
generation, so this adds a virtio-rng device instead of a per-arch split:
real host entropy, identical guest-side protocol on all three arches.

New src/starkernel/virtio/virtio_rng.c + include/starkernel/virtio_rng.h,
transport plumbing mirroring the existing virtio_blk.c driver exactly.
Wired into kernel_main.c boot, -device virtio-rng-pci added to all three
QEMU targets.

Verified live (temp probe, written/run/captured/reverted): 16 real bytes
pulled through the full request/notify/poll round trip on all three
arches, three different values confirming real entropy. Final boot
against the reverted, permanent code: clean compile, clean boot to ok>
on amd64/aarch64/riscv64, Stadium conservation intact, no panics or
guest errors.

Ed25519 keygen/signing itself (Phase B) and the MINT word design
(Phase C) remain open, documented in FABRIC-3.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
This commit is contained in:
Robert Allan James
2026-08-26 14:32:20 -04:00
co-authored by Claude Sonnet 5
parent 6f5605d479
commit 309e792f07
13 changed files with 54761 additions and 1 deletions
+47
View File
@@ -346,6 +346,53 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
ACL is opt-in (`init.4th`'s `S" ACL.4th" EXEC` commented out by default) so the new words
weren't exercised live from the REPL this pass — compile/lint/boot verification only.
**MINT word design, picked up 2026-08-26.** Before scoping `MINT` itself, found a real
conflict with an existing, deliberate decision: `include/starkernel/ed25519.h` is verify-
only by design — "this kernel never signs or generates keys (no entropy source to do so
safely anyway); signing happens in the host-side build tool" (`FABRIC-2.md`, Milestone 6,
2026-08-22). But the vision for `MINT` (§D below) is an *interactive*, on-device
`S" name" MINT` word — an authenticated Zuse session signing a new user's cert live, at
runtime. A kernel that structurally never signs can't do that as envisioned. Raised
directly; **decided (Captain Bob, 2026-08-26): give the kernel a real signing capability**
rather than reshape `MINT` around verify-only. This reopens the prior "no entropy source"
constraint deliberately, not by accident.
**Phase A — `virtio-rng`, done 2026-08-26.** Checked what entropy is actually available
before choosing a design: `include/starkernel/vm_uuid.h` already found, for VM UUIDs, that
amd64 has RDRAND and riscv64 has the Zkr extension, but QEMU's aarch64 CPU models (including
`max`) expose neither RNDR nor any RNG property at all — confirmed directly against QEMU
10.2.1. That's why VM UUIDs use a deterministic PRNG uniformly instead of a per-arch split;
that same choice is **not safe for Ed25519 keygen** — a seed drawn from a known value makes
the private key predictable. Decided: add a `virtio-rng` device instead of a per-arch
RDRAND/Zkr split with a weaker aarch64 fallback — QEMU supplies real host entropy
identically on all three arches, closing the aarch64 gap directly (QEMU-only; real hardware
at Milestone 8 needs a real per-arch RNG driver, a separate later problem).
New `include/starkernel/virtio_rng.h` + `src/starkernel/virtio/virtio_rng.c`, transport
plumbing (PCI capability walk, common-cfg feature negotiation, split virtqueue) mirroring
the existing `virtio_blk.c` exactly — same device family, same quirks. Simpler shape than
block: one virtqueue, one device-writable descriptor, no request header or status byte (the
entropy device has none); `virtio_rng_get_bytes()` loops internally since the device may
return fewer bytes than requested per round. `-object rng-random,id=rng0,filename=/dev/urandom`
+ `-device virtio-rng-pci` added to all three arches' QEMU invocations
(`Makefile.starkernel`). Wired into boot (`kernel_main.c`, right after the existing
`virtio_blk_find_artemis()` call site, same graceful-noop-on-absence precedent).
**Verified live, not just compiled:** a temporary probe (written, run once, captured,
reverted — per this project's standing probe convention) pulled 16 real bytes through the
full request/notify/poll/used-ring round trip on all three architectures and printed them:
amd64 `be9223909b86a8ccbfff705ccae2caa6`, aarch64 `861487df65a6d7a26b2c9c34f5ff96ee`, riscv64
`fec51d80e8c169a35aad9d908eab2f39` — three different values, confirming real entropy, not a
stale or repeated buffer. Probe reverted; permanent code is just the driver + init call. A
second, final 3-arch acceptance boot ran against that reverted code (not the probe build) to
confirm the shipped state itself is clean. Clean zero-warning compile and clean boot to
`ok>` on all three architectures, Stadium conservation intact (43691/21845/65536), no panics
or guest errors on either pass.
**Still open: Phase B (real Ed25519 keygen/signing, seeded from this entropy) and Phase C
(the `MINT` word itself, cert format, and whether Zuse's own keypair needs to chain to the
Milestone 6 offline root CA or is a self-sovereign instance-local root of trust).**
### From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding
- [ ] Settle the still-open question: reuse `ACL-PIN`/`acl_allow` directly, or build a
+6
View File
@@ -764,6 +764,8 @@ ifeq ($(ARCH),amd64)
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device qemu-xhci,id=xhci0 \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0 \
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
-qmp unix:$$QMP_SOCK,server=on,wait=off \
@@ -828,6 +830,8 @@ else ifeq ($(ARCH),aarch64)
-device virtio-blk-pci,drive=artdisk \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4 \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
-device ramfb \
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
@@ -912,6 +916,8 @@ else ifeq ($(ARCH),riscv64)
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4 \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
-device ramfb \
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
-serial chardev:cserial \
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-26T17:54:43Z -->
<!-- Generated by mkcapsule --manifest 2026-08-26T18:28:05Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
+58
View File
@@ -0,0 +1,58 @@
/*
* virtio_rng.h — Virtio 1.0 entropy source driver for StarKernel
*
* Real hardware/host entropy, uniformly across all three architectures.
* Exists specifically because this kernel has no per-arch RNG that covers
* all three ISAs: amd64 has RDRAND and riscv64 has the Zkr extension, but
* QEMU's aarch64 CPU models (including "max") expose neither RNDR nor any
* other RNG property (checked directly against QEMU 10.2.1, see
* vm_uuid.h's identical finding). A paravirtualized virtio-rng device
* sidesteps the per-ISA gap entirely: the host supplies the entropy, the
* guest-side protocol is identical on all three arches.
*
* QEMU-only: there is no virtio-rng on real hardware (Milestone 8, bare-
* metal boot). A real per-arch RNG driver is a separate, later problem.
*
* Only one virtio-rng device is supported.
*/
#ifndef STARKERNEL_VIRTIO_RNG_H
#define STARKERNEL_VIRTIO_RNG_H
#include <stddef.h>
#include <stdint.h>
/* virtio PCI vendor (shared with every virtio device) */
#define VIRTIO_RNG_PCI_VENDOR_ID 0x1AF4u
/* device IDs: virtio device id 4 (entropy source) */
#define VIRTIO_RNG_DEVICE_MODERN 0x1044u
#define VIRTIO_RNG_DEVICE_LEGACY 0x1005u
/*
* virtio_rng_init — locate the virtio-rng device on the PCI bus and bring
* the driver up. Call once, before the first virtio_rng_get_bytes() call.
*
* Returns 0 on success.
* Returns -1 if no virtio-rng device was found on the PCI bus.
* Returns -2 on driver initialisation failure (bad BAR, queue setup, etc.).
*/
int virtio_rng_init(void);
/*
* virtio_rng_ready — 1 if virtio_rng_init() has already succeeded.
*/
int virtio_rng_ready(void);
/*
* virtio_rng_get_bytes — fill buf with n bytes of real host-supplied
* entropy, blocking (polling) until all n bytes are obtained. The device
* may return fewer bytes than requested per request; this loops
* internally until the buffer is full.
*
* Returns 0 on success (buf fully filled).
* Returns -1 if virtio_rng_init() has not succeeded.
* Returns -2 on a device/timeout error partway through.
*/
int virtio_rng_get_bytes(uint8_t *buf, size_t n);
#endif /* STARKERNEL_VIRTIO_RNG_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+18
View File
@@ -64,6 +64,7 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL;
#include "starkernel/repl.h"
#include "starkernel/pci.h"
#include "starkernel/virtio_blk.h"
#include "starkernel/virtio_rng.h"
#include "starkernel/virtio_input.h"
#include "starkernel/xhci_driver.h"
#include "block_subsystem.h"
@@ -610,6 +611,23 @@ static void kernel_main_deep(BootInfo *boot_info) {
}
}
/* Phase 8: virtio-rng entropy source. Real per-arch RNG doesn't cover
* all three architectures (amd64 RDRAND, riscv64 Zkr, but aarch64 has
* neither in QEMU's CPU models -- see vm_uuid.h's identical finding),
* so signing/keygen entropy comes from this paravirtualized device
* instead. Unconditional call site, same graceful-noop precedent as
* virtio_blk_find_artemis() above -- boot proceeds either way, the
* device is only required once something actually calls
* virtio_rng_get_bytes(). */
{
int rrc = virtio_rng_init();
if (rrc == 0) {
console_println("virtio-rng: ready");
} else {
console_println("virtio-rng: not available (continuing without)");
}
}
/* item 4.3.5c: virtio-keyboard-pci, riscv64 only today. Unconditional
* call site, same as virtio_blk_find_artemis() above -- the function
* itself no-ops with a console message on architectures/boards where
File diff suppressed because it is too large Load Diff