Implement CERTVERIFY -- real DER cert verification, tested against OpenSSL
Phase B of the identity pipeline (FABRIC-3.md §F.7/§F.17): - x509_ed25519.c/.h: two new DER walkers alongside the existing pubkey extractor -- x509_verify_signature() (verifies a cert's outer Ed25519 signature over the raw, exactly-as-encoded tbsCertificate bytes, real signature verification against issuer_pubkey, rejects non-Ed25519 signatureAlgorithm) and x509_extract_serial() (extracts the serialNumber INTEGER, stripping a DER padding byte if present, for the drive_uuid binding decided in §F.7). - vm_identity.c: vm_identity_from_cert(), ties the three DER primitives together into the actual CERTVERIFY check -- signature verifies against issuer_pubkey, serialNumber matches this drive's own drive_uuid, subject pubkey extracts cleanly -- and populates a VMIdentity on success. acl_caps is caller-supplied, not read from the cert (nothing in the decided cert fields encodes capabilities); deciding what a verified identity is allowed to do is policy for the caller (WIREBIND, not yet built), not this function's job. Verified two ways: a standalone host-side test harness (not part of the kernel build) links the real source files against a real openssl- generated Ed25519 X.509 cert -- extracted pubkey, extracted serial, and signature verification all match ground truth, plus two negative tests (wrong issuer pubkey, corrupted signature) both correctly rejected. Then the actual kernel build verified live on all three architectures: clean compile, clean boot to ok>, Hermes/Artemis both live with no KILL. Same pre-existing, unrelated Zuse fence-write anomaly observed on all three (not caused by this change, not chased here). 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
c07184e984
commit
0ec91b517a
@@ -49,6 +49,7 @@
|
||||
#ifdef __STARKERNEL__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t owner_pubkey[32]; /**< Ed25519 public key of this VM's owning
|
||||
@@ -85,6 +86,40 @@ typedef struct {
|
||||
*/
|
||||
int vm_identity_has_cap(const VMIdentity *id, uint32_t cap);
|
||||
|
||||
/**
|
||||
* vm_identity_from_cert - CERTVERIFY (FABRIC-3.md §F.7/§F.17): verify a
|
||||
* DER-encoded, Zuse-signed X.509 cert and populate a VMIdentity from it.
|
||||
*
|
||||
* Three checks, all must pass: the cert's own signature verifies against
|
||||
* issuer_pubkey (Zuse's own on-device key -- a separate trust root from
|
||||
* the capsule-PKI chain, no chain walk needed); the cert's serialNumber
|
||||
* equals drive_uuid byte-for-byte (binds this cert to one physical drive,
|
||||
* §F.7 decision 2 -- a copied cert on different media will not verify);
|
||||
* the subject's Ed25519 public key extracts cleanly.
|
||||
*
|
||||
* acl_caps is *not* read from the cert -- nothing in the decided cert
|
||||
* fields encodes capabilities (§F.16). It's supplied by the caller, whose
|
||||
* job it is to decide what this verified identity is allowed to do (e.g.
|
||||
* comparing the extracted pubkey against Zuse's own system-resident
|
||||
* pubkey to decide VM_IDENTITY_CAP_ALL vs. a lesser default) -- that
|
||||
* policy decision doesn't belong inside a pure verification function.
|
||||
*
|
||||
* @param out Populated on success; left untouched on failure.
|
||||
* @param der DER-encoded certificate bytes.
|
||||
* @param der_len Their length.
|
||||
* @param issuer_pubkey Zuse's own Ed25519 public key.
|
||||
* @param drive_uuid This physical drive's own 16-byte drive_uuid
|
||||
* (homeblocks_sig_t), compared against the cert's
|
||||
* serialNumber.
|
||||
* @param acl_caps Capability bitmask to install, caller-decided.
|
||||
* @return 0 on success, -1 if any check fails (malformed DER, wrong
|
||||
* signature algorithm, signature doesn't verify, serial mismatch,
|
||||
* or the extracted key isn't a valid Ed25519 point).
|
||||
*/
|
||||
int vm_identity_from_cert(VMIdentity *out, const uint8_t *der, size_t der_len,
|
||||
const uint8_t issuer_pubkey[32],
|
||||
const uint8_t drive_uuid[16], uint32_t acl_caps);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_VM_IDENTITY_H */
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
/*
|
||||
* x509_ed25519.h -- extract a raw Ed25519 public key from a DER-encoded
|
||||
* X.509 certificate's SubjectPublicKeyInfo (RFC 8410).
|
||||
* x509_ed25519.h -- minimal, targeted DER walkers for Ed25519-signed X.509
|
||||
* certificates (RFC 8410). Deliberately NOT a general ASN.1/X.509 parser
|
||||
* (Milestone 6 decision, FABRIC-2.md): each function walks exactly as far
|
||||
* into the DER structure as its own job needs, nothing more.
|
||||
*
|
||||
* Deliberately NOT a general ASN.1/X.509 parser (Milestone 6 decision,
|
||||
* FABRIC-2.md): walks exactly as far into the DER structure as needed
|
||||
* to reach SubjectPublicKeyInfo, verifies its AlgorithmIdentifier OID is
|
||||
* Ed25519 (1.3.101.112) and rejects anything else, then returns the raw
|
||||
* 32-byte key from the trailing BIT STRING. No signature verification,
|
||||
* no chain validation, no extension parsing -- this only answers "what
|
||||
* public key does this cert claim to hold," matching exactly what
|
||||
* ed25519_verify() needs as input.
|
||||
* x509_extract_ed25519_pubkey() only ever reads SubjectPublicKeyInfo --
|
||||
* no signature verification, no chain validation, no extension parsing.
|
||||
* That's the capsule-PKI use case (Milestone 6): the embedded snakeoil
|
||||
* intermediate cert is trusted because it's baked into the trusted build,
|
||||
* never re-verified against the offline root CA at boot.
|
||||
*
|
||||
* x509_verify_signature()/x509_extract_serial() (added 2026-08-28,
|
||||
* FABRIC-3.md §F.7/§F.17) are for CERTVERIFY -- a regular user's cert,
|
||||
* which unlike the capsule-PKI chain is signed by Zuse's own on-device
|
||||
* key and genuinely needs its signature checked at attach time, not just
|
||||
* trusted by embedding. Two separate trust roots, two separate reasons
|
||||
* to exist in the same small file (shared DER-walking internals only).
|
||||
*
|
||||
* Freestanding C99, no libc beyond memcmp/memcpy (already provided by
|
||||
* src/starkernel/vm/host/shim.c in the kernel build).
|
||||
@@ -27,4 +33,32 @@
|
||||
int x509_extract_ed25519_pubkey(const uint8_t *der, size_t der_len,
|
||||
uint8_t pubkey_out[32]);
|
||||
|
||||
/* Verify a DER-encoded certificate's own outer Ed25519 signature (the
|
||||
* signatureValue field) was produced by issuer_pubkey signing the raw,
|
||||
* exactly-as-encoded tbsCertificate bytes (DER signs the octets, not a
|
||||
* re-derived hash of "the fields" -- the TLV framing is part of what's
|
||||
* signed). Rejects a non-Ed25519 signatureAlgorithm rather than guessing.
|
||||
*
|
||||
* Returns 0 if the signature verifies, -1 on any malformed encoding,
|
||||
* unexpected structure, non-Ed25519 signature algorithm, or a signature
|
||||
* that does not verify. Never faults on malformed input. */
|
||||
int x509_verify_signature(const uint8_t *der, size_t der_len,
|
||||
const uint8_t issuer_pubkey[32]);
|
||||
|
||||
/* Extract the raw serialNumber INTEGER content bytes from a DER-encoded
|
||||
* certificate's tbsCertificate. A single leading 0x00 pad byte (DER adds
|
||||
* one when the value's high bit would otherwise read as a negative
|
||||
* INTEGER) is stripped before copying, so a 16-byte drive_uuid compares
|
||||
* byte-for-byte regardless of whether DER happened to pad it.
|
||||
*
|
||||
* @param serial_out Caller-provided buffer.
|
||||
* @param serial_out_cap Its size in bytes; returns -1 if the real
|
||||
* (pad-stripped) serial is larger than this.
|
||||
* @param serial_len_out Set to the real length actually copied.
|
||||
* @return 0 on success, -1 on any malformed encoding or unexpected
|
||||
* structure. Never faults on malformed input. */
|
||||
int x509_extract_serial(const uint8_t *der, size_t der_len,
|
||||
uint8_t *serial_out, size_t serial_out_cap,
|
||||
size_t *serial_len_out);
|
||||
|
||||
#endif /* STARKERNEL_X509_ED25519_H */
|
||||
|
||||
Reference in New Issue
Block a user