nx_x509_verify_ecdsa.nx source
↩ module page · 106 lines · 4159 B
1// nx_x509_verify_ecdsa.nx -- X.509 outer-signature verification for
2// ECDSA-P256-with-SHA-256 signed certs (the dominant Web PKI sig
3// algorithm).
4//
5// Phase 0b §I.4 piece 2 of the chain-walker arc. Composes:
6// - nx_sha256 (sha256_digest -- 256-bit hash of tbs)
7// - nx_ecdsa_sig_der (parse DER sig into r, s)
8// - nx_ecdsa_p256 (verify under issuer's pubkey)
9//
10// into the per-cert verify the chain-walker will call repeatedly:
11// "verify this cert's outer sig against this issuer's pubkey"
12//
13// What this primitive verifies:
14// - SHA-256(tbs_bytes) -> e (the 256-bit message hash)
15// - DER-decode the signature into r, s
16// - Call ecdsa_p256_verify(pub_x, pub_y, e, r, s)
17//
18// What it does NOT verify (caller / chain walker / X.509 leaf-check
19// responsibility):
20// - cert validity period
21// - cert SAN matches hostname
22// - cert sig_alg OID actually IS ecdsa-with-SHA-256 (caller dispatches
23// by sig_alg before calling this primitive)
24// - issuer's pubkey actually matches cert.issuer Subject DN
25// (chain walker resolves issuer first)
26//
27// Public API:
28// nx_x509_verify_ecdsa_p256(tbs, tbs_len, sig_der, sig_len,
29// pub_x, pub_y) -> verdict
30// nx_x509_verify_ecdsa_verdict_is_valid(v) -> 0|1
31//
32// Sealed verdict enum:
33// NX_X509_ECDSA_OK sig verifies
34// NX_X509_ECDSA_BAD_SIG_DER DER-decode failed
35// NX_X509_ECDSA_BAD_SIG verify returned BAD_SIG / range fail
36// NX_X509_ECDSA_BAD_PUBKEY pubkey off-curve or infinity
37// NX_X509_ECDSA_INFINITY u1*G + u2*Q = O (corrupt sig)
38//
39// Per Cardinals 9 (single-responsibility -- this primitive is JUST
40// the SHA-256 + parse + verify pipeline; caller selects the algo),
41// 22 (composition over configuration), 23 (preamble names what
42// caller MUST verify separately).
43//
44// license_tier: INDEPENDENT_REDERIVE
45// genealogy_id: international-research-sources/ietf/rfc_5280 + rfc_5480
46// lineage_id: nishi_x509_verify_ecdsa_p256_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-19, x509-outer-sig-ecdsa-orchestrator]
52// verdict: NOT_YET_EVALUATED
53
54import "nx_syscalls.nx"
55import "nx_u256.nx"
56import "nx_sha256.nx"
57import "nx_ecdsa_sig_der.nx"
58import "nx_ecdsa_p256.nx"
59
60const NX_X509_ECDSA_OK: i64 = 1
61const NX_X509_ECDSA_BAD_SIG_DER: i64 = 2
62const NX_X509_ECDSA_BAD_SIG: i64 = 3
63const NX_X509_ECDSA_BAD_PUBKEY: i64 = 4
64const NX_X509_ECDSA_INFINITY: i64 = 5
65const NX_X509_ECDSA_VERDICT_N: i64 = 6
66
67func nx_x509_verify_ecdsa_verdict_is_valid(v: i64) -> i64 {
68 if v < NX_X509_ECDSA_OK { return 0 }
69 if v >= NX_X509_ECDSA_VERDICT_N { return 0 }
70 return 1
71}
72
73// Verify an X.509 outer signature for an ECDSA-P256-with-SHA-256
74// signed cert.
75//
76// Returns NX_X509_ECDSA_OK on valid signature, or a non-OK verdict.
77func nx_x509_verify_ecdsa_p256(tbs: *u8, tbs_len: i64,
78 sig_der: *u8, sig_len: i64,
79 pub_x: *i64, pub_y: *i64) -> i64 {
80 // ---- Hash tbs with SHA-256 ----
81 let hash_bytes: *u8 = sys_mmap(32)
82 sha256_digest(tbs, tbs_len, hash_bytes)
83
84 let e: *i64 = u256_alloc()
85 u256_load_be(e, hash_bytes)
86
87 // ---- Parse DER signature into r, s ----
88 let r: *i64 = u256_alloc()
89 let s: *i64 = u256_alloc()
90 let parse: i64 = nx_ecdsa_sig_parse_der(sig_der, sig_len, r, s)
91 if parse != NX_ECDSA_DER_OK { return NX_X509_ECDSA_BAD_SIG_DER }
92
93 // ---- Call ECDSA verify ----
94 let v: i64 = nx_ecdsa_p256_verify(pub_x, pub_y, e, r, s)
95 if v == NX_ECDSA_OK { return NX_X509_ECDSA_OK }
96 if v == NX_ECDSA_PUBKEY_NOT_ON_CURVE { return NX_X509_ECDSA_BAD_PUBKEY }
97 if v == NX_ECDSA_PUBKEY_INFINITY { return NX_X509_ECDSA_BAD_PUBKEY }
98 if v == NX_ECDSA_INFINITY_RESULT { return NX_X509_ECDSA_INFINITY }
99 // BAD_R_RANGE, BAD_S_RANGE, BAD_SIG -> generic bad-sig verdict
100 return NX_X509_ECDSA_BAD_SIG
101}
102
103// Compile-only smoke. Real KAT in nx_x509_verify_ecdsa_test.nx.
104func main() -> i64 {
105 return 0
106}