Implement homeblocks_sig_check(): the drive signature check (Phase 8)

Real, complete verification logic -- not yet wired to any write path.
homeblocks_sig_check(dev, sig_start_fblock, out_sig) reads the 4
consecutive 1KB blkio forth-blocks the 4KB header spans, verifies
magic -> version -> CRC-64 in order, returns HOMEBLOCKS_SIG_OK/_BLANK/
_BAD_VERSION/_BAD_CRC/_READ_ERROR. Reuses block_subsystem.c's existing
CRC-64/ISO (compute_crc64, previously static/file-local, now exposed
via block_subsystem.h) rather than a second CRC implementation --
same algorithm already proven via per-block checksums. Takes the
header's starting block as a plain parameter rather than resolving it
internally: verifies a signature given a location, finding that
location (GPT-partition-relative) stays the caller's job.

Verified against the actual shipped code, not a reimplementation: a
standalone host test links the real homeblocks_sig.c against a fake
in-memory blkio_dev and exercises all four outcomes -- blank media,
a correctly-minted header (round-trips drive_uuid/minted_time_ns), a
flipped CRC, an unrecognized version. All four pass. A full
QEMU-hotplug live test isn't proportionate yet since nothing calls
this function from the live kernel path -- wiring it into the attach
path is the next punch-list item. Clean zero-warning compile and
clean boot on all three architectures confirms no build/link
regression from exposing compute_crc64 and adding the new source
file to every kernel build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
This commit is contained in:
Robert Allan James
2026-08-26 07:05:37 -04:00
co-authored by Claude Sonnet 5
parent 10b96870c5
commit 2c45744995
13 changed files with 27292 additions and 6 deletions
+25 -3
View File
@@ -212,10 +212,32 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
3-arch acceptance boot needed for this step; that starts with the signature-check
implementation, the next punch-list item below.
- [ ] Implement the signature check, called before any write path touches a newly-inserted
drive.
- [x] **Implemented (2026-08-26): the check function itself, real and complete — not yet
wired to any write path.** `include/starkernel/homeblocks_sig.h` +
`src/starkernel/homeblocks_sig.c`: `homeblocks_sig_check(dev, sig_start_fblock, out_sig)`
reads the 4 consecutive 1KB `blkio` forth-blocks the 4KB header spans, verifies magic →
version → CRC-64 in order, returns one of `HOMEBLOCKS_SIG_OK`/`_BLANK`/`_BAD_VERSION`/
`_BAD_CRC`/`_READ_ERROR`. Reuses `block_subsystem.c`'s existing CRC-64/ISO
(`compute_crc64`, previously `static`/file-local, now exposed) rather than a second CRC
implementation — same algorithm already proven via per-block checksums. Takes the header's
starting block as a plain parameter rather than resolving it internally: this function
verifies a signature given a location; finding that location (GPT-partition-relative,
once a parser exists) stays the caller's job, not invented here.
- [ ] Implement the warn-and-refuse behavior for blank/foreign/unrecognized media.
**Verified against the actual shipped code**, not a reimplementation: a standalone host
test links the real `homeblocks_sig.c` against a fake in-memory `blkio_dev` and exercises
all four outcomes — blank media → `BLANK`, a correctly-minted header → `OK` (round-trips
`drive_uuid`/`minted_time_ns` correctly), a flipped CRC → `BAD_CRC`, an unrecognized
version → `BAD_VERSION`. All four pass. A full QEMU-hotplug live test isn't proportionate
yet — nothing calls this function from the live kernel path (deliberately; wiring it into
the attach path is the next item below), so a live boot check has nothing to exercise.
Clean zero-warning compile and clean boot on all three architectures confirms no
build/link regression from exposing `compute_crc64` and adding the new source file to
every kernel build.
- [ ] Implement the warn-and-refuse behavior for blank/foreign/unrecognized media — this is
where `homeblocks_sig_check()` above actually gets a live caller, wiring it into the real
drive-insertion path.
- [ ] Extend `acl_pinned`'s one-way-ratchet mechanism (already exists, already proven, just
needs applying) to gate zuse credential minting specifically — confirm whether this
+4 -2
View File
@@ -402,7 +402,8 @@ LOADER_SRCS_BASE := \
$(wildcard $(KERNEL_SRC)/usb/*.c) \
$(KERNEL_SRC)/repl.c \
$(KERNEL_SRC)/doe_log.c \
$(KERNEL_SRC)/heartbeat.c
$(KERNEL_SRC)/heartbeat.c \
$(KERNEL_SRC)/homeblocks_sig.c
LOADER_ASM := \
$(KERNEL_SRC)/arch/$(ARCH)/boot.S \
@@ -454,7 +455,8 @@ KERNEL_SRCS_BASE := \
$(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.c) \
$(KERNEL_SRC)/repl.c \
$(KERNEL_SRC)/doe_log.c \
$(KERNEL_SRC)/heartbeat.c
$(KERNEL_SRC)/heartbeat.c \
$(KERNEL_SRC)/homeblocks_sig.c
KERNEL_ASM := $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.S)
BIN
View File
Binary file not shown.
+6
View File
@@ -293,6 +293,12 @@ int blk_get_volume_meta(blk_volume_meta_t *meta);
int blk_set_volume_meta(const blk_volume_meta_t *meta);
/* CRC-64/ISO (poly 0x42F0E1EBA9EA3693), reflected, init/final all-ones --
* exposed for homeblocks_sig.c's drive-signature integrity check, which
* needs the exact same algorithm this file already uses for per-block
* checksums rather than a second, duplicate CRC implementation. */
uint64_t compute_crc64(const uint8_t *data, size_t len);
int blk_is_valid(uint32_t block_num);
uint32_t blk_get_total_blocks(void);
+50
View File
@@ -118,6 +118,56 @@ typedef struct {
* same discipline stadium.h's own header-size checks already use. */
typedef char homeblocks_sig_size_check[(sizeof(homeblocks_sig_t) == 4096) ? 1 : -1];
/*===========================================================================
* Signature check (FABRIC-3.md, Milestone 4)
*===========================================================================*/
typedef enum {
HOMEBLOCKS_SIG_OK = 0, /* magic, version, and crc all check out */
HOMEBLOCKS_SIG_BLANK, /* magic does not match -- blank or foreign media */
HOMEBLOCKS_SIG_BAD_VERSION, /* magic matches, version unrecognized */
HOMEBLOCKS_SIG_BAD_CRC, /* magic+version match, crc fails -- corrupt or tampered */
HOMEBLOCKS_SIG_READ_ERROR /* could not read from the device at all */
} homeblocks_sig_result_t;
/* Forward-declared, not included here -- avoids a hard dependency from this
* small format header onto blkio.h's full device/vtable machinery for
* callers that only need the struct layout (e.g. a future minting tool). */
struct blkio_dev;
/*
* homeblocks_sig_check - Read and verify the drive signature header.
*
* Reads 4 consecutive 1KB "forth blocks" (dev->read()'s own unit) starting
* at sig_start_fblock into a local 4KB buffer and interprets it as a
* homeblocks_sig_t. Deliberately takes the starting block as a plain
* parameter rather than resolving it internally -- this function verifies a
* signature given a location; finding that location (GPT-partition-relative
* today, once a GPT parser exists) is the caller's job, not invented here.
*
* @param dev Open block device to read from.
* @param sig_start_fblock First of 4 consecutive forth-blocks holding the
* 4KB header.
* @param out_sig On HOMEBLOCKS_SIG_OK, populated with the verified
* header. Left unspecified on any other result.
* @return HOMEBLOCKS_SIG_OK, or the specific reason for refusal.
*/
homeblocks_sig_result_t homeblocks_sig_check(struct blkio_dev *dev,
uint32_t sig_start_fblock,
homeblocks_sig_t *out_sig);
/*
* homeblocks_sig_compute_crc - CRC-64 over every field of `sig` up to but
* not including hdr_crc itself and the trailing padding -- the same
* boundary homeblocks_sig_check() verifies against and any future minting
* code must use when writing a fresh header. Exposed publicly since both
* directions (check and future mint) need the identical computation.
*
* @param sig Header to checksum. hdr_crc and _pad are not read.
* @return The CRC-64 value that hdr_crc should hold for `sig` to verify.
*/
uint64_t homeblocks_sig_compute_crc(const homeblocks_sig_t *sig);
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -104,7 +104,7 @@ static void crc64_init(void) {
crc64_inited = 1;
}
static inline uint64_t compute_crc64(const uint8_t *data, size_t len) {
uint64_t compute_crc64(const uint8_t *data, size_t len) {
if (!crc64_inited) crc64_init();
uint64_t crc = 0xFFFFFFFFFFFFFFFFULL;
for (size_t i = 0; i < len; i++) {
+87
View File
@@ -0,0 +1,87 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* homeblocks_sig.c - Drive signature check (FABRIC-3.md, Milestone 4).
* See starkernel/homeblocks_sig.h for the format and interface design.
*/
#include "starkernel/homeblocks_sig.h"
#include <stddef.h>
#include <string.h>
#include "blkio.h"
#include "block_subsystem.h" /* compute_crc64() -- same CRC-64/ISO this file's
* check reuses rather than duplicating */
uint64_t homeblocks_sig_compute_crc(const homeblocks_sig_t *sig) {
/* Covers every field up to but not including hdr_crc itself (and never
* _pad, which sits after it) -- offsetof is the exact boundary, not a
* hand-counted byte offset that could drift out of sync with the
* struct's own field list. */
size_t crc_span = offsetof(homeblocks_sig_t, hdr_crc);
return compute_crc64((const uint8_t *)sig, crc_span);
}
homeblocks_sig_result_t homeblocks_sig_check(struct blkio_dev *dev,
uint32_t sig_start_fblock,
homeblocks_sig_t *out_sig) {
uint8_t buf[4096];
uint32_t i;
homeblocks_sig_t local;
uint64_t expected_crc;
if (!dev) return HOMEBLOCKS_SIG_READ_ERROR;
/* homeblocks_sig_t is exactly one 4KiB devblock; blkio's own unit is a
* 1KiB "forth block" (BLKIO_FORTH_BLOCK_SIZE), so the header spans 4
* consecutive reads starting at sig_start_fblock. */
for (i = 0; i < 4; i++) {
if (blkio_read((blkio_dev_t *)dev, sig_start_fblock + i,
buf + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) {
return HOMEBLOCKS_SIG_READ_ERROR;
}
}
/* Copy into a properly-aligned local rather than reinterpreting buf's
* address directly -- buf is only byte-aligned, and homeblocks_sig_t
* has uint64_t members; a raw cast would be a strict-aliasing and
* alignment violation for no benefit over one memcpy. */
memcpy(&local, buf, sizeof(local));
if (HOMEBLOCKS_SIG_GET_MAGIC(local.magic) != (uint32_t)(HOMEBLOCKS_SIG_MAGIC & 0xFFFFFFFFULL)) {
return HOMEBLOCKS_SIG_BLANK;
}
if (HOMEBLOCKS_SIG_GET_VERSION(local.magic) != HOMEBLOCKS_SIG_VERSION_0) {
return HOMEBLOCKS_SIG_BAD_VERSION;
}
expected_crc = homeblocks_sig_compute_crc(&local);
if (expected_crc != local.hdr_crc) {
return HOMEBLOCKS_SIG_BAD_CRC;
}
if (out_sig) *out_sig = local;
return HOMEBLOCKS_SIG_OK;
}