diff --git a/kernel/include/starkernel/v4/sk_v4_mint.h b/kernel/include/starkernel/v4/sk_v4_mint.h index cdcbf156..6e2b23e9 100644 --- a/kernel/include/starkernel/v4/sk_v4_mint.h +++ b/kernel/include/starkernel/v4/sk_v4_mint.h @@ -9,6 +9,8 @@ #ifndef STARKERNEL_V4_SK_V4_MINT_H #define STARKERNEL_V4_SK_V4_MINT_H +#include + /* The whole of STICK.md 6a's minting: asks for a stick, shows its size, writes * nothing until YES, then a GPT and a fresh Zuse identity on it, read back, * and only then the real key in the genesis marker. 0 when she is minted; @@ -16,4 +18,8 @@ * MINT-ZUSE word, and (Task 5) the fixed screen's R. */ int sk_v4_mint_zuse(void); +/* The same, with Zuse's seed and its read-back in `seeds` (64 bytes), owned by the caller: on every + * return the 64 bytes are zero (#176). */ +int sk_v4_mint_zuse_with(uint8_t seeds[64]); + #endif /* STARKERNEL_V4_SK_V4_MINT_H */ diff --git a/kernel/src/v4/sk_v4_mint.c b/kernel/src/v4/sk_v4_mint.c index ad706ec2..d9b9cce8 100644 --- a/kernel/src/v4/sk_v4_mint.c +++ b/kernel/src/v4/sk_v4_mint.c @@ -81,7 +81,10 @@ static int erased(void) /* its old partition table is gone (#168 return -1; } -int sk_v4_mint_zuse(void) +/* The mint, with Zuse's seed and its read-back in `seeds`, which the caller owns: seed is its first + * 32 bytes, rseed the next 32. Called only by sk_v4_mint_zuse_with, which wipes `seeds` after it + * whichever way it returned (#176). */ +static int mint(uint8_t seeds[64]) { static blkio_dev_t part; static gpt_part_dev_t part_state; @@ -89,7 +92,8 @@ int sk_v4_mint_zuse(void) 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]; + uint8_t guid[16], pub[32], rpub[32]; + uint8_t *seed = seeds, *rseed = seeds + 32; homeblocks_sig_t sig; homeblocks_sig_result_t sig_rc; sk_v4_marker_t m; @@ -148,10 +152,25 @@ int sk_v4_mint_zuse(void) m.has_real = 1; if (sk_v4_marker_write(&m) != 0) return erased(); sk_v4_identity_set_marker(&m); - memset(seed, 0, sizeof seed); - memset(rseed, 0, sizeof rseed); + memset(seeds, 0, 64); console_println("Zuse: real Zuse minted onto the second stick"); sk_v4_identity_recheck_offered(); return 0; } + +/* One place wipes the seeds, after every return of the mint -- its refusals, each failure after + * YES, and success alike. They were wiped on success only, and left in memory on every other way + * out (#176). */ +int sk_v4_mint_zuse_with(uint8_t seeds[64]) +{ + int rc = mint(seeds); + memset(seeds, 0, 64); + return rc; +} + +int sk_v4_mint_zuse(void) +{ + static uint8_t seeds[64]; + return sk_v4_mint_zuse_with(seeds); +} diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile index 616e12e7..c6fb3f81 100644 --- a/kernel/tests/Makefile +++ b/kernel/tests/Makefile @@ -19,7 +19,9 @@ CC ?= cc CFLAGS := -std=c99 -Wall -Wextra -Werror -O2 -g -I$(HERE) -I$(ROOT)/kernel/include \ -I$(ROOT)/kernel/include/starkernel -I$(ROOT)/v3/include -TESTS := test_gpt test_usb_config test_sk_v4_stop test_sk_v4_slots +TESTS := test_gpt test_usb_config test_sk_v4_stop test_sk_v4_slots test_sk_v4_mint +SRC_test_sk_v4_mint := $(ROOT)/kernel/src/v4/sk_v4_mint.c $(ROOT)/kernel/src/gpt.c +EXTRA_test_sk_v4_mint := -D__STARKERNEL__ -I$(ROOT)/v4/include SRC_test_sk_v4_slots := $(ROOT)/kernel/src/v4/sk_v4_slots.c SRC_test_sk_v4_stop := $(ROOT)/kernel/src/v4/sk_v4_stop.c SRC_test_gpt := $(ROOT)/kernel/src/gpt.c @@ -28,7 +30,7 @@ SRC_test_usb_config := $(ROOT)/kernel/src/usb/usb_config.c define TEST_RULE $(BUILD)/$(1): $(HERE)/$(1).c $(HERE)/fakes.c $(HERE)/fakes.h $(HERE)/harness.h $$(SRC_$(1)) @mkdir -p $(BUILD) - $(CC) $(CFLAGS) $(HERE)/$(1).c $(HERE)/fakes.c $$(SRC_$(1)) -o $$@ + $(CC) $(CFLAGS) $$(EXTRA_$(1)) $(HERE)/$(1).c $(HERE)/fakes.c $$(SRC_$(1)) -o $$@ .PHONY: run-$(1) run-$(1): $(BUILD)/$(1) diff --git a/kernel/tests/test_sk_v4_mint.c b/kernel/tests/test_sk_v4_mint.c new file mode 100644 index 00000000..350506ca --- /dev/null +++ b/kernel/tests/test_sk_v4_mint.c @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LicenseRef-Starship-1.0 + * Copyright (c) 2023-2026 Robert A. James + */ +/* test_sk_v4_mint.c -- kernel/src/v4/sk_v4_mint.c on the host (#176): Zuse's seed and its read-back + * are wiped on every return of the mint, not only on success. The caller owns the 64-byte buffer + * (sk_v4_mint_zuse_with); each case fills it with 0xEE, runs the mint to one of its exits, and + * checks it is all zero after. The real gpt.c on the harness's RAM disk; the identity code, the key + * mint, the signature checks, the marker and the keyboard faked. + */ +#include "harness.h" +#include "fakes.h" +#include "starkernel/gpt.h" +#include "starkernel/capsule_zuse_boot.h" +#include "starkernel/homeblocks_sig.h" +#include "starkernel/v4/sk_v4_mint.h" +#include "starkernel/v4/sk_v4_marker.h" +#include + +/* ---- the scenario ---- */ +static blkio_dev_t stick; +static fake_disk disk; +static int offer, mint_rc, key_hers, marker_present, marker_rc; +static const char *typed; static unsigned typed_at; +static sk_v4_marker_t marker, written; + +/* ---- the fakes the mint calls ---- */ +int sk_v4_identity_offer_wait(blkio_dev_t **raw, uint64_t *bytes) { *raw = &stick; *bytes = (uint64_t)disk.blocks * 1024u; return offer; } +int sk_v4_identity_stick_in(void) { return 1; } +int sk_v4_identity_offered_present(void) { return 1; } +void sk_v4_identity_release_offered(void) { } +void sk_v4_identity_recheck_offered(void) { } +void sk_v4_identity_beat(void) { } +const sk_v4_marker_t *sk_v4_identity_marker(void) { marker.present = marker_present; return ▮ } +void sk_v4_identity_set_marker(const sk_v4_marker_t *m) { marker = *m; } +int sk_v4_marker_write(const sk_v4_marker_t *m) { written = *m; return marker_rc; } +int sk_input_getc(void) { return (typed && typed[typed_at]) ? (unsigned char)typed[typed_at++] : -1; } +void arch_halt(void) { } +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); /* the seed is in the buffer even when it then fails */ + return mint_rc; +} +homeblocks_sig_result_t homeblocks_sig_check(struct blkio_dev *dev, uint32_t start_fblock, homeblocks_sig_t *out) +{ + (void)dev; (void)start_fblock; memset(out, 0, sizeof *out); return (homeblocks_sig_result_t)0; +} +zuse_drive_t capsule_zuse_boot_check_key(struct blkio_dev *dev, homeblocks_sig_result_t sig_rc, const homeblocks_sig_t *sig, + const uint8_t want[32], uint8_t seed[32], uint8_t pubkey[32]) +{ + (void)dev; (void)sig_rc; (void)sig; (void)want; + memset(seed, 0x5A, 32); memset(pubkey, 0x11, 32); /* read back, as minted */ + return key_hers ? ZUSE_DRIVE_HERS : (zuse_drive_t)(ZUSE_DRIVE_HERS + 1); +} + +static int all_zero(const uint8_t *p, unsigned n) { unsigned i; for (i = 0; i < n; i++) if (p[i]) return 0; return 1; } + +/* run the mint once: the scenario, then the buffer must be zero */ +static void run(const char *what, int offer_, uint32_t blocks, const char *typed_, int mint_rc_, int hers, int present, int mrc, int want_rc) +{ + uint8_t seeds[64]; + int rc; + fake_disk_open(&stick, &disk, blocks); + offer = offer_; typed = typed_; typed_at = 0; mint_rc = mint_rc_; key_hers = hers; marker_present = present; marker_rc = mrc; + memset(&marker, 0, sizeof marker); memset(seeds, 0xEE, sizeof seeds); + fake_console_reset(); + rc = sk_v4_mint_zuse_with(seeds); + CHECK(rc == want_rc, "%s: returns %d (got %d)", what, want_rc, rc); + CHECK(all_zero(seeds, 64), "#176: %s -- the seed buffer is zero after", what); + fake_disk_free(&disk); +} + +int main(void) +{ + const uint32_t BIG = 70000u, SMALL = 1000u; /* 1 KiB blocks: 68 MiB, and under the 64 MiB the mint wants */ + + fake_rng_reset(); + run("no stick offered", 0, BIG, "YES\r", 0, 1, 1, 0, -1); + run("a stick under 64 MiB", 1, SMALL, "YES\r", 0, 1, 1, 0, -1); + run("not YES", 1, BIG, "no\r", 0, 1, 1, 0, -1); + run("the key mint fails", 1, BIG, "YES\r", -1, 1, 1, 0, -1); + run("the read-back is not hers", 1, BIG, "YES\r", 0, 0, 1, 0, -1); + run("no marker on the system stick", 1, BIG, "YES\r", 0, 1, 0, 0, -1); + run("the marker write fails", 1, BIG, "YES\r", 0, 1, 1, -1, -1); + run("minted", 1, BIG, "YES\r", 0, 1, 1, 0, 0); + CHECK(written.has_real == 1 && written.real[0] == 0x11, "minted: the marker names the real Zuse's key"); + return DONE(); +} diff --git a/logs/20261112-181719/kernel-tests/test_sk_v4_mint.log b/logs/20261112-181719/kernel-tests/test_sk_v4_mint.log new file mode 100644 index 00000000..3ea4c692 --- /dev/null +++ b/logs/20261112-181719/kernel-tests/test_sk_v4_mint.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f4ae28a84b61c501dc1c26861a90461b41bce9f899d939505a86babe9c1939f +size 1096