Files
LithosAnanake/include/starkernel/capsule_mint.h
T
Robert Allan JamesandClaude Sonnet 5 63864c4b01
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Punch list item 1: scratch-device MINT-SCRATCH, verified live on all 3 arches
Implements FABRIC-3.md §XXXII.2's scratch-thumbdrive mint mechanism:
capsule_mint_identity_scratch() (capsule_mint.c/.h) builds a throwaway
RAM-backed blkio_dev via blkio_ram.c's backend and runs
capsule_mint_identity() against it completely unmodified -- same live
Zuse-signing operation, same rng_get_bytes() draw for drive_uuid a real
thumbdrive gets. Reads back only drive_uuid + the cert devblock; the
seed devblock is written into the scratch buffer internally but never
read out (no seed is ever baked into a capsule, per the ratified
no-seed decision).

New FORTH word MINT-SCRATCH (mama_forth_words.c), same stack signature
as MINT, mints into the scratch device instead of any attached drive
and never touches sk_repl_get_attached_blk_dev() or console-pairing
state. Prints the drive_uuid as hex so a live boot log itself proves
each call drew fresh entropy.

Build correction found along the way: blkio_ram.c was excluded from
the kernel build (Makefile.starkernel VM_EXCLUDE) alongside
blkio_factory.c/blkio_file.c. blkio_factory_open() unconditionally
references blkio_file.c's real fopen()/fread() file I/O, which has no
freestanding-kernel equivalent, so the factory function couldn't be
used as-is. blkio_ram.c itself is pure memcpy over a caller buffer --
pulled it alone into the kernel build and wired it directly in
capsule_mint.c, the same way blkio_factory.c's own extern declarations
do internally.

Verified live on amd64: two MINT-SCRATCH calls produced two genuinely
different drive_uuids (b533246d.../ae11b2b2...), confirming fresh
entropy per call rather than stale reuse; VM stayed healthy afterward
(5 6 + . -> 11). Clean 3-arch qemu boot (amd64/aarch64/riscv64),
logs and DoE CSVs committed per standing convention.

Remaining punch-list items (hand-transcription into a .4th block, the
unattended-birth call site, the ACL cap bit) not started.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
2026-09-16 04:42:15 -04:00

