code wiki / (root) / nx_x509_leaf_emit.nx

nx_x509_leaf_emit.nx source

↩ module page · 197 lines · 10579 B

1// nx_x509_leaf_emit.nx -- self-signed Ed25519 X.509 leaf-certificate emitter (RFC 5280 + RFC 8410). 2// 3// mTLS rung R4 of the NO-COOKIE session carrier. Mints the CLIENT IDENTITY certificate the browser 4// presents at the TLS layer: a self-signed Ed25519 leaf whose Subject CN carries the sovereign identity 5// (e.g. hex(user_id_hash)). R5 derives the keypair from the OPAQUE export_key + packages this cert as 6// PKCS#12; R3 verifies a presented cert's CertificateVerify against the pubkey parsed out of it; R6 reads 7// the Subject CN -> handle -> HR entitlements. 8// 9// Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue BIT STRING } 10// TBSCertificate ::= SEQUENCE { [0]{INTEGER v3(2)}, serial INTEGER, sigAlg, issuer Name, validity, 11// subject Name, subjectPublicKeyInfo } (extensions omitted -- OPTIONAL) 12// Self-signed: issuer == subject; signatureValue = Ed25519 over DER(tbsCertificate). 13// 14// SYSCALL-VARIANT NOTE (Rule 23 -- comment the WHY): the x509 VERIFY stack (nx_x509 / nx_x509_validate / 15// nx_ed25519_signature) imports nx_syscalls.nx, but nx_x509_csr_emit imports nx_syscalls_x86_64.nx -- 16// importing both = duplicate sys_* link error. So this file stays on nx_syscalls.nx and reimplements the 17// few small ASN.1 Name/SPKI helpers inline (pure nxae_ compositions) rather than import the CSR emitter. 18// Composes nx_asn1_emit (DER) + nx_ed25519_signature (sign). license_tier: ORIGINAL expect_exit: 0 19import "nx_syscalls.nx" 20import "nx_asn1_emit.nx" 21import "nx_ed25519_signature.nx" 22import "nx_x509.nx" 23import "nx_x509_validate.nx" 24const K_MAGIC_2048: i64 = 2048 25const K_MAGIC_4096: i64 = 4096 26 27func lf_oid_ed25519(out: *u8) -> i64 { out[0] = 0x2b as u8; out[1] = 0x65 as u8; out[2] = 0x70 as u8; return 3 } 28func lf_oid_cn(out: *u8) -> i64 { out[0] = 0x55 as u8; out[1] = 0x04 as u8; out[2] = 0x03 as u8; return 3 } 29 30// SubjectPublicKeyInfo for Ed25519: SEQUENCE { SEQUENCE{ OID id-Ed25519 }, BIT STRING pubkey }. 0 ok / 1 err. 31func lf_emit_spki_ed25519(out: *u8, off: *i64, cap: i64, pubkey_32: *u8) -> i64 { 32 let oid: *u8 = sys_mmap(8); let oid_n: i64 = lf_oid_ed25519(oid) 33 let alg: *u8 = sys_mmap(32); let alg_off: *i64 = sys_mmap(8) as *i64; alg_off[0] = 0 34 if nxae_put_oid(alg, alg_off, 32, oid, oid_n) != NXAE_OK { return 1 } 35 let spki: *u8 = sys_mmap(128); let spki_off: *i64 = sys_mmap(8) as *i64; spki_off[0] = 0 36 if nxae_put_tlv(spki, spki_off, 128, 0x30, alg, alg_off[0]) != NXAE_OK { return 1 } 37 if nxae_put_bit_string(spki, spki_off, 128, 0, pubkey_32, 32) != NXAE_OK { return 1 } 38 if nxae_put_tlv(out, off, cap, 0x30, spki, spki_off[0]) != NXAE_OK { return 1 } 39 return 0 40} 41 42// Name with a single commonName: SEQUENCE { SET { SEQUENCE { OID 2.5.4.3, UTF8String cn } } }. 0 ok / 1 err. 43func lf_emit_name_cn(out: *u8, off: *i64, cap: i64, cn: *u8, cn_n: i64) -> i64 { 44 let oid: *u8 = sys_mmap(8); let oid_n: i64 = lf_oid_cn(oid) 45 let atv: *u8 = sys_mmap(256); let atv_off: *i64 = sys_mmap(8) as *i64; atv_off[0] = 0 46 if nxae_put_oid(atv, atv_off, 256, oid, oid_n) != NXAE_OK { return 1 } 47 if nxae_put_utf8(atv, atv_off, 256, cn, cn_n) != NXAE_OK { return 1 } 48 let rdn: *u8 = sys_mmap(384); let rdn_off: *i64 = sys_mmap(8) as *i64; rdn_off[0] = 0 49 if nxae_put_tlv(rdn, rdn_off, 384, 0x30, atv, atv_off[0]) != NXAE_OK { return 1 } 50 let set: *u8 = sys_mmap(512); let set_off: *i64 = sys_mmap(8) as *i64; set_off[0] = 0 51 if nxae_put_tlv(set, set_off, 512, 0x31, rdn, rdn_off[0]) != NXAE_OK { return 1 } 52 if nxae_put_tlv(out, off, cap, 0x30, set, set_off[0]) != NXAE_OK { return 1 } 53 return 0 54} 55 56// signature AlgorithmIdentifier: SEQUENCE { OID id-Ed25519 } (no parameters per RFC 8410). 0 ok / 1 err. 57func lf_emit_sigalg(out: *u8, off: *i64, cap: i64) -> i64 { 58 let oid: *u8 = sys_mmap(8); let oid_n: i64 = lf_oid_ed25519(oid) 59 let s: *u8 = sys_mmap(32); let so: *i64 = sys_mmap(8) as *i64; so[0] = 0 60 if nxae_put_oid(s, so, 32, oid, oid_n) != NXAE_OK { return 1 } 61 if nxae_put_tlv(out, off, cap, 0x30, s, so[0]) != NXAE_OK { return 1 } 62 return 0 63} 64 65// validity SEQUENCE { UTCTime notBefore, UTCTime notAfter }; each a 13-char "YYMMDDHHMMSSZ". 0 ok / 1 err. 66func lf_emit_validity(out: *u8, off: *i64, cap: i64, nb_13: *u8, na_13: *u8) -> i64 { 67 let v: *u8 = sys_mmap(64); let vo: *i64 = sys_mmap(8) as *i64; vo[0] = 0 68 if nxae_put_tlv(v, vo, 64, 0x17, nb_13, 13) != NXAE_OK { return 1 } 69 if nxae_put_tlv(v, vo, 64, 0x17, na_13, 13) != NXAE_OK { return 1 } 70 if nxae_put_tlv(out, off, cap, 0x30, v, vo[0]) != NXAE_OK { return 1 } 71 return 0 72} 73 74// Emit a self-signed Ed25519 leaf certificate. serial must be a positive INTEGER (first byte < 0x80). 75// not_before / not_after are 13-byte UTCTime strings. Returns 0 on success, negative on error; out_n set. 76func nx_x509_leaf_emit_ed25519( 77 priv_32: *u8, pubkey_32: *u8, 78 cn: *u8, cn_n: i64, 79 serial: *u8, serial_n: i64, 80 not_before: *u8, not_after: *u8, 81 out: *u8, cap: i64, out_n: *i64 82) -> i64 { 83 if (priv_32 as i64) == 0 { return 0 - 1 } 84 if (pubkey_32 as i64) == 0 { return 0 - 1 } 85 if cn_n <= 0 { return 0 - 1 } 86 if serial_n <= 0 { return 0 - 1 } 87 88 // ---- TBSCertificate body ---- 89 let tbs: *u8 = sys_mmap(K_MAGIC_2048); let to: *i64 = sys_mmap(8) as *i64; to[0] = 0 90 // version [0] EXPLICIT { INTEGER 2 } (v3) 91 let ver: *u8 = sys_mmap(16); let vo: *i64 = sys_mmap(8) as *i64; vo[0] = 0 92 if nxae_put_int_u8(ver, vo, 16, 2) != NXAE_OK { return 0 - 1 } 93 if nxae_put_context_explicit(tbs, to, K_MAGIC_2048, 0, ver, vo[0]) != NXAE_OK { return 0 - 1 } 94 // serialNumber INTEGER 95 if nxae_put_tlv(tbs, to, K_MAGIC_2048, 0x02, serial, serial_n) != NXAE_OK { return 0 - 1 } 96 // signature AlgorithmIdentifier 97 if lf_emit_sigalg(tbs, to, K_MAGIC_2048) != 0 { return 0 - 1 } 98 // issuer (self-signed: == subject) 99 if lf_emit_name_cn(tbs, to, K_MAGIC_2048, cn, cn_n) != 0 { return 0 - 1 } 100 // validity 101 if lf_emit_validity(tbs, to, K_MAGIC_2048, not_before, not_after) != 0 { return 0 - 1 } 102 // subject 103 if lf_emit_name_cn(tbs, to, K_MAGIC_2048, cn, cn_n) != 0 { return 0 - 1 } 104 // subjectPublicKeyInfo 105 if lf_emit_spki_ed25519(tbs, to, K_MAGIC_2048, pubkey_32) != 0 { return 0 - 1 } 106 107 // ---- wrap TBS in SEQUENCE (this DER is what gets signed + hashed) ---- 108 let tbsw: *u8 = sys_mmap(K_MAGIC_2048); let tw: *i64 = sys_mmap(8) as *i64; tw[0] = 0 109 if nxae_put_tlv(tbsw, tw, K_MAGIC_2048, 0x30, tbs, to[0]) != NXAE_OK { return 0 - 1 } 110 111 // ---- sign DER(TBSCertificate) ---- 112 let sig: *u8 = sys_mmap(64) 113 if ed25519_sign_full(priv_32, tbsw, tw[0], sig) != 0 { return 0 - 2 } 114 115 // ---- Certificate = SEQUENCE { TBS, sigAlg, BIT STRING(sig) } ---- 116 let cc: *u8 = sys_mmap(K_MAGIC_4096); let co: *i64 = sys_mmap(8) as *i64; co[0] = 0 117 if nxae_put_bytes(cc, co, K_MAGIC_4096, tbsw, tw[0]) != NXAE_OK { return 0 - 1 } // TBS (already DER) 118 if lf_emit_sigalg(cc, co, K_MAGIC_4096) != 0 { return 0 - 1 } 119 if nxae_put_bit_string(cc, co, K_MAGIC_4096, 0, sig, 64) != NXAE_OK { return 0 - 1 } 120 var fo: i64 = 0 121 if nxae_put_tlv(out, &fo, cap, 0x30, cc, co[0]) != NXAE_OK { return 0 - 1 } 122 out_n[0] = fo 123 return 0 124} 125 126// ===== in-process gate: emit -> parse -> self-verify -> embedded-pubkey -> negative cases ===== 127func lf_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 128func lf_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 129func lf_row(name: *u8, ok: i64) -> i64 { if ok == 1 { lf_w(" PASS " as *u8) } else { lf_w(" FAIL " as *u8) } lf_w(name); lf_w("\n" as *u8); return ok } 130 131func main() -> i64 { 132 lf_w("nx_x509 leaf-cert emit/verify gate (RFC 5280; self-signed Ed25519 identity; mTLS rung R4)\n" as *u8) 133 // deterministic test seed -> derive the matching Ed25519 public key 134 let seed: *u8 = sys_mmap(32) 135 var i: i64 = 0 136 while i < 32 { seed[i] = (0x11 + i) as u8; i = i + 1 } 137 let pub: *u8 = sys_mmap(32) 138 ed25519_pub_from_priv(seed, pub) 139 140 let cn: *u8 = "nishi-uid-2f9c4b7e10a8d6c3e5f10293" as *u8 // stand-in identity (R5 uses hex(user_id_hash)) 141 let cn_n: i64 = lf_len(cn) 142 let serial: *u8 = sys_mmap(8) 143 serial[0] = 0x42 as u8; serial[1] = 0x13 as u8; serial[2] = 0x37 as u8; serial[3] = 0x01 as u8 144 serial[4] = 0x02 as u8; serial[5] = 0x03 as u8; serial[6] = 0x04 as u8; serial[7] = 0x05 as u8 145 let nb: *u8 = "260101000000Z" as *u8 146 let na: *u8 = "360101000000Z" as *u8 147 148 let cert: *u8 = sys_mmap(K_MAGIC_4096) 149 let clen: *i64 = sys_mmap(8) as *i64 150 let rc: i64 = nx_x509_leaf_emit_ed25519(seed, pub, cn, cn_n, serial, 8, nb, na, cert, K_MAGIC_4096, clen) 151 var pass: i64 = 0 152 153 var t1: i64 = 0 154 if rc == 0 { if clen[0] > 64 { t1 = 1 } } 155 pass = pass + lf_row("T1 leaf cert emitted (rc==0, plausible DER length)" as *u8, t1) 156 157 let xc: *X509Cert = sys_mmap(256) as *X509Cert 158 let pr: i64 = x509_parse(cert, clen[0], xc) 159 var t2: i64 = 0 160 if pr == 0 { t2 = 1 } 161 pass = pass + lf_row("T2 our own X.509 parser accepts the emitted cert" as *u8, t2) 162 163 var t3: i64 = 0 164 if x509_verify_signature_ed25519(cert, xc, pub) == NX_X509_VAL_OK { t3 = 1 } 165 pass = pass + lf_row("T3 self-signature verifies under the cert's own Ed25519 key" as *u8, t3) 166 167 var t4: i64 = 0 168 if xc.pubkey_len == 32 { 169 t4 = 1 170 var j: i64 = 0 171 while j < 32 { if cert[xc.pubkey_off + j] != pub[j] { t4 = 0; j = 32 } else { j = j + 1 } } 172 } 173 pass = pass + lf_row("T4 SubjectPublicKeyInfo embeds the exact identity pubkey (extractable for authz)" as *u8, t4) 174 175 // T5: a different signer pubkey must NOT verify 176 let pub2: *u8 = sys_mmap(32) 177 var k: i64 = 0 178 while k < 32 { pub2[k] = pub[k]; k = k + 1 } 179 pub2[0] = (pub2[0] ^ 1) as u8 180 var t5: i64 = 0 181 if x509_verify_signature_ed25519(cert, xc, pub2) != NX_X509_VAL_OK { t5 = 1 } 182 pass = pass + lf_row("T5 wrong signer pubkey rejected" as *u8, t5) 183 184 // T6: tampering a TBS byte must break the signature 185 cert[xc.tbs_off + 6] = (cert[xc.tbs_off + 6] ^ 0x40) as u8 186 var t6: i64 = 0 187 if x509_verify_signature_ed25519(cert, xc, pub) != NX_X509_VAL_OK { t6 = 1 } 188 pass = pass + lf_row("T6 tampered tbsCertificate byte rejected" as *u8, t6) 189 190 if pass == 6 { 191 lf_w("NX-X509-LEAF-EMIT GATE GREEN 6/6 (sovereign Ed25519 identity cert; verifies on our own X.509 stack)\n" as *u8) 192 sys_exit(0) 193 } 194 lf_w("NX-X509-LEAF-EMIT GATE RED\n" as *u8) 195 sys_exit(1) 196 return 1 197}