Milestone 6: BLOCK_MAP.md signature-status column

--manifest mode's file scan is a completely separate code path from
build mode (only ever walks .4th files, never the embedded PKI cert or
font capsule) -- extended it to accept the same optional --sign-key
<path> prefix build mode already has, factoring the key-loading code
into a shared load_sign_key(), so the manifest can report real
per-capsule signing status without touching or requiring a rebuild of
capsule_generated.c.

New "Signed" column on the capsule summary table: yes/no when
--sign-key was given, n/a (with an explanatory footnote) when it
wasn't -- never a bare blank that could be misread as "unsigned".
Makefile.starkernel's manifest-generation call site now passes the same
SIGN_KEY_ARGS the real build uses, so capsules/BLOCK_MAP.md reflects
this machine's actual signed state by default.

Verified: clean compile, BLOCK_MAP.md correctly shows "yes" for all 31
tracked capsules on a real signed build; a quick amd64 boot (no kernel
code touched, host tooling only) confirmed no regression.

This closes every open Milestone 6 item except magic-number
content-type detection (shared with Milestone 4, not started) and the
hard-refuse flip (deliberately deferred). Documented in FABRIC-3.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
This commit is contained in:
Robert Allan James
2026-08-26 21:43:50 -04:00
co-authored by Claude Sonnet 5
parent 2fc55f47e1
commit c640f99211
5 changed files with 9170 additions and 75 deletions
+76 -39
View File
@@ -169,6 +169,7 @@ typedef struct {
uint64_t hash;
int blocks[MAX_BLOCKS_PER_CAPSULE];
int block_count;
int has_sig; /* 1 if signed this run (--manifest --sign-key <path>) */
} ManifestEntry;
static ManifestEntry manifest_entries[MAX_CAPSULES];
@@ -607,6 +608,12 @@ static int manifest_file(const char *fpath, const struct stat *sb,
e->block_count = collect_block_numbers(data, (size_t)sz,
e->blocks, MAX_BLOCKS_PER_CAPSULE);
qsort(e->blocks, (size_t)e->block_count, sizeof(int), cmp_int);
e->has_sig = 0;
if (have_sign_seed) {
uint8_t sig[64];
sign_capsule_bytes(data, (size_t)sz, sig);
e->has_sig = 1;
}
free(data);
return 0;
@@ -654,8 +661,8 @@ static void generate_manifest(FILE *out) {
/* Capsule summary */
fprintf(out, "## Capsule Summary\n\n");
fprintf(out, "| Capsule | Blocks claimed | xxHash64 |\n");
fprintf(out, "|---------|----------------|----------|\n");
fprintf(out, "| Capsule | Blocks claimed | xxHash64 | Signed |\n");
fprintf(out, "|---------|----------------|----------|--------|\n");
for (int i = 0; i < manifest_count; i++) {
ManifestEntry *e = &manifest_entries[i];
fprintf(out, "| `%s` | ", e->name);
@@ -667,9 +674,16 @@ static void generate_manifest(FILE *out) {
fprintf(out, "%d", e->blocks[j]);
}
}
fprintf(out, " | `0x%016" PRIx64 "` |\n", e->hash);
fprintf(out, " | `0x%016" PRIx64 "` | %s |\n", e->hash,
have_sign_seed ? (e->has_sig ? "yes" : "no") : "n/a");
}
fprintf(out, "\n");
if (!have_sign_seed) {
fprintf(out,
"*Signed column is `n/a`: this manifest run had no `--sign-key`. "
"Re-run with `--sign-key <path>` to check signing status "
"(does not modify or require rebuilding capsule_generated.c).*\n\n");
}
/* Block map sorted by LBN */
fprintf(out, "## Block Map (sorted by LBN)\n\n");
@@ -972,10 +986,57 @@ static void generate_output(FILE *out) {
* Main
*===========================================================================*/
/* Loads key_path, extracts its Ed25519 seed into sign_seed, sets
* have_sign_seed=1 on success. Shared by build mode and --manifest mode
* (both accept an optional --sign-key <path>). Returns 0 on success,
* writes an ERROR to stderr and returns -1 on failure. */
static int load_sign_key(const char *key_path) {
FILE *kf = fopen(key_path, "rb");
if (!kf) {
fprintf(stderr, "mkcapsule: ERROR: cannot open key '%s'\n", key_path);
return -1;
}
fseek(kf, 0, SEEK_END);
long klen = ftell(kf);
fseek(kf, 0, SEEK_SET);
uint8_t *kbuf = malloc((size_t)klen);
if (!kbuf || fread(kbuf, 1, (size_t)klen, kf) != (size_t)klen) {
fprintf(stderr, "mkcapsule: ERROR: cannot read key '%s'\n", key_path);
fclose(kf);
free(kbuf);
return -1;
}
fclose(kf);
if (pkcs8_extract_ed25519_seed(kbuf, (size_t)klen, sign_seed) != 0) {
fprintf(stderr, "mkcapsule: ERROR: '%s' is not a valid PKCS#8 "
"Ed25519 private key\n", key_path);
free(kbuf);
return -1;
}
free(kbuf);
have_sign_seed = 1;
fprintf(stderr, "mkcapsule: signing capsules with key '%s'\n", key_path);
return 0;
}
int main(int argc, char **argv) {
/* --manifest mode: generate block ownership markdown */
/* --manifest mode: generate block ownership markdown. Optional
* --sign-key <keyfile> prefix: mkcapsule --manifest [--sign-key
* <keyfile>] <capsules_dir> [<out.md>] -- lets the manifest report
* real signing status without touching capsule_generated.c. */
if (argc >= 3 && strcmp(argv[1], "--manifest") == 0) {
base_dir = argv[2];
int a = 2;
if (argc >= 4 && strcmp(argv[2], "--sign-key") == 0) {
if (argc < 5) {
fprintf(stderr, "mkcapsule: ERROR: --manifest --sign-key "
"requires <keyfile> <capsules_dir>\n");
return 1;
}
if (load_sign_key(argv[3]) != 0) return 1;
a = 4;
}
base_dir = argv[a];
base_dir_len = strlen(base_dir);
while (base_dir_len > 0 && base_dir[base_dir_len - 1] == '/') base_dir_len--;
@@ -985,18 +1046,19 @@ int main(int argc, char **argv) {
}
FILE *mout = stdout;
if (argc == 4) {
mout = fopen(argv[3], "w");
int have_out = (argc == a + 2);
if (have_out) {
mout = fopen(argv[a + 1], "w");
if (!mout) {
fprintf(stderr, "mkcapsule: ERROR: cannot create '%s'\n", argv[3]);
fprintf(stderr, "mkcapsule: ERROR: cannot create '%s'\n", argv[a + 1]);
return 1;
}
}
generate_manifest(mout);
if (mout != stdout) {
if (have_out) {
fclose(mout);
fprintf(stderr, "mkcapsule: manifest written to %s (%d capsule(s))\n",
argv[3], manifest_count);
argv[a + 1], manifest_count);
}
return 0;
}
@@ -1026,32 +1088,7 @@ int main(int argc, char **argv) {
"<keyfile> <capsules_dir> <output.c>\n");
return 1;
}
const char *key_path = argv[2];
FILE *kf = fopen(key_path, "rb");
if (!kf) {
fprintf(stderr, "mkcapsule: ERROR: cannot open key '%s'\n", key_path);
return 1;
}
fseek(kf, 0, SEEK_END);
long klen = ftell(kf);
fseek(kf, 0, SEEK_SET);
uint8_t *kbuf = malloc((size_t)klen);
if (!kbuf || fread(kbuf, 1, (size_t)klen, kf) != (size_t)klen) {
fprintf(stderr, "mkcapsule: ERROR: cannot read key '%s'\n", key_path);
fclose(kf);
free(kbuf);
return 1;
}
fclose(kf);
if (pkcs8_extract_ed25519_seed(kbuf, (size_t)klen, sign_seed) != 0) {
fprintf(stderr, "mkcapsule: ERROR: '%s' is not a valid PKCS#8 "
"Ed25519 private key\n", key_path);
free(kbuf);
return 1;
}
free(kbuf);
have_sign_seed = 1;
fprintf(stderr, "mkcapsule: signing capsules with key '%s'\n", key_path);
if (load_sign_key(argv[2]) != 0) return 1;
arg0 = 3;
}
@@ -1061,8 +1098,8 @@ int main(int argc, char **argv) {
" %s [--sign-key <keyfile>] <capsules_dir> <output.c>\n"
" build capsule C source\n"
" %s --lint <capsules_dir> lint all .4th files\n"
" %s --manifest <capsules_dir> print block manifest (stdout)\n"
" %s --manifest <capsules_dir> <out.md> write block manifest to file\n"
" %s --manifest [--sign-key <keyfile>] <capsules_dir> [<out.md>]\n"
" print/write block manifest\n"
"\n"
"Lint checks (per .4th file):\n"
" - each block opens with 'Block N'\n"
@@ -1078,7 +1115,7 @@ int main(int argc, char **argv) {
"\n"
"Names are colon-separated relative paths (e.g. core:init.4th).\n"
"Files whose encoded name exceeds %d bytes are skipped with a warning.\n",
argv[0], argv[0], argv[0], argv[0], CAPSULE_NAME_MAX - 1);
argv[0], argv[0], argv[0], CAPSULE_NAME_MAX - 1);
return 1;
}