First attempt shelled out to `openssl pkeyutl -sign` (fork/execlp, not system() -- avoided shell string interpolation of the key path). Corrected on request: no new external host binary dependency when the repo's own code can do the job -- same standing preference as the earlier anti-file correction. Rewritten to link ed25519_sign() (already verified against OpenSSL in Phase B) directly into mkcapsule. New tools/pkcs8_ed25519.c: a narrow DER walker (same shape as x509_ed25519.c, deliberately not shared -- small enough that duplicating a few TLV-walking lines beat threading a header between the kernel crypto tree and host tooling) extracting the raw seed from the intermediate's PKCS#8 private key, plus a minimal self-written base64 decoder (PEM is openssl genpkey's default output; no decoder existed anywhere in the repo). Verified end-to-end before wiring anything in: the extracted seed's derived pubkey matches the cert's exactly, and a full self-contained sign+verify round-trip (zero openssl) passes. CapsuleDesc had no spare bytes, so signatures live in a new parallel CapsuleSigEntry array, emitted by a new `mkcapsule --sign-key <path>` flag (omitted/missing key -> has_sig=0 everywhere, graceful, not a build failure -- CI has no access to the offline key). New capsule_sig.c/.h: capsule_verify_signature(), a separate function, not folded into the already-tested capsule_validate(). Finds and caches the embedded intermediate cert's pubkey once per boot, then verifies against it. Wired into all three capsule_validate() call sites in capsule_birth.c via log_message(LOG_WARN, ...) -- never refuses yet, per the earlier staged-rollout decision. Verified independently, both directions, live in the real kernel: a full clean build (38 signed capsules) boots clean on all three architectures with zero warnings. Separately, hand-corrupted one byte of Mama's own init.4th capsule's stored signature (not its payload/hash, which capsule_validate() already catches and would have masked the test) and rebuilt just the changed object: produced exactly "capsule sig: init.4th: INVALID -- signature does not verify" on boot, and the kernel still reached ok> -- proving warn-only doesn't refuse anything yet. Reverted before the final, untampered 3-arch acceptance pass. Still open: flipping WARN to hard-refuse (separate, deliberate step) and the BLOCK_MAP.md signature-status column. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
64 lines
2.2 KiB
C
64 lines
2.2 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
Licensed under the StarForth License, Version 1.0
|
||
*/
|
||
|
||
/**
|
||
* capsule_generated.h - Declarations for mkcapsule-generated capsule store.
|
||
*
|
||
* The matching capsule_generated.c is produced at build time by mkcapsule.
|
||
* These three arrays plus the directory header are compiled into .rodata.
|
||
*/
|
||
|
||
#ifndef STARKERNEL_CAPSULE_GENERATED_H
|
||
#define STARKERNEL_CAPSULE_GENERATED_H
|
||
|
||
#include <stdint.h>
|
||
#include "starkernel/capsule.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* Mark as hidden so GCC uses direct RIP-relative addressing in PIC/PE builds,
|
||
* avoiding GOT references which do not exist in PE/COFF binaries. */
|
||
__attribute__((visibility("hidden")))
|
||
extern const uint8_t capsule_arena[];
|
||
__attribute__((visibility("hidden")))
|
||
extern const CapsuleDesc capsule_descriptors[];
|
||
__attribute__((visibility("hidden")))
|
||
extern const CapsuleNameEntry capsule_names[];
|
||
__attribute__((visibility("hidden")))
|
||
extern const CapsuleSigEntry capsule_signatures[];
|
||
__attribute__((visibility("hidden")))
|
||
extern const CapsuleDirHeader capsule_directory;
|
||
|
||
/*
|
||
* Accessor functions — defined in the same TU as the symbols above.
|
||
* PE/COFF builds with -fPIC do not convert GOTPCREL data references to
|
||
* direct LEA the way the ELF linker does, so any cross-TU access to a
|
||
* data symbol goes through GOT and reads garbage in a PE image.
|
||
* These hidden accessor functions are called via a direct CALL (no PLT/GOT)
|
||
* and access the symbols with direct RIP-relative addressing inside their TU.
|
||
*/
|
||
__attribute__((visibility("hidden")))
|
||
uint32_t capsule_get_desc_count(void);
|
||
__attribute__((visibility("hidden")))
|
||
const CapsuleDirHeader *capsule_get_directory(void);
|
||
__attribute__((visibility("hidden")))
|
||
const CapsuleDesc *capsule_get_descriptors(void);
|
||
__attribute__((visibility("hidden")))
|
||
const CapsuleNameEntry *capsule_get_names(void);
|
||
__attribute__((visibility("hidden")))
|
||
const CapsuleSigEntry *capsule_get_signatures(void);
|
||
__attribute__((visibility("hidden")))
|
||
const uint8_t *capsule_get_arena(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* STARKERNEL_CAPSULE_GENERATED_H */
|