Phase 8 C (2/n): expand cert storage; NVRAM persistence crashed, reverted

Cert storage expanded from the old 16-byte placeholder to a real
32-byte seed + 32-byte pubkey. vm_zuse_cert_install() now has a
kernel-side duplicate in src/starkernel/vm/vm_core.c -- the kernel
build's VM_EXCLUDE list drops src/vm.c entirely (same reason
vm_set_base() already has two independent copies), so the hosted-only
version added earlier this session was never actually linked into the
kernel. FORTH-side ZUSE-CERT-LO@/HI@ replaced with ZUSE-PUBKEY@ (i -- u)
over the public half only; ACL-ZUSE-BOOT now checks
ZUSE-CERT-INSTALLED? before authenticating instead of unconditionally.

Attempted NVRAM-based persistence (GetVariable/SetVariable) for the
first-boot mint flow: page-faulted inside OVMF's variable service
(CR2 in the flash MMIO window). Moving the call site to match the one
proven-safe existing SetVariable call site in this codebase produced
the identical crash -- not a timing issue. Localized with debug
markers (one boot): GetVariable works; SetVariable with real data
never returns. The existing "working" precedent call is actually a
delete-of-nonexistent-variable (size=0, data=NULL), a cheaper path
that never touches flash, so it proved nothing about real writes.
Root cause: this kernel's VMM never maps the region OVMF's variable
service needs for real flash writes -- a genuine gap in UEFI runtime-
services support, not Zuse-specific, and not obviously fixable in a
3-arch-uniform way (flash window location is firmware/arch-specific).

Independently, storing the raw seed in RUNTIME_ACCESS NVRAM would have
been a real security defect regardless of the crash -- readable by any
later-loaded UEFI app or the booted OS.

Reverted to a known-safe state: all NVRAM/mint code removed from
kernel_main.c, init.4th's ACL.4th line back to its documented
commented-out default. Verified clean compile and clean boot on all
three architectures. Cert storage expansion (the part that works)
stays. A dedicated system-identity disk (virtio-blk, already proven
for writes via Artemis) is the recommended next substrate -- not yet
decided or built. Full investigation 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 15:55:27 -04:00
co-authored by Claude Sonnet 5
parent f223a31cec
commit e5cbc71f46
17 changed files with 54299 additions and 46 deletions
+26 -14
View File
@@ -793,17 +793,31 @@ static void starforth_word_zuse_authenticate(VM *vm)
vm->zuse_session = 1;
}
/* ZUSE-CERT-LO@ ( -- lo ) Read-only: no FORTH store word exists or should
* exist -- the cert is written exactly once, in C, via vm_zuse_cert_install(). */
static void starforth_word_zuse_cert_lo_fetch(VM *vm)
/* ZUSE-PUBKEY@ ( i -- u ) Read-only: fetch 8-byte little-endian chunk i
* (0..3) of Zuse's 32-byte Ed25519 public key as one cell. Out-of-range i
* pushes 0 and sets vm->error rather than faulting. No FORTH word can
* write these bytes or read the seed -- the cert is written exactly once,
* in C, via vm_zuse_cert_install(); this is a read-only window onto the
* PUBLIC half only. */
static void starforth_word_zuse_pubkey_fetch(VM *vm)
{
vm_push(vm, (cell_t)vm->zuse_cert_lo);
}
/* ZUSE-CERT-HI@ ( -- hi ) See ZUSE-CERT-LO@. */
static void starforth_word_zuse_cert_hi_fetch(VM *vm)
{
vm_push(vm, (cell_t)vm->zuse_cert_hi);
if (vm->dsp < 0) {
log_message(LOG_ERROR, "ZUSE-PUBKEY@: stack underflow");
vm->error = 1;
return;
}
cell_t i = vm_pop(vm);
if (i < 0 || i > 3) {
vm_push(vm, 0);
vm->error = 1;
return;
}
const uint8_t *p = &vm->zuse_cert_pubkey[i * 8];
cell_t chunk = 0;
for (int b = 7; b >= 0; b--) {
chunk = (chunk << 8) | (cell_t)p[b];
}
vm_push(vm, chunk);
}
/* ZUSE-CERT-INSTALLED? ( -- flag ) -1 if the one-time cert fuse has been
@@ -846,8 +860,7 @@ void register_starforth_words(VM* vm)
register_word(vm, "RANDOM", starforth_word_random);
register_word(vm, "WAIT", starforth_word_wait);
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
register_word(vm, "ZUSE-CERT-LO@", starforth_word_zuse_cert_lo_fetch);
register_word(vm, "ZUSE-CERT-HI@", starforth_word_zuse_cert_hi_fetch);
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);
@@ -867,8 +880,7 @@ void register_starforth_words(VM* vm)
register_word(vm, "RANDOM", starforth_word_random);
register_word(vm, "WAIT", starforth_word_wait);
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
register_word(vm, "ZUSE-CERT-LO@", starforth_word_zuse_cert_lo_fetch);
register_word(vm, "ZUSE-CERT-HI@", starforth_word_zuse_cert_hi_fetch);
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);