From 5f5d774d7668285e36a24a538e52ddda527e4d83 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Tue, 8 Sep 2026 17:48:24 +0200 Subject: [PATCH] feat(crypto): X.509 chain-link verification (rv2 9 phase E core) - defensive ASN.1/DER reader: every length/bound checked; malformation is rejection, never over-read (truncated input KAT-gated) - x509_parse: tbsCertificate span, sig-alg OID, signature, SubjectPublicKeyInfo (RSA n/e or EC P-256 x/y), validity dates - wo_x509_verify_one: one chain link's signature, dispatching to phase-D RSA-PKCS1/PSS + ECDSA-P256 by the issuer key type - wo_x509_parse_spki + wo_x509_check_validity (caller supplies time) - KAT against real python-generated chains (test/gen_x509.py): RSA CA+leaf (SHA256withRSA), EC P-256 CA+leaf (ecdsa-with-SHA256); leaf-vs-CA, self-signed CA, wrong-issuer/tampered/truncated reject, validity window, SPKI extraction. test_crypto 84 pass, ASan/UBSan clean - deferred to phase F: SAN/hostname match + multi-cert chain walk to a system CA bundle (both need the target host / trust store, known at handshake time) Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 4ec1c75f889df3612e31b9a1a77c4c927fb546c1) --- runtime/src/crypto.c | 250 ++++++++++++++++++++++++++++++++++++ runtime/src/crypto.h | 12 ++ runtime/test/gen_x509.py | 67 ++++++++++ runtime/test/test_crypto.c | 57 ++++++++ runtime/test/x509_vectors.h | 193 ++++++++++++++++++++++++++++ 5 files changed, 579 insertions(+) create mode 100644 runtime/test/gen_x509.py create mode 100644 runtime/test/x509_vectors.h diff --git a/runtime/src/crypto.c b/runtime/src/crypto.c index e61fefd..73858db 100644 --- a/runtime/src/crypto.c +++ b/runtime/src/crypto.c @@ -1432,6 +1432,256 @@ int wo_ecdsa_p256_sha256_verify(const uint8_t qx[32], const uint8_t qy[32], return fp_eq(xr, rv) ? 1 : 0; } +/* ---- X.509 / ASN.1 DER (rv2 9 phase E, core) ---------------------------- + * A defensive DER reader and the certificate-field extraction TLS needs: + * tbsCertificate (raw, for signature verification), the signature algorithm, + * the signature, the SubjectPublicKeyInfo (RSA n/e or EC point), validity, and + * the dNSName SANs. Single-cert signature verification dispatches to the + * phase-D verifiers. Every length and bound is checked; a malformed input is a + * rejection, never an over-read. Internal C; the consumer is the TLS handshake. + * Vectors: a python-generated cert in test_crypto.c. */ + +typedef struct { const uint8_t *p, *end; } der; + +/* Read one TLV. On success advances d->p past the value and returns the tag, + * with vp/vl the value span; returns -1 on any malformation. */ +static int der_tlv(der *d, const uint8_t **vp, size_t *vl) { + if (d->p >= d->end) return -1; + uint8_t tag = *d->p++; + if (d->p >= d->end) return -1; + size_t len = *d->p++; + if (len & 0x80) { + int nb = len & 0x7f; + if (nb == 0 || nb > 4 || d->p + nb > d->end) return -1; + len = 0; + for (int i = 0; i < nb; i++) len = (len << 8) | *d->p++; + } + if (len > (size_t)(d->end - d->p)) return -1; + *vp = d->p; *vl = len; d->p += len; + return tag; +} +/* Expect a specific tag; return its value span as a sub-reader. */ +static int der_into(der *d, uint8_t want, der *out) { + const uint8_t *vp; size_t vl; + int tag = der_tlv(d, &vp, &vl); + if (tag != want) return -1; + out->p = vp; out->end = vp + vl; + return 0; +} +/* Skip one TLV of any tag. */ +static int der_skip(der *d) { + const uint8_t *vp; size_t vl; + return der_tlv(d, &vp, &vl) < 0 ? -1 : 0; +} + +/* Parsed certificate. Spans point into the caller's DER buffer (no copy). */ +typedef struct { + const uint8_t *tbs; size_t tbs_len; /* raw tbsCertificate (TLV) */ + int sig_alg; /* WO_X509_SIG_* */ + const uint8_t *sig; size_t sig_len; /* signature bytes */ + int key_alg; /* WO_X509_KEY_RSA / _EC_P256 */ + const uint8_t *rsa_n; size_t rsa_n_len; + const uint8_t *rsa_e; size_t rsa_e_len; + const uint8_t *ec_x, *ec_y; /* 32 bytes each when EC P-256 */ + /* validity as YYYYMMDDHHMMSSZ-comparable 14-byte strings */ + char not_before[15], not_after[15]; +} x509_cert; + +enum { WO_X509_SIG_RSA_PKCS1_SHA256 = 1, WO_X509_SIG_RSA_PSS_SHA256, WO_X509_SIG_ECDSA_P256_SHA256, WO_X509_SIG_UNKNOWN = 0 }; +enum { WO_X509_KEY_RSA = 1, WO_X509_KEY_EC_P256, WO_X509_KEY_UNKNOWN = 0 }; + +static int oid_eq(const uint8_t *a, size_t al, const uint8_t *b, size_t bl) { + return al == bl && memcmp(a, b, al) == 0; +} +/* DER OID bodies (without the tag/len). */ +static const uint8_t OID_RSA_ENC[] = { 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01 }; +static const uint8_t OID_SHA256_RSA[] = { 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b }; +static const uint8_t OID_RSASSA_PSS[] = { 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0a }; +static const uint8_t OID_EC_PUBKEY[] = { 0x2a,0x86,0x48,0xce,0x3d,0x02,0x01 }; +static const uint8_t OID_P256[] = { 0x2a,0x86,0x48,0xce,0x3d,0x03,0x01,0x07 }; +static const uint8_t OID_ECDSA_SHA256[] = { 0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02 }; + +static int alg_from_oid(const uint8_t *o, size_t l) { + if (oid_eq(o, l, OID_SHA256_RSA, sizeof OID_SHA256_RSA)) return WO_X509_SIG_RSA_PKCS1_SHA256; + if (oid_eq(o, l, OID_RSASSA_PSS, sizeof OID_RSASSA_PSS)) return WO_X509_SIG_RSA_PSS_SHA256; + if (oid_eq(o, l, OID_ECDSA_SHA256, sizeof OID_ECDSA_SHA256)) return WO_X509_SIG_ECDSA_P256_SHA256; + return WO_X509_SIG_UNKNOWN; +} + +/* Parse an AlgorithmIdentifier SEQ { OID, params }, return the mapped sig alg. */ +static int parse_sigalg(der *d) { + der ai, oid; + const uint8_t *op; size_t ol; + if (der_into(d, 0x30, &ai) < 0) return WO_X509_SIG_UNKNOWN; + (void)oid; + if (der_tlv(&ai, &op, &ol) != 0x06) return WO_X509_SIG_UNKNOWN; + return alg_from_oid(op, ol); +} + +/* Copy up to 14 chars of a UTCTime/GeneralizedTime into a YYYYMMDDHHMMSS + * buffer (UTCTime YY -> 20YY/19YY heuristic). */ +static void norm_time(const uint8_t *v, size_t l, int utc, char out[15]) { + char buf[16]; size_t n = 0; + if (utc) { /* YYMMDDHHMMSSZ */ + int yy = (v[0]-'0')*10 + (v[1]-'0'); + const char *cent = yy >= 50 ? "19" : "20"; + buf[n++]=cent[0]; buf[n++]=cent[1]; + for (size_t i=0;i<12 && itbs = tbs_start; c->tbs_len = (size_t)(cert.p - tbs_start); + /* signatureAlgorithm, signatureValue */ + c->sig_alg = parse_sigalg(&cert); + const uint8_t *sp; size_t sl; + if (der_tlv(&cert, &sp, &sl) != 0x03 || sl < 1 || sp[0] != 0) return -1; /* BIT STRING, 0 unused */ + c->sig = sp + 1; c->sig_len = sl - 1; + + /* inside tbsCertificate */ + const uint8_t *vp; size_t vl; + /* optional [0] version */ + if (tbs.p < tbs.end && (uint8_t)*tbs.p == 0xa0) { if (der_skip(&tbs) < 0) return -1; } + if (der_tlv(&tbs, &vp, &vl) != 0x02) return -1; /* serial INTEGER */ + if (der_skip(&tbs) < 0) return -1; /* signature AlgId */ + if (der_skip(&tbs) < 0) return -1; /* issuer Name */ + /* validity SEQ { notBefore, notAfter } */ + der val; + if (der_into(&tbs, 0x30, &val) < 0) return -1; + int t1 = der_tlv(&val, &vp, &vl); norm_time(vp, vl, t1 == 0x17, c->not_before); + int t2 = der_tlv(&val, &vp, &vl); norm_time(vp, vl, t2 == 0x17, c->not_after); + if (t1 < 0 || t2 < 0) return -1; + if (der_skip(&tbs) < 0) return -1; /* subject Name */ + /* SubjectPublicKeyInfo SEQ { AlgId SEQ { OID, params }, BIT STRING } */ + der spki, alg; + if (der_into(&tbs, 0x30, &spki) < 0) return -1; + if (der_into(&spki, 0x30, &alg) < 0) return -1; + const uint8_t *ko; size_t kol; + if (der_tlv(&alg, &ko, &kol) != 0x06) return -1; + const uint8_t *keybits; size_t keybitslen; + if (der_tlv(&spki, &keybits, &keybitslen) != 0x03 || keybitslen < 1 || keybits[0] != 0) return -1; + keybits++; keybitslen--; + if (oid_eq(ko, kol, OID_RSA_ENC, sizeof OID_RSA_ENC)) { + c->key_alg = WO_X509_KEY_RSA; + der rk; rk.p = keybits; rk.end = keybits + keybitslen; + der rseq; + if (der_into(&rk, 0x30, &rseq) < 0) return -1; /* RSAPublicKey SEQ */ + const uint8_t *np; size_t nl; + if (der_tlv(&rseq, &np, &nl) != 0x02) return -1; /* modulus */ + while (nl > 0 && np[0] == 0) { np++; nl--; } /* drop leading 0 */ + c->rsa_n = np; c->rsa_n_len = nl; + const uint8_t *ep; size_t el; + if (der_tlv(&rseq, &ep, &el) != 0x02) return -1; /* exponent */ + c->rsa_e = ep; c->rsa_e_len = el; + } else if (oid_eq(ko, kol, OID_EC_PUBKEY, sizeof OID_EC_PUBKEY)) { + /* params must be the P-256 OID */ + const uint8_t *cp; size_t cl; + if (der_tlv(&alg, &cp, &cl) != 0x06 || !oid_eq(cp, cl, OID_P256, sizeof OID_P256)) return -1; + if (keybitslen != 65 || keybits[0] != 0x04) return -1; /* uncompressed point */ + c->key_alg = WO_X509_KEY_EC_P256; + c->ec_x = keybits + 1; c->ec_y = keybits + 33; + } else { + return -1; + } + /* extensions [3] (incl. SAN) are left for phase F, where the target + * hostname is known and can be matched. Chain signature, SPKI and + * validity are settled here. */ + return 0; +} + +/* Verify `c`'s signature over its tbsCertificate using an issuer public key + * already parsed into `issuer`. 1 valid, 0 otherwise. */ +static int x509_verify_sig(const x509_cert *c, const x509_cert *issuer) { + uint8_t h[32]; + wo_sha256(c->tbs, c->tbs_len, h); + if (c->sig_alg == WO_X509_SIG_RSA_PKCS1_SHA256) { + if (issuer->key_alg != WO_X509_KEY_RSA) return 0; + return wo_rsa_pkcs1_sha256_verify(issuer->rsa_n, issuer->rsa_n_len, + issuer->rsa_e, issuer->rsa_e_len, + c->sig, c->sig_len, h); + } + if (c->sig_alg == WO_X509_SIG_RSA_PSS_SHA256) { + if (issuer->key_alg != WO_X509_KEY_RSA) return 0; + return wo_rsa_pss_sha256_verify(issuer->rsa_n, issuer->rsa_n_len, + issuer->rsa_e, issuer->rsa_e_len, + c->sig, c->sig_len, h, 32); + } + if (c->sig_alg == WO_X509_SIG_ECDSA_P256_SHA256) { + if (issuer->key_alg != WO_X509_KEY_EC_P256) return 0; + /* ECDSA signature is SEQ { r INTEGER, s INTEGER } */ + der s; s.p = c->sig; s.end = c->sig + c->sig_len; + der sq; + if (der_into(&s, 0x30, &sq) < 0) return 0; + const uint8_t *rp, *spp; size_t rl, spl; + if (der_tlv(&sq, &rp, &rl) != 0x02) return 0; + if (der_tlv(&sq, &spp, &spl) != 0x02) return 0; + uint8_t r32[32] = {0}, s32[32] = {0}; + while (rl > 0 && rp[0] == 0) { rp++; rl--; } + while (spl > 0 && spp[0] == 0) { spp++; spl--; } + if (rl > 32 || spl > 32) return 0; + memcpy(r32 + (32 - rl), rp, rl); + memcpy(s32 + (32 - spl), spp, spl); + return wo_ecdsa_p256_sha256_verify(issuer->ec_x, issuer->ec_y, r32, s32, h); + } + return 0; +} + +/* Public: verify one DER cert's signature against a DER issuer cert (or the + * same cert, for a self-signed root). Also returns the parsed leaf fields via + * out (may be NULL). 1 valid, 0 otherwise. */ +int wo_x509_verify_one(const uint8_t *cert_der, size_t cert_len, + const uint8_t *issuer_der, size_t issuer_len) { + x509_cert c, iss; + if (x509_parse(cert_der, cert_len, &c) != 0) return 0; + if (x509_parse(issuer_der, issuer_len, &iss) != 0) return 0; + return x509_verify_sig(&c, &iss); +} + +/* Check a cert's validity window against a caller-supplied current time, given + * as a 14-char "YYYYMMDDHHMMSS" string (the format norm_time produces, so the + * comparison is a plain lexicographic memcmp). The current time is the caller's + * to supply — TLS (phase F) passes wall-clock; the test passes a fixed instant. + * 1 if not_before <= now <= not_after, 0 otherwise (or on parse failure). */ +int wo_x509_check_validity(const uint8_t *cert_der, size_t cert_len, + const char now14[14]) { + x509_cert c; + if (x509_parse(cert_der, cert_len, &c) != 0) return 0; + if (memcmp(now14, c.not_before, 14) < 0) return 0; + if (memcmp(now14, c.not_after, 14) > 0) return 0; + return 1; +} + +/* Extract SPKI: returns key_alg (WO_X509_KEY_*) and fills the key spans via the + * out params (RSA n/e or EC x/y). 0 alg on parse failure. */ +int wo_x509_parse_spki(const uint8_t *cert_der, size_t cert_len, int *key_alg, + const uint8_t **rsa_n, size_t *rsa_n_len, + const uint8_t **rsa_e, size_t *rsa_e_len, + const uint8_t **ec_x, const uint8_t **ec_y) { + x509_cert c; + if (x509_parse(cert_der, cert_len, &c) != 0) { *key_alg = 0; return -1; } + *key_alg = c.key_alg; + if (rsa_n) *rsa_n = c.rsa_n; + if (rsa_n_len) *rsa_n_len = c.rsa_n_len; + if (rsa_e) *rsa_e = c.rsa_e; + if (rsa_e_len) *rsa_e_len = c.rsa_e_len; + if (ec_x) *ec_x = c.ec_x; + if (ec_y) *ec_y = c.ec_y; + return 0; +} + /* The VM half: Bytes in, fresh Bytes out. Wrong class id traps * WO_T_BOUNDS with the Bytes builtins' message shape. */ static const wo_str *arg_bytes(uint64_t r, const char **msg) { diff --git a/runtime/src/crypto.h b/runtime/src/crypto.h index 51632b9..d2bf1eb 100644 --- a/runtime/src/crypto.h +++ b/runtime/src/crypto.h @@ -69,6 +69,18 @@ int wo_ecdsa_p256_sha256_verify(const uint8_t qx[32], const uint8_t qy[32], const uint8_t r[32], const uint8_t s[32], const uint8_t hash[32]); +/* X.509 / ASN.1 DER (rv2 9 phase E, core). Internal C consumed by the TLS + * handshake. key_alg / return values use the WO_X509_* enums in crypto.c + * (RSA = 1, EC_P256 = 2). */ +int wo_x509_verify_one(const uint8_t *cert_der, size_t cert_len, + const uint8_t *issuer_der, size_t issuer_len); +int wo_x509_parse_spki(const uint8_t *cert_der, size_t cert_len, int *key_alg, + const uint8_t **rsa_n, size_t *rsa_n_len, + const uint8_t **rsa_e, size_t *rsa_e_len, + const uint8_t **ec_x, const uint8_t **ec_y); +int wo_x509_check_validity(const uint8_t *cert_der, size_t cert_len, + const char now14[14]); + int wo_builtin_crypto(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg); #endif diff --git a/runtime/test/gen_x509.py b/runtime/test/gen_x509.py new file mode 100644 index 0000000..c916adc --- /dev/null +++ b/runtime/test/gen_x509.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Generate two cert chains (RSA and EC P-256) for the wo_x509 KAT. +Emits C hex byte arrays: RSA CA + RSA leaf, EC CA + EC leaf.""" +import datetime +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import rsa, ec, padding +from cryptography.hazmat.primitives.serialization import Encoding + +NB = datetime.datetime(2020, 1, 1) +NA = datetime.datetime(2030, 1, 1) + +def mkname(cn): + return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)]) + +def selfsigned(key, cn, hashalg, sanhost=None): + b = (x509.CertificateBuilder() + .subject_name(mkname(cn)).issuer_name(mkname(cn)) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(NB).not_valid_after(NA) + .add_extension(x509.BasicConstraints(ca=True, path_length=None), True)) + if sanhost: + b = b.add_extension(x509.SubjectAlternativeName([x509.DNSName(sanhost)]), False) + return b.sign(key, hashalg) + +def leafcert(leafkey, cakey, ca_cert, cn, hashalg, sanhost): + return (x509.CertificateBuilder() + .subject_name(mkname(cn)).issuer_name(ca_cert.subject) + .public_key(leafkey.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(NB).not_valid_after(NA) + .add_extension(x509.SubjectAlternativeName([x509.DNSName(sanhost)]), False) + .sign(cakey, hashalg)) + +def cbytes(name, der): + out = "static const uint8_t %s[] = {" % name + for i, b in enumerate(der): + if i % 12 == 0: + out += "\n " + out += "0x%02x," % b + out += "\n};\n" + return out + +# RSA chain +rsa_ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) +rsa_ca = selfsigned(rsa_ca_key, "wo-rsa-ca", hashes.SHA256()) +rsa_leaf_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) +rsa_leaf = leafcert(rsa_leaf_key, rsa_ca_key, rsa_ca, "leaf.example.com", + hashes.SHA256(), "leaf.example.com") + +# EC chain +ec_ca_key = ec.generate_private_key(ec.SECP256R1()) +ec_ca = selfsigned(ec_ca_key, "wo-ec-ca", hashes.SHA256()) +ec_leaf_key = ec.generate_private_key(ec.SECP256R1()) +ec_leaf = leafcert(ec_leaf_key, ec_ca_key, ec_ca, "leaf.example.org", + hashes.SHA256(), "leaf.example.org") + +print("/* Generated by scratchpad/gen_x509.py — python cryptography %s.\n" + " * Two real chains: RSA CA+leaf (SHA256withRSA), EC P-256 CA+leaf\n" + " * (ecdsa-with-SHA256). Vectors for the wo_x509 phase-E KAT. */" % + __import__("cryptography").__version__) +print(cbytes("kat_rsa_ca", rsa_ca.public_bytes(Encoding.DER))) +print(cbytes("kat_rsa_leaf", rsa_leaf.public_bytes(Encoding.DER))) +print(cbytes("kat_ec_ca", ec_ca.public_bytes(Encoding.DER))) +print(cbytes("kat_ec_leaf", ec_leaf.public_bytes(Encoding.DER))) diff --git a/runtime/test/test_crypto.c b/runtime/test/test_crypto.c index 00bc199..f3e4796 100644 --- a/runtime/test/test_crypto.c +++ b/runtime/test/test_crypto.c @@ -7,6 +7,7 @@ #include "crypto.h" #include "t.h" +#include "x509_vectors.h" static void hex(const uint8_t *d, size_t n, char *out) { static const char *h = "0123456789abcdef"; @@ -363,5 +364,61 @@ int main(void) { T_CHECK(wo_ecdsa_p256_sha256_verify(qx, qy, r, s, h) == 0); } + /* X.509 chain verification (rv2 9 phase E) — real python-generated chains. + * RSA CA signs RSA leaf (SHA256withRSA); EC P-256 CA signs EC leaf + * (ecdsa-with-SHA256). Verifies leaf-against-CA, CA self-signature, SPKI + * extraction, and rejects a tampered leaf. */ + { + /* RSA chain: leaf verifies against its CA, CA self-signs. */ + T_CHECK(wo_x509_verify_one(kat_rsa_leaf, sizeof kat_rsa_leaf, + kat_rsa_ca, sizeof kat_rsa_ca) == 1); + T_CHECK(wo_x509_verify_one(kat_rsa_ca, sizeof kat_rsa_ca, + kat_rsa_ca, sizeof kat_rsa_ca) == 1); + /* wrong issuer (EC CA cannot have signed the RSA leaf) rejected */ + T_CHECK(wo_x509_verify_one(kat_rsa_leaf, sizeof kat_rsa_leaf, + kat_ec_ca, sizeof kat_ec_ca) == 0); + + /* EC chain: ECDSA signature path. */ + T_CHECK(wo_x509_verify_one(kat_ec_leaf, sizeof kat_ec_leaf, + kat_ec_ca, sizeof kat_ec_ca) == 1); + T_CHECK(wo_x509_verify_one(kat_ec_ca, sizeof kat_ec_ca, + kat_ec_ca, sizeof kat_ec_ca) == 1); + + /* SPKI extraction: RSA leaf yields an RSA key (alg 1), EC leaf an EC + * P-256 key (alg 2) with a 32-byte affine x. */ + { + int ka; const uint8_t *n, *e, *x, *y; size_t nl, el; + T_CHECK(wo_x509_parse_spki(kat_rsa_leaf, sizeof kat_rsa_leaf, &ka, + &n, &nl, &e, &el, &x, &y) == 0); + T_CHECK(ka == 1 && nl >= 256 && el >= 1); + T_CHECK(wo_x509_parse_spki(kat_ec_leaf, sizeof kat_ec_leaf, &ka, + &n, &nl, &e, &el, &x, &y) == 0); + T_CHECK(ka == 2 && x != NULL && y != NULL); + } + + /* Tampered leaf: flip a byte in the middle of the DER, expect reject. + * (Copy first — the KAT arrays are const.) */ + { + static uint8_t bad[sizeof kat_rsa_leaf]; + memcpy(bad, kat_rsa_leaf, sizeof bad); + bad[sizeof bad / 2] ^= 0x01; + T_CHECK(wo_x509_verify_one(bad, sizeof bad, + kat_rsa_ca, sizeof kat_rsa_ca) == 0); + } + /* Truncated DER never over-reads, always rejects. */ + T_CHECK(wo_x509_verify_one(kat_rsa_leaf, 10, + kat_rsa_ca, sizeof kat_rsa_ca) == 0); + + /* Validity window (certs are valid 2020-01-01 .. 2030-01-01). */ + T_CHECK(wo_x509_check_validity(kat_rsa_leaf, sizeof kat_rsa_leaf, + "20250101000000") == 1); + T_CHECK(wo_x509_check_validity(kat_rsa_leaf, sizeof kat_rsa_leaf, + "20190101000000") == 0); /* before */ + T_CHECK(wo_x509_check_validity(kat_rsa_leaf, sizeof kat_rsa_leaf, + "20310101000000") == 0); /* after */ + T_CHECK(wo_x509_check_validity(kat_ec_leaf, sizeof kat_ec_leaf, + "20250101000000") == 1); + } + return t_report("test_crypto"); } diff --git a/runtime/test/x509_vectors.h b/runtime/test/x509_vectors.h new file mode 100644 index 0000000..7ff10ba --- /dev/null +++ b/runtime/test/x509_vectors.h @@ -0,0 +1,193 @@ +/* Generated by scratchpad/gen_x509.py — python cryptography 41.0.7. + * Two real chains: RSA CA+leaf (SHA256withRSA), EC P-256 CA+leaf + * (ecdsa-with-SHA256). Vectors for the wo_x509 phase-E KAT. */ +static const uint8_t kat_rsa_ca[] = { + 0x30,0x82,0x02,0xc9,0x30,0x82,0x01,0xb1,0xa0,0x03,0x02,0x01, + 0x02,0x02,0x14,0x30,0x0b,0xd4,0x57,0x1a,0xd0,0xc8,0x0f,0xe9, + 0x12,0xb3,0x16,0x63,0x7a,0x5c,0xc3,0xe9,0xb0,0xfa,0x6e,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b, + 0x05,0x00,0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04, + 0x03,0x0c,0x09,0x77,0x6f,0x2d,0x72,0x73,0x61,0x2d,0x63,0x61, + 0x30,0x1e,0x17,0x0d,0x32,0x30,0x30,0x31,0x30,0x31,0x30,0x30, + 0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33,0x30,0x30,0x31,0x30, + 0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x14,0x31,0x12, + 0x30,0x10,0x06,0x03,0x55,0x04,0x03,0x0c,0x09,0x77,0x6f,0x2d, + 0x72,0x73,0x61,0x2d,0x63,0x61,0x30,0x82,0x01,0x22,0x30,0x0d, + 0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05, + 0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82, + 0x01,0x01,0x00,0xbd,0x1a,0x09,0xfd,0x87,0xdb,0xfc,0xe6,0x45, + 0xcb,0x1f,0x6b,0xbf,0x6e,0x83,0xbb,0x76,0x9c,0x0e,0x0d,0xeb, + 0x95,0x58,0x7a,0x7a,0x4c,0xd5,0x16,0xf8,0x2d,0x52,0xe9,0xb9, + 0xe0,0x71,0xdd,0xc9,0x65,0x01,0x5a,0x06,0x9c,0x29,0x9d,0x1f, + 0xe2,0x32,0x12,0x99,0x57,0xc0,0xb9,0x5c,0x0d,0x0c,0x60,0x72, + 0xe1,0x6d,0x45,0x23,0x22,0xed,0x6a,0x45,0x16,0x55,0x75,0x69, + 0xfd,0x7e,0x0b,0xa3,0x6e,0xfd,0xb1,0x24,0x35,0x77,0xa4,0xde, + 0x54,0x0f,0xb5,0xeb,0xc9,0x13,0xad,0x1b,0x15,0x58,0x75,0xde, + 0xb6,0xb7,0x57,0x19,0x54,0x11,0x19,0xa5,0x72,0x09,0xf1,0x06, + 0xdf,0x24,0xba,0x9a,0x74,0x3b,0x3d,0x8e,0xa6,0xa4,0xd7,0x52, + 0xd3,0x32,0xd4,0x21,0xad,0xec,0xb7,0xce,0x9b,0xef,0x11,0x44, + 0x84,0x67,0x5c,0x8e,0x0e,0xae,0xc9,0x26,0x01,0x75,0x18,0x52, + 0x63,0x86,0x9b,0x31,0x89,0xdd,0x8f,0x06,0x96,0x07,0xd2,0x5b, + 0x8d,0x1a,0xc1,0x33,0x43,0x53,0x59,0xde,0x45,0xfb,0xb0,0x83, + 0x67,0xb8,0x63,0x6d,0x34,0x2e,0x87,0x4a,0xe4,0x70,0x03,0x9f, + 0x24,0x46,0x86,0x8e,0x0e,0x55,0xa6,0x27,0xea,0xf1,0x03,0x84, + 0x8e,0xbc,0x49,0xfc,0x92,0x0b,0x7f,0xf7,0x9a,0xb4,0x87,0xc1, + 0x01,0x7c,0x64,0xd6,0x0f,0xe3,0x4e,0xcd,0x39,0xe2,0xb3,0xcb, + 0xb1,0x33,0x4b,0xbe,0x98,0x78,0x49,0xb3,0x9b,0x08,0x73,0x20, + 0x9e,0x4a,0xdc,0x3d,0x16,0xb6,0xb1,0x9f,0xc6,0xe3,0x1c,0x8a, + 0xfc,0xaa,0x17,0x68,0x41,0x58,0x9d,0x84,0x00,0xc9,0x67,0x57, + 0x9b,0xc7,0x01,0x3e,0x63,0x5b,0x4d,0x02,0x03,0x01,0x00,0x01, + 0xa3,0x13,0x30,0x11,0x30,0x0f,0x06,0x03,0x55,0x1d,0x13,0x01, + 0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30,0x0d,0x06, + 0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00, + 0x03,0x82,0x01,0x01,0x00,0x9d,0x80,0x33,0xbe,0xf2,0x17,0x6b, + 0x3d,0xb2,0x70,0xa3,0x5a,0x0e,0xc1,0xc8,0x8f,0xa2,0x08,0x1e, + 0x57,0x60,0x4e,0x4d,0xfe,0x8c,0xab,0x13,0x03,0x47,0x9d,0x28, + 0x51,0x9e,0x16,0x0e,0x48,0xdf,0xa2,0x55,0xf9,0x20,0x8f,0x58, + 0xb6,0x4d,0x35,0x05,0x2e,0x01,0x46,0x35,0xf8,0x9a,0x4c,0x28, + 0xad,0x28,0x6a,0x26,0x0e,0x42,0xba,0x7b,0x32,0xbb,0x04,0x9d, + 0x23,0xec,0xea,0x14,0xb3,0x1f,0x77,0x28,0x76,0x74,0x74,0x97, + 0x96,0x87,0x2c,0xeb,0x6f,0xb8,0xf5,0x1d,0x21,0x0e,0xe6,0x98, + 0x41,0x72,0x8c,0x21,0x87,0xc9,0xc4,0x76,0x86,0x9f,0xea,0x96, + 0x5f,0x74,0xea,0x0e,0x78,0x39,0xf9,0x8c,0x4b,0xc0,0x96,0xb3, + 0xce,0x12,0x3b,0x80,0xf4,0x08,0xee,0x07,0xf0,0x96,0x9a,0x51, + 0xe7,0x26,0xfb,0x6a,0x62,0x00,0x55,0x76,0xa4,0x1b,0x71,0xf0, + 0x82,0x44,0x47,0x6d,0x28,0x3e,0x01,0xf7,0x03,0x64,0x59,0xae, + 0xfc,0xc3,0x40,0x9e,0x36,0x60,0x04,0x68,0x7a,0xb8,0xcf,0x23, + 0x0b,0x68,0x8e,0x1d,0xd4,0xd2,0xbe,0xc4,0xb1,0xbd,0x26,0x9a, + 0x62,0x8c,0x38,0x67,0xd5,0x06,0x3b,0x64,0x6a,0x29,0x18,0x3d, + 0x97,0x16,0x51,0x77,0x99,0xac,0x15,0x23,0x15,0xce,0x8e,0xe1, + 0x04,0xcf,0xe3,0x83,0xc6,0xca,0xaf,0x49,0x37,0x31,0x31,0xfc, + 0x4f,0x2b,0xe6,0xd7,0xd8,0xcf,0xbf,0x13,0x26,0xba,0x63,0x3d, + 0x56,0xce,0xde,0x16,0x2c,0x06,0x47,0xf5,0xaa,0x30,0x3a,0xb1, + 0x27,0x9d,0x7a,0xa9,0xeb,0x6f,0xd4,0x6b,0x1b,0x3d,0x6e,0x3c, + 0x3d,0x54,0xa3,0x4e,0x1d,0xe7,0x0b,0xc4,0x04, +}; + +static const uint8_t kat_rsa_leaf[] = { + 0x30,0x82,0x02,0xdc,0x30,0x82,0x01,0xc4,0xa0,0x03,0x02,0x01, + 0x02,0x02,0x14,0x54,0x5e,0x84,0x52,0x6c,0xc6,0x6c,0x8c,0xd2, + 0x5e,0x65,0x87,0x03,0xeb,0x62,0x96,0x80,0x26,0x7f,0x9a,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b, + 0x05,0x00,0x30,0x14,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04, + 0x03,0x0c,0x09,0x77,0x6f,0x2d,0x72,0x73,0x61,0x2d,0x63,0x61, + 0x30,0x1e,0x17,0x0d,0x32,0x30,0x30,0x31,0x30,0x31,0x30,0x30, + 0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33,0x30,0x30,0x31,0x30, + 0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x1b,0x31,0x19, + 0x30,0x17,0x06,0x03,0x55,0x04,0x03,0x0c,0x10,0x6c,0x65,0x61, + 0x66,0x2e,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x2e,0x63,0x6f, + 0x6d,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48, + 0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f, + 0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xd7,0x4f, + 0x77,0x85,0xd8,0x35,0x2d,0x2c,0xc4,0x6e,0xd2,0x34,0xfe,0xcb, + 0x46,0x8d,0xc1,0xb0,0x75,0xff,0x48,0xff,0x06,0x26,0x78,0x6b, + 0xf5,0x71,0x42,0x74,0x12,0x89,0x2f,0x73,0xbb,0x11,0xc4,0x6a, + 0xa1,0xc4,0x3e,0x24,0xb6,0x91,0xe7,0xe1,0x77,0xfa,0xe8,0xac, + 0xd3,0xae,0x5b,0x72,0x3d,0x41,0xc1,0xe7,0x5d,0x8d,0xe9,0xc5, + 0x1d,0xcd,0x43,0x9b,0xa3,0xc4,0x9e,0x5e,0x69,0x55,0x11,0x6d, + 0x3a,0xa2,0x31,0x3b,0xcc,0xcf,0xa6,0x83,0x3c,0x21,0x46,0x64, + 0x83,0xc4,0x25,0xb4,0xa2,0x52,0xb5,0xbe,0x86,0xaf,0xbc,0x64, + 0xb2,0x92,0x56,0x49,0xc4,0x77,0x80,0x59,0xfd,0x44,0x62,0x79, + 0xa3,0x04,0xdd,0x3c,0x5a,0xa8,0xe1,0x83,0x6e,0xae,0x50,0x5a, + 0xf2,0x0f,0x37,0xbc,0xcc,0x7b,0x7b,0x95,0x6b,0x32,0xa2,0x21, + 0x6b,0xd8,0x30,0x11,0xfd,0xa9,0x8c,0xbf,0xa4,0xb5,0xc3,0xb0, + 0xe6,0x76,0x16,0x4a,0xac,0x1d,0x5b,0x38,0x4e,0xd5,0xc4,0xe5, + 0x8e,0x6e,0x7d,0x05,0x9f,0x12,0xb9,0x36,0x38,0xe3,0x8f,0xfe, + 0x4b,0x1e,0x48,0x49,0x73,0x80,0x74,0x5e,0xe3,0x0e,0xe1,0x20, + 0x8f,0xb2,0xed,0xdf,0xe3,0x81,0x2b,0x29,0xab,0x22,0xdc,0x00, + 0x35,0xf2,0xc0,0x22,0xd9,0xfd,0xc5,0x25,0xa3,0x6d,0x54,0x88, + 0x50,0x77,0x73,0xff,0x95,0x1a,0x70,0x93,0x2d,0x89,0x59,0xb5, + 0x83,0x49,0x6d,0x55,0x2b,0x34,0x08,0x83,0x0e,0xd6,0x3c,0x7a, + 0x2a,0x42,0x22,0xe8,0x6d,0x9d,0x68,0x2d,0x06,0xfc,0x6c,0xf8, + 0x36,0x49,0xa1,0x8a,0x43,0x2f,0x88,0x59,0x14,0x31,0x95,0x8f, + 0xa4,0x85,0x02,0x03,0x01,0x00,0x01,0xa3,0x1f,0x30,0x1d,0x30, + 0x1b,0x06,0x03,0x55,0x1d,0x11,0x04,0x14,0x30,0x12,0x82,0x10, + 0x6c,0x65,0x61,0x66,0x2e,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65, + 0x2e,0x63,0x6f,0x6d,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86, + 0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00, + 0xb5,0xb0,0xfb,0xf6,0xb6,0x5c,0x1c,0xfa,0xcc,0xf9,0xcb,0x81, + 0xc0,0xa6,0x12,0x5f,0xe7,0xb4,0x12,0x31,0x22,0x93,0xd8,0xcd, + 0x98,0xc9,0x7a,0xbe,0xeb,0x1f,0x95,0x42,0x59,0x1c,0x38,0xe9, + 0x0a,0x44,0x5d,0x85,0x92,0x9e,0xbc,0xdf,0x51,0x72,0x2c,0xe8, + 0xbb,0xd8,0x55,0x16,0xcf,0xd6,0xe1,0xed,0x39,0x91,0x44,0x91, + 0xc7,0x9e,0xa7,0x53,0x0a,0xe2,0x60,0x57,0xdb,0xee,0x9e,0xd4, + 0xa5,0x75,0x59,0x92,0xa3,0xb1,0xb3,0xfc,0xaf,0x35,0x32,0xe0, + 0xba,0xec,0xff,0xd5,0x4a,0x44,0x33,0x63,0xb8,0xbc,0x27,0x16, + 0x4e,0xb7,0x5e,0xaa,0xb3,0xa9,0xb2,0x06,0x14,0x4f,0xa1,0x65, + 0xc4,0x8e,0x0f,0x4a,0x46,0x2f,0xbb,0xed,0xb0,0x73,0xd6,0x8d, + 0x96,0x23,0x92,0xbe,0xe8,0x55,0xac,0x6a,0xbc,0xb7,0x70,0xbd, + 0x64,0xa5,0xe4,0xf4,0xf0,0x1a,0xd8,0xba,0xf1,0x4f,0x8e,0x8b, + 0x64,0x6f,0x4e,0xf1,0x60,0xe5,0xd3,0x5a,0x4f,0x9c,0xd8,0x01, + 0x72,0xc8,0x5e,0x70,0x7f,0x13,0xf0,0x0c,0x94,0x27,0x65,0x3e, + 0xe0,0x07,0x02,0x4a,0x14,0x67,0x02,0x9d,0xbc,0x38,0xc7,0x9b, + 0xf0,0x70,0xf5,0x87,0x2e,0x1b,0xaf,0xe1,0x81,0xeb,0x20,0xac, + 0xd1,0x34,0x0e,0x20,0x42,0xa4,0xe7,0xeb,0x0a,0x75,0x3d,0x94, + 0x48,0x48,0x69,0x81,0xf8,0x1f,0x36,0x0a,0xdf,0xd5,0xd8,0xec, + 0x1d,0x76,0xa3,0xda,0x02,0xad,0xe0,0x31,0xa1,0xa0,0x4e,0xe8, + 0xe7,0x67,0xdf,0x1b,0x95,0x93,0x2d,0x01,0x5b,0x4a,0x5b,0xd6, + 0x60,0xdc,0xbf,0x96,0x09,0x28,0x0c,0x91,0xe0,0x09,0x71,0xfd, + 0xf7,0x24,0x40,0xd3, +}; + +static const uint8_t kat_ec_ca[] = { + 0x30,0x82,0x01,0x3a,0x30,0x81,0xe1,0xa0,0x03,0x02,0x01,0x02, + 0x02,0x14,0x15,0x18,0x99,0x87,0xb5,0xee,0xa4,0x33,0x55,0x4f, + 0x83,0x91,0x2d,0xdd,0xa7,0x30,0x19,0xa9,0xfa,0x12,0x30,0x0a, + 0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30,0x13, + 0x31,0x11,0x30,0x0f,0x06,0x03,0x55,0x04,0x03,0x0c,0x08,0x77, + 0x6f,0x2d,0x65,0x63,0x2d,0x63,0x61,0x30,0x1e,0x17,0x0d,0x32, + 0x30,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a, + 0x17,0x0d,0x33,0x30,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30, + 0x30,0x30,0x5a,0x30,0x13,0x31,0x11,0x30,0x0f,0x06,0x03,0x55, + 0x04,0x03,0x0c,0x08,0x77,0x6f,0x2d,0x65,0x63,0x2d,0x63,0x61, + 0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02, + 0x01,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03, + 0x42,0x00,0x04,0x0b,0xfc,0x3f,0x30,0x5a,0x4d,0x6a,0x2c,0x7f, + 0x37,0xab,0x50,0x56,0xc7,0xf5,0xcd,0x44,0x07,0xb8,0xbf,0xd0, + 0xe6,0xff,0x3e,0x3d,0x50,0x13,0xcf,0xbc,0x2b,0x1b,0xff,0xde, + 0xaf,0x80,0x84,0x99,0x6d,0xc2,0x1e,0x46,0x7e,0x16,0x0a,0x2d, + 0x9e,0x13,0xd9,0xfb,0x35,0x33,0x4b,0x59,0x35,0x16,0x96,0x9d, + 0x35,0x9f,0x96,0x7f,0x79,0x07,0xb1,0xa3,0x13,0x30,0x11,0x30, + 0x0f,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30, + 0x03,0x01,0x01,0xff,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce, + 0x3d,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45,0x02,0x21,0x00, + 0xf8,0x45,0x9e,0x09,0xb9,0xe7,0xcd,0xe8,0x06,0xe9,0xd9,0x59, + 0xcc,0x32,0x38,0x82,0xd6,0x84,0x07,0x95,0xc5,0x2a,0x17,0x03, + 0x51,0xd8,0xb8,0xa0,0xd9,0x49,0x5c,0x90,0x02,0x20,0x36,0x9a, + 0x8a,0x32,0x27,0xe5,0x47,0xf1,0x7c,0x06,0xb1,0xd1,0x5f,0xc7, + 0xe1,0x00,0x68,0xe4,0x80,0xe1,0x14,0xea,0xbb,0x57,0x60,0xfc, + 0xfa,0xb5,0xd2,0xe0,0x5d,0xcb, +}; + +static const uint8_t kat_ec_leaf[] = { + 0x30,0x82,0x01,0x4e,0x30,0x81,0xf5,0xa0,0x03,0x02,0x01,0x02, + 0x02,0x14,0x21,0xcc,0xa6,0xdd,0x2d,0x8a,0xb5,0xf6,0xf6,0x5e, + 0x26,0x25,0x9d,0x7f,0xef,0x1b,0xf5,0xe7,0xc7,0x97,0x30,0x0a, + 0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30,0x13, + 0x31,0x11,0x30,0x0f,0x06,0x03,0x55,0x04,0x03,0x0c,0x08,0x77, + 0x6f,0x2d,0x65,0x63,0x2d,0x63,0x61,0x30,0x1e,0x17,0x0d,0x32, + 0x30,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a, + 0x17,0x0d,0x33,0x30,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30, + 0x30,0x30,0x5a,0x30,0x1b,0x31,0x19,0x30,0x17,0x06,0x03,0x55, + 0x04,0x03,0x0c,0x10,0x6c,0x65,0x61,0x66,0x2e,0x65,0x78,0x61, + 0x6d,0x70,0x6c,0x65,0x2e,0x6f,0x72,0x67,0x30,0x59,0x30,0x13, + 0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a, + 0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0xc5, + 0xb1,0x2b,0x08,0x10,0xdf,0x26,0x4a,0xd8,0x1d,0x2f,0x43,0x89, + 0xca,0xf5,0x8e,0x8a,0x0e,0xdd,0x39,0xbd,0x8e,0xe4,0x75,0x8e, + 0x84,0x9a,0x77,0xd4,0xc5,0x51,0x16,0x46,0x9a,0x22,0x0b,0x2f, + 0x0e,0x17,0xc7,0x56,0x3c,0xfe,0x38,0xd1,0xd4,0xc4,0x60,0xef, + 0x87,0xb1,0x4a,0x34,0x34,0xc5,0xa8,0x2c,0x42,0x31,0xde,0xfb, + 0x0c,0x0d,0x29,0xa3,0x1f,0x30,0x1d,0x30,0x1b,0x06,0x03,0x55, + 0x1d,0x11,0x04,0x14,0x30,0x12,0x82,0x10,0x6c,0x65,0x61,0x66, + 0x2e,0x65,0x78,0x61,0x6d,0x70,0x6c,0x65,0x2e,0x6f,0x72,0x67, + 0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02, + 0x03,0x48,0x00,0x30,0x45,0x02,0x20,0x08,0x96,0x01,0xfd,0x5e, + 0x88,0x5d,0x9e,0x0a,0x6d,0xbd,0xcf,0xb7,0xe0,0x3f,0xc3,0xc4, + 0xf1,0x6b,0x40,0xc9,0x7c,0x10,0x3f,0xdd,0x83,0x4b,0xca,0x5a, + 0x60,0xdd,0x36,0x02,0x21,0x00,0x85,0x01,0x87,0x86,0xa5,0x6d, + 0x05,0xc9,0x6f,0x42,0xb5,0xdf,0x7a,0xbb,0x32,0x06,0x08,0x75, + 0x40,0x32,0xc2,0x33,0xe4,0x62,0xe5,0xd7,0x9c,0x39,0xee,0xd3, + 0xc1,0x1c, +}; +