170 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* capsule_mint.h - MINT: mint a fresh identity onto a blank thumbdrive
* (FABRIC-2.md §F.8/§F.19), the last piece of the original Tripod
* vision. Writes a real keypair, a Zuse-signed cert, and a minimal
* working default personality -- everything RUNCAP (capsule_runcap.h)
* and CERTVERIFY need at a later attach.
*/
#ifndef STARKERNEL_CAPSULE_MINT_H
#define STARKERNEL_CAPSULE_MINT_H
#ifdef __STARKERNEL__
#include <stdint.h>
#include "vm.h"
struct blkio_dev;
typedef enum {
MINT_OK = 0,
MINT_ERR_ALREADY_MINTED, /* dev already reads as a recognized home-blocks
* drive -- refuses rather than overwrite,
* mirroring WRITE(10)'s own blank-media
* posture (§F.8, decided 2026-08-28). */
MINT_ERR_NO_ZUSE_CERT, /* issuer_vm->zuse_cert_installed is 0 -- no
* key to sign the new cert with. */
MINT_ERR_NO_ENTROPY, /* virtio_rng not ready. */
MINT_ERR_CERT_BUILD, /* x509_build_user_cert() failed (shouldn't
* happen with fixed-size fields, but not
* assumed away). */
MINT_ERR_WRITE_FAIL, /* a devblock write failed partway through --
* the drive may be left partially minted. */
MINT_ERR_INVALID_PROFILE, /* full_name/username missing or too long for
* user_identity_seed_t's fixed fields, or
* email/phone too long (both may be NULL/empty
* -- that's "null", not invalid). */
MINT_ERR_VERIFY_FAILED, /* every devblock write reported success, but a
* post-write read-back (2026-09-06) found the
* drive doesn't actually read back as a valid,
* complete home-blocks identity -- caught
* live: a device that enumerates and accepts
* writes can still fail to read back correctly
* under real hardware/emulation conditions
* (e.g. concurrent multi-device USB load), and
* blkio_write() returning BLK_OK is not by
* itself proof the bytes landed. The drive may
* be left partially or incorrectly minted --
* treat identically to MINT_ERR_WRITE_FAIL for
* retry purposes. */
} MintResult;
/**
* MintPersonality - which personality-source template gets written to the
* new identity's devblock (identity_src_offset+1). Purely a template
* *selection* -- the actual restriction logic (the FORTH-79/83 allowlist,
* the walk-and-deny loop) lives entirely in capsules/acl-std79.4th, per
* the standing rule that ACL policy belongs in FORTH, never in C. This
* enum just picks which few-line bootstrap stub gets written; that stub
* is the only thing capsule_mint.c itself owns.
*/
typedef enum {
MINT_PERSONALITY_DEFAULT = 0, /* unrestricted -- today's only behavior until this enum existed */
MINT_PERSONALITY_STD79_LOCKDOWN = 1 /* EXECs acl-std79.4th then ACL-LOCKDOWN-STD79 as its last steps */
} MintPersonality;
/**
* capsule_mint_identity - Mint a fresh identity onto dev.
*
* Layout written (devblock offsets from dev's own start; devblock 0 is
* left alone, reserved for the block-subsystem's own generic header):
* devblock 1 homeblocks_sig_t (HOMEBLOCKS_SIG_START_FBLOCK)
* devblock 2 DER cert, Zuse-signed (cert_offset)
* devblock 3 user_identity_seed_t (identity_src_offset)
* devblock 4 personality source (selected by `personality`)
* (identity_src_offset+1)
*
* @param dev Already-open block device for the target drive.
* @param issuer_vm The signing identity -- in practice always Hera's own
* VM (Zuse's cert lives there, vm.h's zuse_cert_seed).
* NULL means genesis mode (§F.21): no cert is built or
* written (cert_offset/cert_devblocks stay 0) and
* issuer_vm->zuse_cert_installed is never checked --
* used exactly once, to mint Zuse's own root identity,
* which by definition has no existing Zuse to sign it.
* @param full_name Required, NUL-terminated, fits user_identity_seed_t's
* full_name field (§F.20).
* @param username Required, NUL-terminated, fits its username field.
* @param email NULL or empty string = null (field stays empty).
* @param phone NULL or empty string = null (field stays empty).
* @param out_pubkey Optional (may be NULL): filled with the newly
* generated identity's own Ed25519 public key on
* success. Genesis mode's only caller needs this, to
* write it into the system-resident zuse_genesis_
* marker_t.
* @param out_seed Optional (may be NULL): filled with the newly
* generated identity's own Ed25519 seed on success.
* Genesis mode's only caller needs this too, to
* install the cert into Hera's own VM immediately
* (vm_zuse_cert_install()) -- the seed otherwise only
* ever lives on the minted thumbdrive.
* @param personality Which personality-source template to write -- see
* MintPersonality's own doc comment above.
* @param drive_known_blank Pass 1 when the caller has *already* just run
* homeblocks_sig_check() on dev and confirmed
* HOMEBLOCKS_SIG_BLANK (e.g. capsule_zuse_boot_try_
* attach(), which must check sig_rc before it can even
* decide to call this) -- skips this function's own
* internal "refuse to overwrite" re-check, which
* otherwise repeats the exact same full BOT read
* sequence a second time for no reason (found live,
* FABRIC-2.md §F.25/§F.26: the redundant check was
* mistaken for a hang before the real cause -- leaked
* `tail -f` processes from repeated hard kills during
* the same debugging session -- was found). Pass 0 from
* any caller (like MINT, mama_forth_words.c) that has
* not already checked -- the safety check still applies
* there.
* @return MINT_OK on success, an error code otherwise.
*/
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
const char *full_name, const char *username,
const char *email, const char *phone,
uint8_t out_pubkey[32], uint8_t out_seed[32],
MintPersonality personality,
int drive_known_blank);
/**
* capsule_mint_identity_scratch - Mint into a throwaway RAM-backed device
* instead of a real thumbdrive (FABRIC-3.md §XXXII.2, unattended-identity
* punch list item 1). Runs capsule_mint_identity() completely unmodified
* against a scratch blkio_dev built from the shared RAM backend
* (blkio_ram.c) -- same live Zuse-signing operation, same rng_get_bytes()
* draw for drive_uuid a real thumbdrive gets. "Scratch" describes only
* where the bytes are written; nothing about verification changes, and
* vm_identity_from_cert() needs no changes to consume the result later.
*
* The seed devblock capsule_mint_identity() writes internally is never
* read back here -- out_uuid/out_cert are the only two fields an
* unattended identity needs (FABRIC-3.md §XXXII.2 decision, 2026-09-16:
* no seed is ever baked into a capsule).
*
* @param issuer_vm Same meaning as capsule_mint_identity()'s own --
* Zuse's cert lives here (in practice Hera).
* @param out_uuid 16 bytes, populated with the minted drive_uuid.
* @param out_cert 4096 bytes, populated with the raw (zero-padded)
* cert devblock -- matches capsule_wirebind_verify_
* cert()'s own read shape, so the same DER-length
* handling (parsed from the ASN.1 header, trailing
* padding ignored) applies at birth time later.
* @return Same MintResult capsule_mint_identity() itself returns.
*/
MintResult capsule_mint_identity_scratch(VM *issuer_vm,
const char *full_name, const char *username,
const char *email, const char *phone,
MintPersonality personality,
uint8_t out_uuid[16], uint8_t out_cert[4096]);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_MINT_H */