MINT: parameterize with full name, username, email, phone
Extends user_identity_seed_t (version 2) with fixed-size full_name/ username/email/phone fields -- plenty of unused pad space (4016 bytes) was already there. Deliberately NOT encoded into the DER cert's Subject field: that would mean building a real X.509 RDNSequence (OIDs for commonName/emailAddress, PrintableString/UTF8String tagging), well past this project's own stated "deliberately not a general ASN.1/X.509 [builder]" scope. This human-readable profile data isn't security-relevant the way pubkey/serial are (the only two fields CERTVERIFY/BINDSTEP actually check) -- it travels alongside the keypair in the plain identity record instead. capsule_mint_identity() takes full_name/username (required, validated non-empty and within their fixed field widths) and email/phone (NULL or empty = null, matching the schema's own nullable convention). The MINT FORTH word's stack signature grows to 4 string pairs ( fname-c fname-u uname-c uname-u email-c email-u phone-c phone-u -- ok? ). Verified live in QEMU: minted two real identities with real profile data -- Zuse (full_name "Konrad Suse", username "Zuse", zuse@pantheon.org) onto disk/zuse.img, and a regular user (full_name "Captain Bob", username "CaptBob", capt.bob@pantheon.org) onto disk/user1.img -- then read the raw devblock bytes back off both images directly and confirmed every field byte-exact at its correct struct offset. Clean 3-architecture regression boot confirms no side effects. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ecbc8813d6
commit
6fd87923a5
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-28T18:43:46Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-28T19:31:45Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -39,6 +39,10 @@ typedef enum {
|
||||
* 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). */
|
||||
} MintResult;
|
||||
|
||||
/**
|
||||
@@ -54,9 +58,16 @@ typedef enum {
|
||||
* @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).
|
||||
* @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).
|
||||
* @return MINT_OK on success, an error code otherwise.
|
||||
*/
|
||||
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm);
|
||||
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
|
||||
const char *full_name, const char *username,
|
||||
const char *email, const char *phone);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* user_identity_seed.h -- on-disk record format for a minted user
|
||||
* identity's own keypair (FABRIC-3.md §F.8, decision 2), stored in the
|
||||
* first devblock of a home-blocks drive's identity_src region
|
||||
* identity's own keypair and profile (FABRIC-3.md §F.8/§F.20), stored in
|
||||
* the first devblock of a home-blocks drive's identity_src region
|
||||
* (homeblocks_sig_t.identity_src_offset). The devblocks that follow it
|
||||
* (identity_src_offset+1 .. identity_src_offset+identity_src_devblocks-1)
|
||||
* hold this identity's own raw FORTH personality/init source, read by
|
||||
@@ -14,6 +14,17 @@
|
||||
* own header" convention (Zuse's own record has no analogous public
|
||||
* cert field; a regular user's does, stored separately in the cert
|
||||
* region MINT also writes -- see homeblocks_sig_t.cert_offset).
|
||||
*
|
||||
* full_name/username/email/phone (added §F.20, 2026-08-28): deliberately
|
||||
* NOT encoded into the DER cert's Subject field -- that would mean
|
||||
* building a real X.509 RDNSequence (AttributeTypeAndValue, OIDs for
|
||||
* commonName/emailAddress, PrintableString/UTF8String tagging), well
|
||||
* past this project's own stated "deliberately NOT a general ASN.1/X.509
|
||||
* parser [or builder]" scope (x509_ed25519.h). This human-readable
|
||||
* profile data isn't security-relevant the way pubkey/serial are (those
|
||||
* two alone are what CERTVERIFY/BINDSTEP actually check) -- it travels
|
||||
* alongside the keypair in this plain record instead. email/phone are
|
||||
* nullable (empty string, first byte 0x00); full_name/username are not.
|
||||
*/
|
||||
#ifndef STARKERNEL_USER_IDENTITY_SEED_H
|
||||
#define STARKERNEL_USER_IDENTITY_SEED_H
|
||||
@@ -23,7 +34,12 @@
|
||||
#define USER_IDENTITY_SEED_MAGIC \
|
||||
((uint32_t)'U' | ((uint32_t)'I' << 8) | ((uint32_t)'D' << 16) | ((uint32_t)'S' << 24))
|
||||
|
||||
#define USER_IDENTITY_SEED_VERSION 1u
|
||||
#define USER_IDENTITY_SEED_VERSION 2u
|
||||
|
||||
#define USER_IDENTITY_FULL_NAME_MAX 64u
|
||||
#define USER_IDENTITY_USERNAME_MAX 32u
|
||||
#define USER_IDENTITY_EMAIL_MAX 64u
|
||||
#define USER_IDENTITY_PHONE_MAX 24u
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* USER_IDENTITY_SEED_MAGIC; anything else means
|
||||
@@ -37,10 +53,16 @@ typedef struct {
|
||||
uint8_t pubkey[32]; /* Ed25519 public key derived from seed at mint
|
||||
* time -- same value the cert region's
|
||||
* SubjectPublicKeyInfo holds. */
|
||||
char full_name[USER_IDENTITY_FULL_NAME_MAX]; /* NUL-terminated, required. */
|
||||
char username[USER_IDENTITY_USERNAME_MAX]; /* NUL-terminated, required. */
|
||||
char email[USER_IDENTITY_EMAIL_MAX]; /* NUL-terminated; empty = null. */
|
||||
char phone[USER_IDENTITY_PHONE_MAX]; /* NUL-terminated; empty = null. */
|
||||
uint64_t crc; /* CRC-64/ISO (block_subsystem.h's compute_crc64())
|
||||
* over every byte of this struct up to (not
|
||||
* including) this field. */
|
||||
uint8_t _pad[4096 - (4 + 4 + 32 + 32 + 8)];
|
||||
uint8_t _pad[4096 - (4 + 4 + 32 + 32 +
|
||||
USER_IDENTITY_FULL_NAME_MAX + USER_IDENTITY_USERNAME_MAX +
|
||||
USER_IDENTITY_EMAIL_MAX + USER_IDENTITY_PHONE_MAX + 8)];
|
||||
} user_identity_seed_t;
|
||||
|
||||
typedef char user_identity_seed_size_check[
|
||||
|
||||
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
@@ -56,8 +56,21 @@ static int write_devblock(struct blkio_dev *dev, uint32_t devblock,
|
||||
return 0;
|
||||
}
|
||||
|
||||
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm) {
|
||||
if (!dev || !issuer_vm) return MINT_ERR_WRITE_FAIL;
|
||||
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
|
||||
const char *full_name, const char *username,
|
||||
const char *email, const char *phone) {
|
||||
if (!dev || !issuer_vm || !full_name || !username) return MINT_ERR_WRITE_FAIL;
|
||||
|
||||
/* full_name/username required and must fit; email/phone may be NULL
|
||||
* (treated as empty/null) but must fit if given. */
|
||||
if (full_name[0] == '\0' || strlen(full_name) >= USER_IDENTITY_FULL_NAME_MAX)
|
||||
return MINT_ERR_INVALID_PROFILE;
|
||||
if (username[0] == '\0' || strlen(username) >= USER_IDENTITY_USERNAME_MAX)
|
||||
return MINT_ERR_INVALID_PROFILE;
|
||||
if (email && strlen(email) >= USER_IDENTITY_EMAIL_MAX)
|
||||
return MINT_ERR_INVALID_PROFILE;
|
||||
if (phone && strlen(phone) >= USER_IDENTITY_PHONE_MAX)
|
||||
return MINT_ERR_INVALID_PROFILE;
|
||||
|
||||
/* Refuse to overwrite an already-recognized home-blocks drive --
|
||||
* mirrors WRITE(10)'s own refuse-on-non-blank-media posture (§F.8,
|
||||
@@ -105,6 +118,10 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm) {
|
||||
idrec.version = USER_IDENTITY_SEED_VERSION;
|
||||
memcpy(idrec.seed, seed, 32);
|
||||
memcpy(idrec.pubkey, pubkey, 32);
|
||||
memcpy(idrec.full_name, full_name, strlen(full_name));
|
||||
memcpy(idrec.username, username, strlen(username));
|
||||
if (email) memcpy(idrec.email, email, strlen(email));
|
||||
if (phone) memcpy(idrec.phone, phone, strlen(phone));
|
||||
idrec.crc = compute_crc64((const uint8_t *)&idrec, offsetof(user_identity_seed_t, crc));
|
||||
if (write_devblock(dev, MINT_IDENTITY_SRC_OFFSET, (const uint8_t *)&idrec) != 0)
|
||||
return MINT_ERR_WRITE_FAIL;
|
||||
|
||||
@@ -37,10 +37,12 @@
|
||||
#ifdef __STARKERNEL__
|
||||
|
||||
#include "platform_alloc.h"
|
||||
#include <string.h>
|
||||
#include "starkernel/capsule.h"
|
||||
#include "starkernel/capsule_birth.h"
|
||||
#include "starkernel/capsule_runcap.h"
|
||||
#include "starkernel/capsule_mint.h"
|
||||
#include "starkernel/user_identity_seed.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
#include "starkernel/capsule_run.h"
|
||||
#include "starkernel/capsule_loader.h"
|
||||
@@ -784,8 +786,44 @@ static void mama_word_vm_call(VM *vm)
|
||||
* no entropy source. Real, working code -- not the eventual Console
|
||||
* onboarding flow (§D.6), which will call this same C function.
|
||||
*/
|
||||
/* Pop one ( caddr u ) string pair and copy it, NUL-terminated, into
|
||||
* dst (capacity dst_cap). Returns 0 on success, -1 on underflow/bounds/
|
||||
* unmapped-address failure (vm->error is set in that case). */
|
||||
static int mint_pop_string(VM *vm, char *dst, size_t dst_cap)
|
||||
{
|
||||
if (vm->dsp < 1) { vm->error = 1; return -1; }
|
||||
cell_t u = vm_pop(vm);
|
||||
cell_t caddr = vm_pop(vm);
|
||||
if (u < 0 || (size_t)u >= dst_cap) { vm->error = 1; return -1; }
|
||||
if (u > 0) {
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!p) { vm->error = 1; return -1; }
|
||||
memcpy(dst, p, (size_t)u);
|
||||
}
|
||||
dst[u] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MINT ( fname-c fname-u uname-c uname-u email-c email-u phone-c phone-u -- ok? )
|
||||
* Mint a fresh identity onto the currently attached USB drive, with a
|
||||
* real human profile (FABRIC-3.md §F.20). full_name/username required
|
||||
* and non-empty; pass a zero-length string (S" ") for email/phone to
|
||||
* leave them null.
|
||||
*/
|
||||
static void mama_word_mint(VM *vm)
|
||||
{
|
||||
char phone[USER_IDENTITY_PHONE_MAX];
|
||||
char email[USER_IDENTITY_EMAIL_MAX];
|
||||
char username[USER_IDENTITY_USERNAME_MAX];
|
||||
char full_name[USER_IDENTITY_FULL_NAME_MAX];
|
||||
|
||||
/* Stack order: fname pushed first, phone last -- pop in reverse. */
|
||||
if (mint_pop_string(vm, phone, sizeof(phone)) != 0) return;
|
||||
if (mint_pop_string(vm, email, sizeof(email)) != 0) return;
|
||||
if (mint_pop_string(vm, username, sizeof(username)) != 0) return;
|
||||
if (mint_pop_string(vm, full_name, sizeof(full_name)) != 0) return;
|
||||
|
||||
struct blkio_dev *dev = sk_repl_get_attached_blk_dev();
|
||||
if (!dev) {
|
||||
console_println("MINT: no drive attached");
|
||||
@@ -793,7 +831,7 @@ static void mama_word_mint(VM *vm)
|
||||
return;
|
||||
}
|
||||
|
||||
MintResult r = capsule_mint_identity(dev, vm);
|
||||
MintResult r = capsule_mint_identity(dev, vm, full_name, username, email, phone);
|
||||
switch (r) {
|
||||
case MINT_OK:
|
||||
console_println("MINT: identity minted");
|
||||
@@ -814,6 +852,9 @@ static void mama_word_mint(VM *vm)
|
||||
case MINT_ERR_WRITE_FAIL:
|
||||
console_println("MINT: FAILED -- devblock write error (drive may be partially minted)");
|
||||
break;
|
||||
case MINT_ERR_INVALID_PROFILE:
|
||||
console_println("MINT: refused -- full_name/username missing or a field too long");
|
||||
break;
|
||||
}
|
||||
vm_push(vm, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user