diff --git a/docs/v4.0.0/plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md b/docs/v4.0.0/plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md index cb02d537..edd74e6d 100644 --- a/docs/v4.0.0/plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md +++ b/docs/v4.0.0/plans/2026-10-29-stick-5-proxy-zuse-and-real-zuse.md @@ -357,7 +357,7 @@ Tasks 1-3 accepted and merged (#159, master 594d2f93); #155 merged with them. - [x] 4.4 `sk_v4_identity_offer_wait()` / `sk_v4_identity_recheck_offered()`: a stick offered, not attached, while a mint waits (`g_mint_waiting`, `g_offered` in `sk_v4_identity.c`; any stick not this system's and readable -- blank, foreign or a bad table; ends on a stick, a key, or the system stick leaving). Builds on three ISAs; not yet called. - [x] 4.5 `sk_v4_mint.c`: the flow -- plug-in line, size line, `YES`, GPT, zeroed, minted, read back, marker. Builds on three ISAs; not yet called. The gate gained a **spare**: the last stick turned away as not this system's (or handed back by a refused mint) is offered first by the next mint (for `mint_zuse_refuse`: "a second `MINT-ZUSE` is offered again"); and accessors `sk_v4_identity_stick_in/offered_present/release_offered/marker/set_marker`. - [x] 4.6 `MINT-ZUSE` in `sk_v4_words[]` (request 2): word_count 317 -> 318. `mint_zuse` PASSES on amd64 (logs/20261108-181556): the size line, YES, "real Zuse minted", the prompt, 7 ok, the new stick one clean identity partition. (Her session is still the proxy's: recognising the real Zuse stick is 4.7.) -- [ ] 4.7 the real Zuse stick recognised and attached after the system stick; her session +- [~] 4.7 the real Zuse stick recognised and attached after the system stick; her session (`sk_v4_real_zuse_arrived()`: identity partition, no Artemis, checked against the marker's real key; attached at the chain's tail; a session already running moves to her; arriving before the system stick, remembered in `g_pending_real` and checked once the marker is read; the installed key now set at every login). Builds on three ISAs; `mint_zuse` checks "identity confirmed" after the minted line -- running (build/t4/mint-47.txt). Not yet handled: pulling the system stick while hers is in (her stick is the chain's tail, so it must go first -- Task 5's `system_pull_real`). - [ ] 4.8 `mint_zuse` passes on amd64; then the other tests: `mint_zuse_refuse`, `mint_pull_system`, `zuse_stick_first`, `mint_small`, the pair's stick offered - [ ] 4.9 regressions, full acceptance on three ISAs (new dict_hash, equal), FABRIC entry, commit diff --git a/kernel/src/v4/sk_v4_identity.c b/kernel/src/v4/sk_v4_identity.c index 7c4cf967..6ea0f5d8 100644 --- a/kernel/src/v4/sk_v4_identity.c +++ b/kernel/src/v4/sk_v4_identity.c @@ -106,6 +106,10 @@ static sk_v4_drive_t *g_offered; /* the last stick turned away as not this system's (or handed back by a mint * that was refused): still plugged in, so a mint offers it first */ static sk_v4_drive_t *g_spare; +/* a stick that looks like the real Zuse's (an identity partition, no + * Artemis) and arrived before the system stick: checked once it is in + * (plan 2026-10-29-stick-5, Review Focus 1) */ +static sk_v4_drive_t *g_pending_real; static const BootDisk *g_boot_disk; /* the loader's: the disk it booted from */ static int g_stick_in; /* a stick's Artemis partition is attached */ @@ -216,11 +220,12 @@ static void sk_v4_login(blkio_dev_t *dev, const uint8_t seed[32], const uint8_t console_fb_mirror(1); console_fb_enable_ttf(); console_println(line); - if (!g_zuse_cert_installed) { - memcpy(g_zuse_seed, seed, 32); - memcpy(g_zuse_pubkey, pubkey, 32); - g_zuse_cert_installed = 1; - } + /* on v4 the installed key is the logged-in Zuse's, set at each login -- + * the proxy's or the real Zuse's (plan 2026-10-29-stick-5, my calls); + * v3's one-way vm_zuse_cert_install() is v3's VM's and is not touched */ + memcpy(g_zuse_seed, seed, 32); + memcpy(g_zuse_pubkey, pubkey, 32); + g_zuse_cert_installed = 1; memcpy(g_zuse_root_pubkey, pubkey, 32); /* as v3's install_and_activate() */ g_zuse_root_known = 1; g_zuse_dev = dev; @@ -268,6 +273,39 @@ static int sk_v4_genesis(blkio_dev_t *dev, blkio_dev_t *art) return 1; } +/* A stick that looks like the real Zuse's -- a GPT disk (just read into + * g_gpt_parts) with a StarshipOS identity partition and no Artemis -- and the + * system stick in, its marker naming a real Zuse: her identity checked + * against the marker's real key. A match is attached (the block chain's tail: + * after the system stick's two partitions) and her session begins, or goes + * on as hers if the proxy's was running (after MINT-ZUSE). Returns 1 when the + * stick was hers to judge (a match, or "not this system's Zuse stick"), 0 + * when it is no Zuse stick of this system's at all. */ +static int sk_v4_real_zuse_arrived(sk_v4_drive_t *d, unsigned nparts) +{ + homeblocks_sig_t sig; + homeblocks_sig_result_t sig_rc; + uint8_t seed[32], pubkey[32]; + int ii = gpt_find(g_gpt_parts, nparts, gpt_type_starship_identity); + + if (!g_marker.present || !g_marker.has_real || ii < 0) return 0; + if (gpt_part_open(&d->part, &d->part_state, &d->raw, &g_gpt_parts[ii]) != BLKIO_OK) return 0; + sig_rc = homeblocks_sig_check(&d->part, HOMEBLOCKS_SIG_START_FBLOCK, &sig); + if (capsule_zuse_boot_check_key(&d->part, sig_rc, &sig, g_marker.real, seed, pubkey) != ZUSE_DRIVE_HERS) { + console_println("Zuse: not this system's Zuse stick -- not used"); + return 1; + } + if (blk_subsys_attach_device(&d->part) != BLK_OK) { + console_println("xhci: USB MSC block-subsystem attach failed"); + return 1; + } + d->used = &d->part; + g_proxy_fallback = 0; + sk_v4_login(&d->part, seed, pubkey, "Zuse: identity confirmed from attached thumbdrive"); + memset(seed, 0, sizeof seed); + return 1; +} + /* A drive that has arrived. This system's stick: its Artemis partition is * attached (the first disk slot, the block subsystem's), Zuse's public key is * read from its genesis marker, then its identity partition is attached and, @@ -302,6 +340,12 @@ static int sk_v4_drive_arrived(sk_v4_drive_t *d) return 1; } if (gpt_rc != GPT_OK || !sk_v4_is_this_stick(nparts)) { + /* the real Zuse's stick: an identity partition and no Artemis */ + if (gpt_rc == GPT_OK && gpt_find(g_gpt_parts, nparts, gpt_type_starship_identity) >= 0 && + gpt_find(g_gpt_parts, nparts, gpt_type_starship_artemis) < 0) { + if (!g_stick_in) { g_pending_real = d; return 1; } /* checked when the system stick is in */ + if (sk_v4_real_zuse_arrived(d, nparts)) return 1; + } console_println("USB: a drive that is not this system's stick -- not used"); if (gpt_rc != GPT_EIO) g_spare = d; return 1; @@ -363,6 +407,16 @@ static int sk_v4_drive_arrived(sk_v4_drive_t *d) if (which != ZUSE_DRIVE_HERS) return 1; if (g_marker.has_real) { g_proxy_fallback = 1; /* a real Zuse exists: the proxy gets no session */ + /* her stick came first: checked now that the marker is read */ + if (g_pending_real) { + sk_v4_drive_t *r = g_pending_real; + g_pending_real = (sk_v4_drive_t *)0; + if (gpt_read(&r->raw, g_gpt_parts, SK_V4_GPT_PARTS, &nparts) != GPT_OK || + !sk_v4_real_zuse_arrived(r, nparts)) { + console_println("USB: a drive that is not this system's stick -- not used"); + g_spare = r; + } + } return 1; } sk_v4_login(dev, seed, pubkey, "Zuse: identity confirmed from attached thumbdrive"); @@ -378,6 +432,7 @@ static void sk_v4_drive_left(sk_v4_drive_t *d) if (d == g_offered) g_offered = (sk_v4_drive_t *)0; /* the offered stick, pulled */ if (d == g_spare) g_spare = (sk_v4_drive_t *)0; + if (d == g_pending_real) g_pending_real = (sk_v4_drive_t *)0; if (dev) { d->used = (blkio_dev_t *)0; diff --git a/scripts/qemu_session.py b/scripts/qemu_session.py index 91646591..728a085a 100755 --- a/scripts/qemu_session.py +++ b/scripts/qemu_session.py @@ -562,6 +562,8 @@ elif m == "mint_zuse": check("Zuse: real Zuse minted onto the second stick", minted, near=r"MINT-ZUSE|Zuse:") t = text() after = t[t.find("Zuse: real Zuse minted onto the second stick"):] if minted else "" + check("her stick recognised: identity confirmed after it (her session on it)", + minted and "Zuse: identity confirmed from attached thumbdrive" in after, near=r"real Zuse minted") check("[zuse@Hera] ok> after it (the real Zuse)", minted and (re.search(PROMPT, after) is not None or wait_for(PROMPT, 120, count(PROMPT)))) n = count(PROMPT)