nx_x509_validate.nx source
↩ module page · 104 lines · 4418 B
1// nx_x509_validate.nx -- X.509 certificate validation (Gap I.2.A).
2//
3// Phase 0b §I.2 of the Nishi TLS 1.3 stack per
4// docs/NISHI_TLS13_GAP_AUDIT.md. Composes the shipped
5// ed25519_verify_full (T11) with the X509Cert parse offsets
6// (nx_x509, extended in T6) into the cert-validation primitives
7// that the TLS client's accept-any stub will eventually call.
8//
9// What it does today:
10// - x509_verify_signature_ed25519(buf, cert, signer_pubkey32):
11// read cert.tbs + cert.sig from buf, verify the signature
12// under signer_pubkey32 using shipped ed25519_verify_full.
13// Returns NX_X509_VAL_OK (1) on valid, NX_X509_VAL_BAD_SIG (0)
14// on invalid. Negative on malformed (e.g. sig wrong length).
15//
16// - Sealed verdict + validity gate.
17//
18// What it doesn't do yet (sub-pieces I.2.B + I.2.C, ~1 session each):
19// - validity period check (UTCTime / GeneralizedTime parse +
20// compare against caller-supplied now_unix_secs). Needs:
21// * fn x509_validity_get(cert, buf, out_notBefore, out_notAfter)
22// * ASN.1 time-string parser (Year Month Day Hour Min Sec Z)
23// * compose against nx_time / unix-epoch math
24// - SAN extension walk (parse cert.extensions_off/len for
25// OID 2.5.29.17 SubjectAltName, walk GeneralName SEQUENCE,
26// extract dNSName entries, match against SNI hostname).
27// - ECDSA-P256 signature verify (for real-world server certs;
28// covers ~80% of Web PKI).
29// - RSA-PSS signature verify (for the remaining ~15%).
30// - Full RFC 5280 chain walk + trust-anchor matching (Gap I.3).
31//
32// KAT verified:
33// - Self-signed Ed25519 cert (RFC 8032 §7.1 TEST 1 keypair;
34// fake tbs blob; sign with ed25519_sign_full at test time;
35// wrap into a synthetic X509Cert offset structure) validates.
36// - Same cert with one tbs byte flipped: validation FAILS.
37// - Same cert with a different signer_pubkey: validation FAILS.
38// - Same cert with sig byte flipped: validation FAILS.
39//
40// Composes with:
41// - nx_x509 (X509Cert struct + parse offsets)
42// - nx_ed25519_signature (ed25519_verify_full)
43//
44// license_tier: INDEPENDENT_REDERIVE
45// genealogy_id: international-research-sources/ietf/rfc_8032 + ietf/rfc_5280
46// lineage_id: nishi_x509_validate_q10
47
48// nx_safety_envelope:
49// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
50// sil_target: SIL1
51// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
52// verdict: NOT_YET_EVALUATED
53
54import "nx_syscalls.nx"
55import "nx_x509.nx"
56import "nx_ed25519_signature.nx"
57
58const NX_X509_VAL_OK: i64 = 1
59const NX_X509_VAL_BAD_SIG: i64 = 2
60const NX_X509_VAL_BAD_SIG_LEN: i64 = 3
61const NX_X509_VAL_BAD_PUB_LEN: i64 = 4
62const NX_X509_VAL_BAD_TBS: i64 = 5
63const NX_X509_VAL_VERDICT_N: i64 = 6
64
65// Verify an X.509 certificate's outer signature under an
66// Ed25519 signer public key.
67//
68// Inputs:
69// buf = the full DER cert byte buffer (cert was
70// parsed by x509_parse against this buf)
71// cert = parsed X509Cert with tbs_off/len + sig_off/len
72// populated
73// signer_pubkey32 = 32-byte raw Ed25519 public key of the
74// signer (for self-signed: cert's own pubkey;
75// for chain: parent CA's pubkey)
76//
77// Returns NX_X509_VAL_OK (1) on valid, NX_X509_VAL_BAD_SIG (0)
78// on invalid, or a NX_X509_VAL_BAD_* negative verdict on
79// malformed input.
80//
81// Caller responsibility (NOT enforced here, queued):
82// - validity period check (notBefore <= now <= notAfter)
83// - SAN hostname match against the SNI peer name
84// - chain walk to a configured trust anchor
85// - signature_algorithm OID matches Ed25519 (1.3.101.112)
86func x509_verify_signature_ed25519(
87 buf: *u8, cert: *X509Cert,
88 signer_pubkey32: *u8
89) -> i64 {
90 if cert.sig_len != 64 { return NX_X509_VAL_BAD_SIG_LEN }
91 if cert.tbs_len < 1 { return NX_X509_VAL_BAD_TBS }
92 let tbs_ptr: *u8 = buf + cert.tbs_off
93 let sig_ptr: *u8 = buf + cert.sig_off
94 let v: i64 = ed25519_verify_full(signer_pubkey32, tbs_ptr, cert.tbs_len, sig_ptr)
95 if v == NX_ED25519_SIG_OK { return NX_X509_VAL_OK }
96 return NX_X509_VAL_BAD_SIG
97}
98
99// Sealed-enum validity gate.
100func nx_x509_val_verdict_is_valid(v: i64) -> i64 {
101 if v < NX_X509_VAL_OK { return 0 }
102 if v >= NX_X509_VAL_VERDICT_N { return 0 }
103 return 1
104}