fix(v4): MINT-ZUSE wipes Zuse's seed on every return, not only on success (#176, the mint)
sk_v4_mint_zuse wiped seed and rseed only when the real Zuse was minted; its refusals and every failure after YES left them in stack memory. Option 1 (Captain Bob, 2026-11-12: "1 is great"): the seeds are in a 64-byte buffer the caller owns. sk_v4_mint_zuse_with(seeds) runs the mint (now a private mint()) and then wipes the buffer, whichever way it returned -- one place, every exit; sk_v4_mint_zuse() gives it a buffer of its own. kernel/tests/test_sk_v4_mint.c, 17 checks: the real gpt.c on the RAM disk, the identity code, key mint, signature checks, marker and keyboard faked; each of the mint's eight exits run with the buffer filled with 0xEE first -- no stick, under 64 MiB, not YES, the key mint failing, the read-back not hers, no marker, the marker write failing, and minted. Against the mint as it was: 7 failures (every exit but success). Now: 0. Both runs in logs/20261112-181719/. Built for amd64, aarch64, riscv64. The identity code's two (sk_v4_genesis, sk_v4_real_zuse_arrived) follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
@@ -9,6 +9,8 @@
|
|||||||
#ifndef STARKERNEL_V4_SK_V4_MINT_H
|
#ifndef STARKERNEL_V4_SK_V4_MINT_H
|
||||||
#define STARKERNEL_V4_SK_V4_MINT_H
|
#define STARKERNEL_V4_SK_V4_MINT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* The whole of STICK.md 6a's minting: asks for a stick, shows its size, writes
|
/* 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,
|
* 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;
|
* 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. */
|
* MINT-ZUSE word, and (Task 5) the fixed screen's R. */
|
||||||
int sk_v4_mint_zuse(void);
|
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 */
|
#endif /* STARKERNEL_V4_SK_V4_MINT_H */
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ static int erased(void) /* its old partition table is gone (#168
|
|||||||
return -1;
|
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 blkio_dev_t part;
|
||||||
static gpt_part_dev_t part_state;
|
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];
|
static uint8_t zero[BLKIO_FORTH_BLOCK_SIZE];
|
||||||
blkio_dev_t *raw;
|
blkio_dev_t *raw;
|
||||||
uint64_t bytes, sectors;
|
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_t sig;
|
||||||
homeblocks_sig_result_t sig_rc;
|
homeblocks_sig_result_t sig_rc;
|
||||||
sk_v4_marker_t m;
|
sk_v4_marker_t m;
|
||||||
@@ -148,10 +152,25 @@ int sk_v4_mint_zuse(void)
|
|||||||
m.has_real = 1;
|
m.has_real = 1;
|
||||||
if (sk_v4_marker_write(&m) != 0) return erased();
|
if (sk_v4_marker_write(&m) != 0) return erased();
|
||||||
sk_v4_identity_set_marker(&m);
|
sk_v4_identity_set_marker(&m);
|
||||||
memset(seed, 0, sizeof seed);
|
memset(seeds, 0, 64);
|
||||||
memset(rseed, 0, sizeof rseed);
|
|
||||||
|
|
||||||
console_println("Zuse: real Zuse minted onto the second stick");
|
console_println("Zuse: real Zuse minted onto the second stick");
|
||||||
sk_v4_identity_recheck_offered();
|
sk_v4_identity_recheck_offered();
|
||||||
return 0;
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ CC ?= cc
|
|||||||
CFLAGS := -std=c99 -Wall -Wextra -Werror -O2 -g -I$(HERE) -I$(ROOT)/kernel/include \
|
CFLAGS := -std=c99 -Wall -Wextra -Werror -O2 -g -I$(HERE) -I$(ROOT)/kernel/include \
|
||||||
-I$(ROOT)/kernel/include/starkernel -I$(ROOT)/v3/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_slots := $(ROOT)/kernel/src/v4/sk_v4_slots.c
|
||||||
SRC_test_sk_v4_stop := $(ROOT)/kernel/src/v4/sk_v4_stop.c
|
SRC_test_sk_v4_stop := $(ROOT)/kernel/src/v4/sk_v4_stop.c
|
||||||
SRC_test_gpt := $(ROOT)/kernel/src/gpt.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
|
define TEST_RULE
|
||||||
$(BUILD)/$(1): $(HERE)/$(1).c $(HERE)/fakes.c $(HERE)/fakes.h $(HERE)/harness.h $$(SRC_$(1))
|
$(BUILD)/$(1): $(HERE)/$(1).c $(HERE)/fakes.c $(HERE)/fakes.h $(HERE)/harness.h $$(SRC_$(1))
|
||||||
@mkdir -p $(BUILD)
|
@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)
|
.PHONY: run-$(1)
|
||||||
run-$(1): $(BUILD)/$(1)
|
run-$(1): $(BUILD)/$(1)
|
||||||
|
|||||||
@@ -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 <string.h>
|
||||||
|
|
||||||
|
/* ---- 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();
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user