code wiki / (root) / nx_x509_csr_emit.nx

nx_x509_csr_emit.nx source

↩ module page · 302 lines · 11142 B

1// nx_x509_csr_emit.nx -- PKCS#10 CSR emitter for Ed25519 (RFC 2986 + RFC 8410). 2// 3// Closes the CSR-emit gap for ACME finalize step. Composes: 4// nx_asn1_emit.nx -- DER encoder helpers 5// nx_ed25519_signature.nx -- ed25519_sign_full over TBS bytes 6// 7// PKCS#10 CertificationRequest structure (RFC 2986 §4.2): 8// 9// CertificationRequest ::= SEQUENCE { 10// certificationRequestInfo CertificationRequestInfo, 11// signatureAlgorithm AlgorithmIdentifier, 12// signature BIT STRING 13// } 14// 15// CertificationRequestInfo ::= SEQUENCE { 16// version INTEGER (0 for PKCS#10 v1.0), 17// subject Name, 18// subjectPKInfo SubjectPublicKeyInfo, 19// attributes [0] IMPLICIT Attributes 20// } 21// 22// SubjectPublicKeyInfo ::= SEQUENCE { 23// algorithm AlgorithmIdentifier, 24// subjectPublicKey BIT STRING 25// } 26// 27// Per RFC 8410 §4, Ed25519 SubjectPublicKeyInfo has: 28// - algorithm.algorithm = id-Ed25519 (1.3.101.112) 29// - algorithm.parameters absent (no NULL placeholder) 30// - subjectPublicKey = 32-byte raw public key 31// 32// And RFC 8410 §6: the signatureAlgorithm in the outer CSR is 33// identical (id-Ed25519, no parameters). 34// 35// Per cardinal feedback-no-third-party-trust-native-or-nothing: 36// substrate's own CSR emitter; no openssl req, no certbot-builtin. 37// 38// nx_capability_claims: 39// needs: [asn1_der_encode, ed25519_sign] 40// provides: [pkcs10_csr_eddsa_emit, subject_pubkey_info_ed25519] 41// safety: [no_unchecked_deref, no_floating_point, bounded_buffer] 42// verdict: [sealed_enum_5_state] 43// license: ORIGINAL 44// kind: racing_crew_specialist 45// layer: L4 (composite over L3 ed25519 + L2 asn1_emit) 46 47import "nx_syscalls_x86_64.nx" 48import "nx_asn1_emit.nx" 49import "nx_ed25519_signature.nx" 50 51// ---- Sealed enum: CSR verdict ------------------------------------ 52 53const NXCSR_OK: i64 = 0 54const NXCSR_OOM_BUFFER: i64 = 1 55const NXCSR_BAD_KEY_LEN: i64 = 2 56const NXCSR_BAD_SUBJECT: i64 = 3 57const NXCSR_SIGN_ERR: i64 = 4 58const NXCSR_BAD_ARG: i64 = 5 59const NXCSR_VERDICT_N: i64 = 6 60 61func nxcsr_verdict_is_valid(v: i64) -> i64 { 62 if v < 0 { return 0 } 63 if v >= NXCSR_VERDICT_N { return 0 } 64 return 1 65} 66 67func nxcsr_verdict_name(v: i64) -> *u8 { 68 if v == NXCSR_OK { return "OK" as *u8 } 69 if v == NXCSR_OOM_BUFFER { return "OOM_BUFFER" as *u8 } 70 if v == NXCSR_BAD_KEY_LEN { return "BAD_KEY_LEN" as *u8 } 71 if v == NXCSR_BAD_SUBJECT { return "BAD_SUBJECT" as *u8 } 72 if v == NXCSR_SIGN_ERR { return "SIGN_ERR" as *u8 } 73 if v == NXCSR_BAD_ARG { return "BAD_ARG" as *u8 } 74 return "INVALID" as *u8 75} 76 77// ---- OID byte literals ------------------------------------------- 78// 79// id-Ed25519 (RFC 8410 §3): 1.3.101.112 80// Encoded as: 0x2b, 0x65, 0x70 (3 bytes) 81// 82// commonName (RFC 5280 §4.1.2.4): 2.5.4.3 83// Encoded as: 0x55, 0x04, 0x03 (3 bytes) 84 85func nx_csr_oid_ed25519(out: *u8) -> i64 { 86 out[0] = 0x2b as u8 87 out[1] = 0x65 as u8 88 out[2] = 0x70 as u8 89 return 3 90} 91 92func nx_csr_oid_common_name(out: *u8) -> i64 { 93 out[0] = 0x55 as u8 94 out[1] = 0x04 as u8 95 out[2] = 0x03 as u8 96 return 3 97} 98 99// ---- SubjectPublicKeyInfo for Ed25519 ---------------------------- 100// 101// SEQUENCE { 102// SEQUENCE { -- AlgorithmIdentifier 103// OID 1.3.101.112 (id-Ed25519, no parameters per RFC 8410 §3) 104// } 105// BIT STRING (0 unused bits, 32 bytes pubkey) 106// } 107// 108// Total size: 12 bytes prefix + 32 bytes key = 44 bytes typical. 109 110func nx_csr_emit_subject_pubkey_info( 111 out: *u8, off: *i64, cap: i64, 112 pubkey_32: *u8) -> i64 { 113 if out == (0 as *u8) { return NXCSR_BAD_ARG } 114 if off == (0 as *i64) { return NXCSR_BAD_ARG } 115 if pubkey_32 == (0 as *u8) { return NXCSR_BAD_KEY_LEN } 116 if cap <= 0 { return NXCSR_BAD_ARG } 117 118 // Build the AlgorithmIdentifier into a scratch buffer first. 119 // OID 1.3.101.112 -> {0x06, 0x03, 0x2b, 0x65, 0x70} = 5 bytes 120 let oid: *u8 = sys_mmap(8) 121 let oid_n: i64 = nx_csr_oid_ed25519(oid) 122 123 let alg_scratch: *u8 = sys_mmap(32) 124 let alg_off: *i64 = sys_mmap(8) as *i64 125 alg_off[0] = 0 126 let r1: i64 = nxae_put_oid(alg_scratch, alg_off, 32, oid, oid_n) 127 if r1 != NXAE_OK { return NXCSR_OOM_BUFFER } 128 129 // Build SubjectPublicKeyInfo content into scratch. 130 let spki_inner: *u8 = sys_mmap(128) 131 let spki_off: *i64 = sys_mmap(8) as *i64 132 spki_off[0] = 0 133 // SEQUENCE (AlgorithmIdentifier) 134 let r2: i64 = nxae_put_tlv(spki_inner, spki_off, 128, 135 0x30, alg_scratch, alg_off[0]) 136 if r2 != NXAE_OK { return NXCSR_OOM_BUFFER } 137 // BIT STRING (0 unused, 32 bytes pubkey) 138 let r3: i64 = nxae_put_bit_string(spki_inner, spki_off, 128, 139 0, pubkey_32, 32) 140 if r3 != NXAE_OK { return NXCSR_OOM_BUFFER } 141 142 // Wrap the inner content as outer SEQUENCE. 143 return nxae_put_tlv(out, off, cap, 0x30, spki_inner, spki_off[0]) 144} 145 146// ---- Subject Name for a single Common Name ----------------------- 147// 148// Name ::= SEQUENCE OF RDN -- which is SET OF AttrTypeAndValue 149// 150// SEQUENCE { 151// SET { 152// SEQUENCE { 153// OID 2.5.4.3 -- commonName 154// UTF8String "<cn>" 155// } 156// } 157// } 158 159func nx_csr_emit_subject_cn( 160 out: *u8, off: *i64, cap: i64, 161 cn: *u8, cn_n: i64) -> i64 { 162 if cn == (0 as *u8) { return NXCSR_BAD_SUBJECT } 163 if cn_n <= 0 { return NXCSR_BAD_SUBJECT } 164 165 let oid: *u8 = sys_mmap(8) 166 let oid_n: i64 = nx_csr_oid_common_name(oid) 167 168 // AttrTypeAndValue SEQUENCE { OID, UTF8String } 169 let atv_scratch: *u8 = sys_mmap(256) 170 let atv_off: *i64 = sys_mmap(8) as *i64 171 atv_off[0] = 0 172 let r1: i64 = nxae_put_oid(atv_scratch, atv_off, 256, oid, oid_n) 173 if r1 != NXAE_OK { return NXCSR_OOM_BUFFER } 174 let r2: i64 = nxae_put_utf8(atv_scratch, atv_off, 256, cn, cn_n) 175 if r2 != NXAE_OK { return NXCSR_OOM_BUFFER } 176 177 // RDN: SET { AttrTypeAndValue } 178 let rdn_scratch: *u8 = sys_mmap(384) 179 let rdn_off: *i64 = sys_mmap(8) as *i64 180 rdn_off[0] = 0 181 let r3: i64 = nxae_put_tlv(rdn_scratch, rdn_off, 384, 182 0x30, atv_scratch, atv_off[0]) 183 if r3 != NXAE_OK { return NXCSR_OOM_BUFFER } 184 185 // Subject Name: SEQUENCE { SET { ... } } 186 let set_scratch: *u8 = sys_mmap(512) 187 let set_off: *i64 = sys_mmap(8) as *i64 188 set_off[0] = 0 189 let r4: i64 = nxae_put_tlv(set_scratch, set_off, 512, 190 0x31, rdn_scratch, rdn_off[0]) 191 if r4 != NXAE_OK { return NXCSR_OOM_BUFFER } 192 193 return nxae_put_tlv(out, off, cap, 0x30, set_scratch, set_off[0]) 194} 195 196// ---- Full CSR emission (RFC 2986 + RFC 8410) --------------------- 197// 198// Caller supplies: 199// priv_32 Ed25519 private seed (32 bytes) 200// pubkey_32 Ed25519 public key (32 bytes) 201// cn Common Name (e.g., "nishifamily.com") 202// out, cap output buffer + capacity 203// 204// Returns: bytes written or negated sealed-enum verdict. 205// 206// Output structure: 207// 208// SEQUENCE { -- CertificationRequest 209// SEQUENCE { -- CertificationRequestInfo (TBS) 210// INTEGER 0, -- version 211// SEQUENCE { ... }, -- subject (CN only) 212// SEQUENCE { ... }, -- subjectPKInfo (Ed25519) 213// [0] { } -- attributes (empty) 214// }, 215// SEQUENCE { -- signatureAlgorithm 216// OID 1.3.101.112 -- id-Ed25519, no parameters 217// }, 218// BIT STRING (0 unused, 64 bytes) -- signature over TBS bytes 219// } 220 221func nx_x509_csr_emit_ed25519( 222 priv_32: *u8, pubkey_32: *u8, 223 cn: *u8, cn_n: i64, 224 out: *u8, cap: i64, out_n: *i64) -> i64 { 225 if priv_32 == (0 as *u8) { return NXCSR_BAD_KEY_LEN } 226 if pubkey_32 == (0 as *u8) { return NXCSR_BAD_KEY_LEN } 227 if cn == (0 as *u8) { return NXCSR_BAD_SUBJECT } 228 if cn_n <= 0 { return NXCSR_BAD_SUBJECT } 229 if out == (0 as *u8) { return NXCSR_BAD_ARG } 230 if out_n == (0 as *i64) { return NXCSR_BAD_ARG } 231 if cap <= 0 { return NXCSR_BAD_ARG } 232 233 // ---- Step 1: build CertificationRequestInfo (TBS) ---- 234 let tbs_buf: *u8 = sys_mmap(2048) 235 let tbs_off: *i64 = sys_mmap(8) as *i64 236 tbs_off[0] = 0 237 238 // version INTEGER 0 239 let r1: i64 = nxae_put_int_u8(tbs_buf, tbs_off, 2048, 0) 240 if r1 != NXAE_OK { return NXCSR_OOM_BUFFER } 241 // subject 242 let r2: i64 = nx_csr_emit_subject_cn(tbs_buf, tbs_off, 2048, cn, cn_n) 243 if r2 != NXCSR_OK { return r2 } 244 // subjectPKInfo 245 let r3: i64 = nx_csr_emit_subject_pubkey_info(tbs_buf, tbs_off, 2048, pubkey_32) 246 if r3 != NXCSR_OK { return r3 } 247 // attributes [0] (empty) 248 let empty: *u8 = sys_mmap(4) 249 let r4: i64 = nxae_put_context_explicit(tbs_buf, tbs_off, 2048, 250 0, empty, 0) 251 if r4 != NXAE_OK { return NXCSR_OOM_BUFFER } 252 253 // ---- Step 2: wrap TBS in SEQUENCE -> tbs_wrapped ---- 254 let tbs_wrapped: *u8 = sys_mmap(2048) 255 let tbs_w_off: *i64 = sys_mmap(8) as *i64 256 tbs_w_off[0] = 0 257 let r5: i64 = nxae_put_tlv(tbs_wrapped, tbs_w_off, 2048, 258 0x30, tbs_buf, tbs_off[0]) 259 if r5 != NXAE_OK { return NXCSR_OOM_BUFFER } 260 261 // ---- Step 3: sign TBS bytes (the WRAPPED SEQUENCE) ---- 262 let sig_64: *u8 = sys_mmap(64) 263 let sign_rc: i64 = ed25519_sign_full(priv_32, 264 tbs_wrapped, tbs_w_off[0], 265 sig_64) 266 if sign_rc != 0 { return NXCSR_SIGN_ERR } 267 268 // ---- Step 4: build signatureAlgorithm SEQUENCE { OID } ---- 269 let oid: *u8 = sys_mmap(8) 270 let oid_n: i64 = nx_csr_oid_ed25519(oid) 271 let sigalg_scratch: *u8 = sys_mmap(32) 272 let sigalg_off: *i64 = sys_mmap(8) as *i64 273 sigalg_off[0] = 0 274 let r6: i64 = nxae_put_oid(sigalg_scratch, sigalg_off, 32, oid, oid_n) 275 if r6 != NXAE_OK { return NXCSR_OOM_BUFFER } 276 277 // ---- Step 5: build outer CertificationRequest content ---- 278 let cr_content: *u8 = sys_mmap(4096) 279 let cr_off: *i64 = sys_mmap(8) as *i64 280 cr_off[0] = 0 281 // TBS wrapped SEQUENCE (already DER-encoded) 282 let r7: i64 = nxae_put_bytes(cr_content, cr_off, 4096, 283 tbs_wrapped, tbs_w_off[0]) 284 if r7 != NXAE_OK { return NXCSR_OOM_BUFFER } 285 // signatureAlgorithm SEQUENCE 286 let r8: i64 = nxae_put_tlv(cr_content, cr_off, 4096, 287 0x30, sigalg_scratch, sigalg_off[0]) 288 if r8 != NXAE_OK { return NXCSR_OOM_BUFFER } 289 // signature BIT STRING 290 let r9: i64 = nxae_put_bit_string(cr_content, cr_off, 4096, 291 0, sig_64, 64) 292 if r9 != NXAE_OK { return NXCSR_OOM_BUFFER } 293 294 // ---- Step 6: wrap as outer SEQUENCE ---- 295 var final_off: i64 = 0 296 let r10: i64 = nxae_put_tlv(out, &final_off, cap, 297 0x30, cr_content, cr_off[0]) 298 if r10 != NXAE_OK { return NXCSR_OOM_BUFFER } 299 300 *out_n = final_off 301 return NXCSR_OK 302}