fix(v4): the identity code wipes Zuse's seed after every drive it handles (#176, the identity code)

sk_v4_genesis never wiped its seed or its read-back; sk_v4_drive_arrived
never wiped the proxy's seed after her login; sk_v4_real_zuse_arrived wiped
the seed only on a match. (The logged-in Zuse's own key stays in g_zuse_seed,
by design.)

Option 1: the three use one buffer the identity module owns, g_seeds (seed,
then its read-back), and sk_v4_drive_arrived -- which every drive goes through,
and which alone calls the other two -- runs the drive's handling (now
handle_drive()) and then wipes it, whichever way it went.
sk_v4_identity_seeds() lets the host tests read it.

kernel/tests/test_sk_v4_identity.c, 10 checks, through the real drive-arrival
path: a fake xHCI controller marks a slot attach-pending, the identity code's
idle beat handles it, the real gpt.c reads the stick's table from the RAM disk;
the block layer, signatures, key mint, marker and console faked. Genesis; a
genesis not read back; the proxy logging in; a real Zuse named, then her stick
logging in. Against the code before the wipe: 5 failures (every case's seeds
left). Now: 0. The harness gains fake_disk_bind. Built for amd64, aarch64,
riscv64. #176 done with 2bac2ac8 (the mint).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
Claude
2026-11-12 18:20:09 +00:00
parent 2bac2ac8fc
commit d7b2f6bf54
8 changed files with 196 additions and 8 deletions
+147
View File
@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* test_sk_v4_identity.c -- kernel/src/v4/sk_v4_identity.c on the host (#176): the seeds the identity
* code mints and reads while it handles a drive -- genesis, the proxy's login, the real Zuse's --
* are wiped once the drive has been handled, whichever way it went. Drives arrive as on the
* machine: a fake xHCI controller marks a slot's drive attach-pending, sk_v4_identity_check_now()
* runs the identity code's idle beat, and the drive's GPT is read by the real gpt.c from the
* harness's RAM disk. The block layer, the signatures, the key mint, the marker and the console are
* faked.
*/
#define _POSIX_C_SOURCE 200809L
#include "harness.h"
#include "fakes.h"
#include "starkernel/gpt.h"
#include "starkernel/xhci_driver.h"
#include "starkernel/artemis_sig.h"
#include "starkernel/capsule_zuse_boot.h"
#include "starkernel/homeblocks_sig.h"
#include "starkernel/console.h"
#include "starkernel/v4/sk_v4_identity.h"
#include "starkernel/v4/sk_v4_marker.h"
#include "starkernel/v4/sk_v4_slots.h"
#include "block_subsystem.h"
#include <stdlib.h>
#include <string.h>
#define SLOTS 4u
static xhci_dev_t xdev;
static xhci_msc_slot_t ms[SLOTS + 1];
static fake_disk disks[SLOTS + 1];
/* the scenario */
static int sig_blank, key_hers;
static sk_v4_marker_t marker_on_stick;
static int marker_rc;
/* ---- the fakes ---- */
uint32_t xhci_dev_count(void) { return 1u; }
xhci_dev_t *xhci_dev_at(uint32_t i) { return i == 0 ? &xdev : 0; }
xhci_msc_slot_t *xhci_msc_slot_for(xhci_dev_t *dev, uint32_t slot) { (void)dev; return slot <= SLOTS ? &ms[slot] : 0; }
void xhci_poll_events(void) { }
int xhci_bringup(xhci_dev_t *dev) { (void)dev; return 0; }
int xhci_find_and_map(xhci_dev_t *dev, unsigned index) { (void)dev; (void)index; return -1; }
int blkio_usb_open_msc(blkio_dev_t *dev_out, xhci_dev_t *x, uint32_t slot) { (void)x; return fake_disk_bind(dev_out, &disks[slot]) == BLKIO_OK ? 0 : -1; }
void *kmalloc_aligned(size_t size, size_t align) { void *p = 0; return posix_memalign(&p, align, size) ? 0 : p; }
artemis_sig_result_t artemis_sig_check(struct blkio_dev *dev, artemis_sig_t *out) { (void)dev; memset(out, 0, sizeof *out); return ARTEMIS_SIG_OK; }
int blk_subsys_attach_device(struct blkio_dev *dev) { (void)dev; return BLK_OK; }
int blk_subsys_detach_device(struct blkio_dev *dev) { (void)dev; return BLK_OK; }
int blk_get_device_range(struct blkio_dev *dev, uint32_t *start, uint32_t *count) { (void)dev; *start = 0; *count = 1; return BLK_OK; }
int blk_subsys_confirm_format(uint32_t lbn) { (void)lbn; return BLK_OK; }
int sk_v4_grow_artemis(blkio_dev_t *art) { (void)art; return 0; }
uint64_t sk_v4_grow_cap_sectors(blkio_dev_t *art, uint64_t cap) { (void)art; (void)cap; return 0; }
int sk_v4_marker_read(sk_v4_marker_t *m) { *m = marker_on_stick; return marker_on_stick.present ? 0 : -1; }
int sk_v4_marker_write(const sk_v4_marker_t *m) { marker_on_stick = *m; return marker_rc; }
int sk_v4_mint_zuse(void) { return -1; }
homeblocks_sig_result_t homeblocks_sig_check(struct blkio_dev *dev, uint32_t start, homeblocks_sig_t *out)
{ (void)dev; (void)start; memset(out, 0, sizeof *out); return sig_blank ? HOMEBLOCKS_SIG_BLANK : HOMEBLOCKS_SIG_OK; }
int capsule_zuse_boot_genesis_mint(struct blkio_dev *dev, uint8_t seed[32], uint8_t pubkey[32])
{ (void)dev; memset(seed, 0x5A, 32); memset(pubkey, 0x11, 32); return 0; }
zuse_drive_t capsule_zuse_boot_check_key(struct blkio_dev *dev, homeblocks_sig_result_t rc, const homeblocks_sig_t *sig,
const uint8_t want[32], uint8_t seed[32], uint8_t pubkey[32])
{ (void)dev; (void)rc; (void)sig; memset(seed, 0x5A, 32); memcpy(pubkey, want, 32);
return key_hers ? ZUSE_DRIVE_HERS : (zuse_drive_t)(ZUSE_DRIVE_HERS + 1); }
static uint64_t ticks;
uint64_t heartbeat_ticks(void) { return ticks += 1000u; }
void heartbeat_service(void) { }
void apic_timer_start(void) { }
void arch_halt(void) { }
void arch_enable_interrupts(void) { }
int sk_input_getc(void) { return -1; }
void sk_input_drop(void) { }
void console_cancel_deferred_line_start(void) { }
void console_ensure_line_start(void) { }
void console_fb_clear(void) { }
void console_fb_enable_ttf(void) { }
void console_fb_mirror(int on) { (void)on; }
void console_set_user_prefix_provider(console_user_prefix_fn fn) { (void)fn; }
void console_set_vm_name(const char *name) { (void)name; }
uint64_t console_tx_count(void) { return 0; }
/* ---- the sticks ---- */
static void put_le32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); }
static void put_le64(uint8_t *p, uint64_t v) { put_le32(p, (uint32_t)v); put_le32(p + 4, (uint32_t)(v >> 32)); }
/* a GPT: the identity partition at 2048..4095, and Artemis after it when `artemis` */
static void make_stick(fake_disk *d, int artemis)
{
uint64_t sectors = (uint64_t)d->blocks * 2u, last_usable = sectors - 34u;
uint8_t *mbr = fake_disk_sector(d, 0), *h = fake_disk_sector(d, 1), *e = fake_disk_sector(d, 2);
memset(d->bytes, 0, 34u * 512u);
mbr[446 + 4] = 0xEE; put_le32(mbr + 446 + 8, 1u); put_le32(mbr + 446 + 12, (uint32_t)(sectors - 1u)); mbr[510] = 0x55; mbr[511] = 0xAA;
memcpy(e, gpt_type_starship_identity, 16); e[16] = 1; put_le64(e + 32, 2048u); put_le64(e + 40, 4095u);
if (artemis) { memcpy(e + 128, gpt_type_starship_artemis, 16); e[128 + 16] = 2; put_le64(e + 128 + 32, 4096u); put_le64(e + 128 + 40, 14335u); }
memcpy(h, "EFI PART", 8); put_le32(h + 8, 0x00010000u); put_le32(h + 12, 92u);
put_le64(h + 24, 1u); put_le64(h + 32, sectors - 1u); put_le64(h + 40, 34u); put_le64(h + 48, last_usable);
h[56] = (uint8_t)(0x40 + artemis); put_le64(h + 72, 2u); put_le32(h + 80, 128u); put_le32(h + 84, 128u);
put_le32(h + 88, gpt_crc32(e, 128u * 128u)); put_le32(h + 16, 0u); put_le32(h + 16, gpt_crc32(h, 92u));
}
static void plug(unsigned slot) { ms[slot].bot_msc_attach_pending = 1; sk_v4_identity_check_now(); }
static void pull(unsigned slot) { ms[slot].bot_msc_detach_pending = 1; sk_v4_identity_check_now(); }
static int zero(void) { const uint8_t *p = sk_v4_identity_seeds(); unsigned i; for (i = 0; i < 64; i++) if (p[i]) return 0; return 1; }
int main(void)
{
unsigned k;
memset(&xdev, 0, sizeof xdev); xdev.max_slots = SLOTS;
for (k = 1; k <= SLOTS; k++) fake_disk_open(&(blkio_dev_t){0}, &disks[k], 8192u);
make_stick(&disks[1], 1); /* the system stick */
make_stick(&disks[2], 0); /* a real Zuse's stick: identity only */
fake_console_reset();
/* 1. a blank system stick: genesis mints the proxy, and she logs in */
sig_blank = 1; key_hers = 1; marker_rc = 0; memset(&marker_on_stick, 0, sizeof marker_on_stick);
plug(1);
CHECK(fake_console_said("Zuse: genesis minted onto attached thumbdrive") && sk_v4_identity_present(), "1. genesis: minted, logged in");
CHECK(zero(), "#176: 1. genesis minted -- the seed buffer is zero after");
pull(1);
/* 2. genesis whose read-back is not hers: not minted */
memset(&marker_on_stick, 0, sizeof marker_on_stick); key_hers = 0; fake_console_reset();
plug(1);
CHECK(fake_console_said("Zuse: genesis mint failed -- not read back") && !sk_v4_identity_present(), "2. genesis not read back: no login");
CHECK(zero(), "#176: 2. genesis not read back -- the seed buffer is zero after");
pull(1);
/* 3. the system stick with its proxy Zuse: she logs in */
marker_on_stick.present = 1; memset(marker_on_stick.proxy, 0x11, 32); marker_on_stick.has_real = 0;
sig_blank = 0; key_hers = 1; fake_console_reset();
plug(1);
CHECK(fake_console_said("Zuse: identity confirmed from attached thumbdrive") && sk_v4_identity_present(), "3. the proxy: logged in");
CHECK(zero(), "#176: 3. the proxy logged in -- the seed buffer is zero after");
pull(1);
/* 4. a real Zuse: the system stick in (no session, she exists), then her stick */
marker_on_stick.has_real = 1; memset(marker_on_stick.real, 0x22, 32); fake_console_reset();
plug(1);
CHECK(!sk_v4_identity_present(), "4. the system stick, a real Zuse named: the proxy gets no session");
CHECK(zero(), "#176: 4. the proxy's key checked, no session -- the seed buffer is zero after");
plug(2);
CHECK(fake_console_said("Zuse: identity confirmed from attached thumbdrive") && sk_v4_identity_present(), "4. her stick: logged in");
CHECK(zero(), "#176: 4. the real Zuse logged in -- the seed buffer is zero after");
return DONE();
}