feat(kernel): sk_v4_mint_zuse -- the real Zuse minted onto a second stick, read back before the marker names her (#92, 4.5) [skip ci]
Asks for a stick, shows its size, writes nothing until YES; then gpt_write_one, the partition's first devblocks zeroed, a fresh Zuse minted, read back, and only then the marker's real key. Any failure before the marker: 'failed -- nothing changed'. The gate remembers the last stick it turned away as a spare, offered first by the next mint. Builds on three ISAs; not yet called. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
This commit is contained in:
@@ -100,6 +100,9 @@ static uint64_t g_last_beat;
|
||||
* not refused; it is remembered here until the mint is done with it */
|
||||
static int g_mint_waiting;
|
||||
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;
|
||||
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 */
|
||||
|
||||
@@ -297,6 +300,7 @@ static int sk_v4_drive_arrived(sk_v4_drive_t *d)
|
||||
}
|
||||
if (gpt_rc != GPT_OK || !sk_v4_is_this_stick(nparts)) {
|
||||
console_println("USB: a drive that is not this system's stick -- not used");
|
||||
if (gpt_rc != GPT_EIO) g_spare = d;
|
||||
return 1;
|
||||
}
|
||||
if (sk_v4_grow_stick(d, &nparts) != 0) {
|
||||
@@ -370,6 +374,7 @@ static void sk_v4_drive_left(sk_v4_drive_t *d)
|
||||
blkio_dev_t *dev = d->used;
|
||||
|
||||
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 (dev) {
|
||||
d->used = (blkio_dev_t *)0;
|
||||
@@ -457,7 +462,8 @@ int sk_v4_identity_offer_wait(blkio_dev_t **raw, uint64_t *bytes)
|
||||
{
|
||||
int got = 0;
|
||||
|
||||
g_offered = (sk_v4_drive_t *)0;
|
||||
g_offered = g_spare; /* one already plugged in, turned away */
|
||||
g_spare = (sk_v4_drive_t *)0;
|
||||
g_mint_waiting = 1;
|
||||
for (;;) {
|
||||
sk_v4_identity_beat();
|
||||
@@ -474,6 +480,32 @@ int sk_v4_identity_offer_wait(blkio_dev_t **raw, uint64_t *bytes)
|
||||
return got;
|
||||
}
|
||||
|
||||
int sk_v4_identity_stick_in(void)
|
||||
{
|
||||
return g_stick_in;
|
||||
}
|
||||
|
||||
int sk_v4_identity_offered_present(void)
|
||||
{
|
||||
return g_offered != (sk_v4_drive_t *)0;
|
||||
}
|
||||
|
||||
void sk_v4_identity_release_offered(void)
|
||||
{
|
||||
g_spare = g_offered;
|
||||
g_offered = (sk_v4_drive_t *)0;
|
||||
}
|
||||
|
||||
const sk_v4_marker_t *sk_v4_identity_marker(void)
|
||||
{
|
||||
return &g_marker;
|
||||
}
|
||||
|
||||
void sk_v4_identity_set_marker(const sk_v4_marker_t *m)
|
||||
{
|
||||
g_marker = *m;
|
||||
}
|
||||
|
||||
void sk_v4_identity_recheck_offered(void)
|
||||
{
|
||||
sk_v4_drive_t *d = g_offered;
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/* sk_v4_mint.c -- MINT-ZUSE: the real Zuse minted onto a second stick
|
||||
* (docs/v4.0.0/STICK.md 6a; plan 2026-10-29-stick-5, Task 4).
|
||||
*
|
||||
* The stick is asked for, offered (sk_v4_identity_offer_wait), its size
|
||||
* shown, and nothing is written until YES is typed. Then a GPT with one
|
||||
* StarshipOS identity partition (gpt_write_one), the partition's first
|
||||
* devblocks zeroed (a used stick's old bytes must not read as an identity), a
|
||||
* fresh Zuse minted into it, read back -- and only then the real key written
|
||||
* into the genesis marker on the system stick's Artemis. Any failure before
|
||||
* the marker leaves the marker as it was: the old Zuse stick, if there is
|
||||
* one, still works.
|
||||
*/
|
||||
#include "starkernel/v4/sk_v4_mint.h"
|
||||
#include "starkernel/v4/sk_v4_identity.h"
|
||||
#include "starkernel/v4/sk_v4_marker.h"
|
||||
#include "starkernel/capsule_zuse_boot.h"
|
||||
#include "starkernel/homeblocks_sig.h"
|
||||
#include "starkernel/kbd_input.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/arch.h"
|
||||
#include "starkernel/gpt.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MINT_MIN_BYTES (64ULL * 1024ULL * 1024ULL) /* a stick under 64 MiB is refused */
|
||||
#define MINT_IDENT_SECTORS (500ULL * 2048ULL) /* the identity partition: 500 MiB */
|
||||
#define MINT_ZERO_FBLOCKS 32u /* its first 8 devblocks, zeroed */
|
||||
|
||||
/* "MINT-ZUSE: a stick of <N> MiB -- all of it is erased" */
|
||||
static void say_size(uint64_t bytes)
|
||||
{
|
||||
static char line[80];
|
||||
char num[24];
|
||||
uint64_t mib = bytes / (1024ULL * 1024ULL);
|
||||
int n = 0, i = 0;
|
||||
|
||||
do { num[n++] = (char)('0' + (int)(mib % 10u)); mib /= 10u; } while (mib && n < 23);
|
||||
memcpy(line, "MINT-ZUSE: a stick of ", 22);
|
||||
i = 22;
|
||||
while (n) line[i++] = num[--n];
|
||||
memcpy(line + i, " MiB -- all of it is erased", 28); /* with its NUL */
|
||||
console_println(line);
|
||||
}
|
||||
|
||||
/* A line typed at the keyboard or the serial line, echoed, while the beat
|
||||
* runs. 1 when Enter ended it; 0 when the system stick or the offered stick
|
||||
* left first. */
|
||||
static int read_answer(char *buf, int max)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
for (;;) {
|
||||
int c = sk_input_getc();
|
||||
if (c < 0) {
|
||||
if (!sk_v4_identity_stick_in() || !sk_v4_identity_offered_present()) return 0;
|
||||
sk_v4_identity_beat();
|
||||
arch_halt();
|
||||
continue;
|
||||
}
|
||||
if (c == '\r' || c == '\n') { console_putc('\n'); buf[len] = '\0'; return 1; }
|
||||
if ((c == 8 || c == 127) && len > 0) { len--; console_puts("\b \b"); continue; }
|
||||
if (c >= 32 && c < 127 && len < max - 1) { buf[len++] = (char)c; console_putc((char)c); }
|
||||
}
|
||||
}
|
||||
|
||||
static int failed(void)
|
||||
{
|
||||
console_println("MINT-ZUSE: failed -- nothing changed");
|
||||
sk_v4_identity_release_offered();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sk_v4_mint_zuse(void)
|
||||
{
|
||||
static blkio_dev_t part;
|
||||
static gpt_part_dev_t part_state;
|
||||
static gpt_part_t parts[4];
|
||||
static uint8_t zero[BLKIO_FORTH_BLOCK_SIZE];
|
||||
blkio_dev_t *raw;
|
||||
uint64_t bytes, sectors;
|
||||
uint8_t guid[16], seed[32], pub[32], rseed[32], rpub[32];
|
||||
homeblocks_sig_t sig;
|
||||
homeblocks_sig_result_t sig_rc;
|
||||
sk_v4_marker_t m;
|
||||
unsigned n, i;
|
||||
int pi, rc;
|
||||
char answer[8];
|
||||
|
||||
console_println("MINT-ZUSE: plug in the stick for the real Zuse");
|
||||
if (!sk_v4_identity_offer_wait(&raw, &bytes)) {
|
||||
console_println("MINT-ZUSE: stopped -- nothing written");
|
||||
return -1;
|
||||
}
|
||||
if (bytes < MINT_MIN_BYTES) {
|
||||
console_println("MINT-ZUSE: the stick is under 64 MiB");
|
||||
sk_v4_identity_release_offered();
|
||||
return -1;
|
||||
}
|
||||
say_size(bytes);
|
||||
console_println("MINT-ZUSE: type YES to write it");
|
||||
if (!read_answer(answer, (int)sizeof answer) || strcmp(answer, "YES") != 0) {
|
||||
console_println("MINT-ZUSE: stopped -- nothing written");
|
||||
if (sk_v4_identity_offered_present()) sk_v4_identity_release_offered();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* the stick: one identity partition, 500 MiB or the stick less 2 MiB */
|
||||
sectors = bytes / 512u;
|
||||
sectors = sectors - 4096u < MINT_IDENT_SECTORS ? sectors - 4096u : MINT_IDENT_SECTORS;
|
||||
rc = gpt_write_one(raw, gpt_type_starship_identity, sectors, guid);
|
||||
if (rc == GPT_BAD) {
|
||||
console_println("MINT-ZUSE: the stick's sectors are not 512 bytes");
|
||||
sk_v4_identity_release_offered();
|
||||
return -1;
|
||||
}
|
||||
if (rc != GPT_OK || gpt_read(raw, parts, 4u, &n) != GPT_OK) return failed();
|
||||
pi = gpt_find(parts, n, gpt_type_starship_identity);
|
||||
if (pi < 0 || gpt_part_open(&part, &part_state, raw, &parts[pi]) != BLKIO_OK) return failed();
|
||||
|
||||
/* nothing of the stick's old bytes may read as an identity */
|
||||
for (i = 0; i < MINT_ZERO_FBLOCKS; i++)
|
||||
if (blkio_write(&part, i, zero) != BLKIO_OK) return failed();
|
||||
|
||||
/* Zuse, minted, then read back from the stick */
|
||||
if (capsule_zuse_boot_genesis_mint(&part, seed, pub) != 0) return failed();
|
||||
sig_rc = homeblocks_sig_check(&part, HOMEBLOCKS_SIG_START_FBLOCK, &sig);
|
||||
if (capsule_zuse_boot_check_key(&part, sig_rc, &sig, pub, rseed, rpub) != ZUSE_DRIVE_HERS ||
|
||||
memcmp(rseed, seed, 32) != 0)
|
||||
return failed();
|
||||
|
||||
/* only now the marker: the proxy kept, the real Zuse named */
|
||||
m = *sk_v4_identity_marker();
|
||||
if (!m.present) return failed();
|
||||
memcpy(m.real, pub, 32);
|
||||
m.has_real = 1;
|
||||
if (sk_v4_marker_write(&m) != 0) return failed();
|
||||
sk_v4_identity_set_marker(&m);
|
||||
memset(seed, 0, sizeof seed);
|
||||
memset(rseed, 0, sizeof rseed);
|
||||
|
||||
console_println("Zuse: real Zuse minted onto the second stick");
|
||||
sk_v4_identity_recheck_offered();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user