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:
Robert Allan James
2026-08-28 15:33:02 -04:00
co-authored by Claude Sonnet 5
parent ecbc8813d6
commit 6fd87923a5
9 changed files with 27874 additions and 9 deletions
+19 -2
View File
@@ -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;
+42 -1
View File
@@ -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);
}