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
@@ -12,8 +12,32 @@
|
||||
#endif
|
||||
|
||||
#include "starkernel/vm_identity.h"
|
||||
#include "starkernel/x509_ed25519.h"
|
||||
#include <string.h>
|
||||
|
||||
int vm_identity_has_cap(const VMIdentity *id, uint32_t cap) {
|
||||
if (!id || !id->installed) return 0;
|
||||
return (id->acl_caps & cap) == cap;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!out || !der || !issuer_pubkey || !drive_uuid) return -1;
|
||||
|
||||
if (x509_verify_signature(der, der_len, issuer_pubkey) != 0) return -1;
|
||||
|
||||
uint8_t serial[32];
|
||||
size_t serial_len = 0;
|
||||
if (x509_extract_serial(der, der_len, serial, sizeof(serial), &serial_len) != 0)
|
||||
return -1;
|
||||
if (serial_len != 16 || memcmp(serial, drive_uuid, 16) != 0) return -1;
|
||||
|
||||
uint8_t owner_pubkey[32];
|
||||
if (x509_extract_ed25519_pubkey(der, der_len, owner_pubkey) != 0) return -1;
|
||||
|
||||
memcpy(out->owner_pubkey, owner_pubkey, 32);
|
||||
out->installed = 1;
|
||||
out->acl_caps = acl_caps;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* x509_ed25519.c -- see x509_ed25519.h. */
|
||||
#include "starkernel/x509_ed25519.h"
|
||||
#include "starkernel/ed25519.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -122,3 +123,99 @@ int x509_extract_ed25519_pubkey(const uint8_t *der, size_t der_len,
|
||||
memcpy(pubkey_out, bitstr.p + 1, 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x509_verify_signature(const uint8_t *der, size_t der_len,
|
||||
const uint8_t issuer_pubkey[32]) {
|
||||
if (!der || !issuer_pubkey) return -1;
|
||||
|
||||
const uint8_t *cur = der;
|
||||
const uint8_t *end = der + der_len;
|
||||
uint8_t tag;
|
||||
der_span_t cert;
|
||||
|
||||
/* Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue } */
|
||||
if (der_next(&cur, end, &tag, &cert) != 0 || tag != 0x30) return -1;
|
||||
|
||||
/* tbsCertificate's own encoded bytes -- tag+length+value, the exact
|
||||
* octets Ed25519 signs, not just der_next()'s value-only span. cert.p
|
||||
* is where tbsCertificate's own tag byte starts; der_next() below
|
||||
* advances cur2 past its whole TLV. */
|
||||
const uint8_t *cur2 = cert.p;
|
||||
const uint8_t *limit2 = cert.p + cert.len;
|
||||
const uint8_t *tbs_raw = cur2;
|
||||
der_span_t tbs;
|
||||
if (der_next(&cur2, limit2, &tag, &tbs) != 0 || tag != 0x30) return -1;
|
||||
size_t tbs_raw_len = (size_t)(cur2 - tbs_raw);
|
||||
|
||||
/* signatureAlgorithm ::= AlgorithmIdentifier -- must be Ed25519, same
|
||||
* OID check as SubjectPublicKeyInfo's own algorithm above. */
|
||||
der_span_t sigalg;
|
||||
if (der_next(&cur2, limit2, &tag, &sigalg) != 0 || tag != 0x30) return -1;
|
||||
{
|
||||
const uint8_t *a_cur = sigalg.p;
|
||||
const uint8_t *a_limit = sigalg.p + sigalg.len;
|
||||
uint8_t atag;
|
||||
der_span_t oid;
|
||||
if (der_next(&a_cur, a_limit, &atag, &oid) != 0 || atag != 0x06) return -1;
|
||||
static const uint8_t ED25519_OID[3] = { 0x2B, 0x65, 0x70 };
|
||||
if (oid.len != sizeof(ED25519_OID) ||
|
||||
memcmp(oid.p, ED25519_OID, sizeof(ED25519_OID)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* signatureValue ::= BIT STRING -- 1 unused-bits byte (must be 0)
|
||||
* followed by the raw 64-byte R||S signature. */
|
||||
der_span_t sigval;
|
||||
if (der_next(&cur2, limit2, &tag, &sigval) != 0 || tag != 0x03) return -1;
|
||||
if (sigval.len != 65 || sigval.p[0] != 0x00) return -1;
|
||||
|
||||
return ed25519_verify(issuer_pubkey, tbs_raw, tbs_raw_len, sigval.p + 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!der || !serial_out || !serial_len_out) return -1;
|
||||
|
||||
const uint8_t *cur = der;
|
||||
const uint8_t *end = der + der_len;
|
||||
uint8_t tag;
|
||||
der_span_t cert;
|
||||
if (der_next(&cur, end, &tag, &cert) != 0 || tag != 0x30) return -1;
|
||||
|
||||
const uint8_t *tbs_cur = cert.p;
|
||||
const uint8_t *tbs_limit = cert.p + cert.len;
|
||||
der_span_t tbs;
|
||||
if (der_next(&tbs_cur, tbs_limit, &tag, &tbs) != 0 || tag != 0x30) return -1;
|
||||
|
||||
const uint8_t *cur2 = tbs.p;
|
||||
const uint8_t *limit2 = tbs.p + tbs.len;
|
||||
|
||||
/* Optional version [0] EXPLICIT -- same peek-and-consume as
|
||||
* x509_extract_ed25519_pubkey() above; serialNumber follows it
|
||||
* directly whether or not it's present. */
|
||||
{
|
||||
const uint8_t *peek = cur2;
|
||||
uint8_t ptag;
|
||||
der_span_t pcontent;
|
||||
if (der_next(&peek, limit2, &ptag, &pcontent) == 0 && ptag == 0xA0) {
|
||||
cur2 = peek;
|
||||
}
|
||||
}
|
||||
|
||||
der_span_t serial;
|
||||
if (der_next(&cur2, limit2, &tag, &serial) != 0 || tag != 0x02) return -1;
|
||||
|
||||
const uint8_t *sp = serial.p;
|
||||
size_t slen = serial.len;
|
||||
/* Strip a single leading 0x00 pad byte -- DER adds one whenever the
|
||||
* value's high bit would otherwise make an unsigned INTEGER read as
|
||||
* negative. */
|
||||
if (slen > 1 && sp[0] == 0x00) { sp++; slen--; }
|
||||
|
||||
if (slen > serial_out_cap) return -1;
|
||||
memcpy(serial_out, sp, slen);
|
||||
*serial_len_out = slen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user