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:
co-authored by
Claude Sonnet 5
parent
10b96870c5
commit
2c45744995
+25
-3
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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
@@ -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++) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 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;
|
||||
}
|
||||
Reference in New Issue
Block a user