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}