code wiki / _hdl_build / nx_nginx_cert_sync.nx
nx_nginx_cert_sync.nx source
↩ module page · 233 lines · 8278 B
1// nx_nginx_cert_sync.nx -- feed the AUTORENEWED Route-B wildcard cert into the DSM-nginx cert store
2// (single renewal pipeline, many consumers -- DRY). The nginx nishi-wildcard PEMs went stale (Mar 30
3// issue -> expired ~Jun 28) while nx_cert_autorenew kept the sites.elf DER fresh (Jun 29): chat/matrix/
4// mfg/staging.nishifamily.com all served an EXPIRED cert. This organ converts the fresh DER chain +
5// raw P-256 key into the PEM files nginx needs. Caller (aw_sudo cmd) wraps it with backup + nginx -t
6// gate + reload + restore-on-fail (never-brick).
7// usage: nx_nginx_cert_sync <fullchain.der> <key32.bin> <outdir>
8// writes: <outdir>/fullchain.pem <outdir>/cert.pem <outdir>/privkey.pem
9// license_tier: ORIGINAL
10import "nx_syscalls.nx"
11const K_MAGIC_65536: i64 = 65536
12const K_MAGIC_131072: i64 = 131072
13const K_MAGIC_4096: i64 = 4096
14const K_MAGIC_1024: i64 = 1024
15
16func cs_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17func cs_putn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
18func cs_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
19
20func cs_read(path: *u8, buf: *u8, cap: i64) -> i64 {
21 let fd: i64 = sys_openat_rd(path)
22 if fd < 0 { return 0 - 1 }
23 var off: i64 = 0
24 var go: i64 = 1
25 while go == 1 {
26 if off >= cap { go = 0 }
27 else {
28 let dst: *u8 = buf + off
29 let r: i64 = sys_read(fd, dst, cap - off)
30 if r <= 0 { go = 0 } else { off = off + r }
31 }
32 }
33 sys_close(fd)
34 return off
35}
36
37// append standard base64 of src[0..n) into out at *oo, wrapped at 64 chars per line
38func cs_b64(src: *u8, n: i64, out: *u8, oo: *i64) -> i64 {
39 let alpha: *u8 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" as *u8
40 var o: i64 = oo[0]
41 var i: i64 = 0
42 var col: i64 = 0
43 while i < n {
44 var b0: i64 = src[i] as i64
45 b0 = b0 & 0xff
46 var b1: i64 = 0
47 var b2: i64 = 0
48 var have: i64 = 1
49 if i + 1 < n { b1 = (src[i+1] as i64) & 0xff; have = 2 }
50 if i + 2 < n { b2 = (src[i+2] as i64) & 0xff; have = 3 }
51 let v: i64 = (b0 << 16) | (b1 << 8) | b2
52 out[o] = alpha[(v >> 18) & 63]
53 o = o + 1
54 out[o] = alpha[(v >> 12) & 63]
55 o = o + 1
56 if have >= 2 { out[o] = alpha[(v >> 6) & 63] } else { out[o] = 61 as u8 }
57 o = o + 1
58 if have >= 3 { out[o] = alpha[v & 63] } else { out[o] = 61 as u8 }
59 o = o + 1
60 col = col + 4
61 if col >= 64 { out[o] = 10 as u8; o = o + 1; col = 0 }
62 i = i + 3
63 }
64 if col > 0 { out[o] = 10 as u8; o = o + 1 }
65 oo[0] = o
66 return 0
67}
68
69func cs_append(out: *u8, oo: *i64, s: *u8) -> i64 {
70 var o: i64 = oo[0]
71 var i: i64 = 0
72 while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 }
73 oo[0] = o
74 return 0
75}
76
77// one PEM certificate block from DER
78func cs_pem_cert(der: *u8, n: i64, out: *u8, oo: *i64) -> i64 {
79 cs_append(out, oo, "-----BEGIN CERTIFICATE-----\n" as *u8)
80 cs_b64(der, n, out, oo)
81 cs_append(out, oo, "-----END CERTIFICATE-----\n" as *u8)
82 return 0
83}
84
85func cs_write(path: *u8, buf: *u8, n: i64) -> i64 {
86 let fd: i64 = sys_openat_wr(path, 0x1A4) // 0644
87 if fd < 0 { return 0 - 1 }
88 var off: i64 = 0
89 while off < n {
90 let src: *u8 = buf + off
91 let w: i64 = sys_write(fd, src, n - off)
92 if w <= 0 { sys_close(fd); return 0 - 1 }
93 off = off + w
94 }
95 sys_close(fd)
96 return n
97}
98
99// build path <dir>/<name> into out
100func cs_path(dir: *u8, name: *u8, out: *u8) -> i64 {
101 var o: i64 = 0
102 var i: i64 = 0
103 while dir[i] != (0 as u8) { out[o] = dir[i]; o = o + 1; i = i + 1 }
104 out[o] = 47 as u8
105 o = o + 1
106 i = 0
107 while name[i] != (0 as u8) { out[o] = name[i]; o = o + 1; i = i + 1 }
108 out[o] = 0 as u8
109 return o
110}
111
112func main(argc: i64, argv: *i64) -> i64 {
113 if argc < 4 { cs_puts("usage: nx_nginx_cert_sync <fullchain.der> <key32.bin> <outdir>\n" as *u8); return 2 }
114 let derp: *u8 = argv[1] as *u8
115 let keyp: *u8 = argv[2] as *u8
116 let outd: *u8 = argv[3] as *u8
117
118 let der: *u8 = sys_mmap(K_MAGIC_65536)
119 let dn: i64 = cs_read(derp, der, K_MAGIC_65536)
120 if dn <= 0 { cs_puts("FAIL read fullchain.der\n" as *u8); return 1 }
121 let key: *u8 = sys_mmap(64)
122 let kn: i64 = cs_read(keyp, key, 64)
123 if kn != 32 { cs_puts("FAIL key32.bin: expected exactly 32 bytes, got " as *u8); cs_putn(kn); cs_puts("\n" as *u8); return 1 }
124
125 // ---- split the concatenated DER chain (each cert: 30 82 hi lo ...) and emit PEM blocks ----
126 let full: *u8 = sys_mmap(K_MAGIC_131072)
127 let fo: *i64 = sys_mmap(8) as *i64
128 fo[0] = 0
129 let leaf: *u8 = sys_mmap(K_MAGIC_131072)
130 let lo: *i64 = sys_mmap(8) as *i64
131 lo[0] = 0
132 var off: i64 = 0
133 var certs: i64 = 0
134 var go: i64 = 1
135 while go == 1 {
136 if off + 4 > dn { go = 0 }
137 else {
138 let t0: i64 = (der[off] as i64) & 0xff
139 let t1: i64 = (der[off+1] as i64) & 0xff
140 if t0 != 0x30 { go = 0 }
141 else { if t1 != 0x82 { go = 0 }
142 else {
143 let hi: i64 = (der[off+2] as i64) & 0xff
144 let lw: i64 = (der[off+3] as i64) & 0xff
145 let total: i64 = 4 + ((hi << 8) | lw)
146 if off + total > dn { go = 0 }
147 else {
148 let one: *u8 = der + off
149 cs_pem_cert(one, total, full, fo)
150 if certs == 0 { cs_pem_cert(one, total, leaf, lo) }
151 certs = certs + 1
152 off = off + total
153 }
154 } }
155 }
156 }
157 if certs < 1 { cs_puts("FAIL: no DER certs parsed from fullchain\n" as *u8); return 1 }
158 cs_puts("parsed DER chain: certs=" as *u8)
159 cs_putn(certs)
160 cs_puts(" bytes=" as *u8)
161 cs_putn(off)
162 cs_puts("\n" as *u8)
163
164 // ---- SEC1 ECPrivateKey DER for a raw P-256 scalar (named-curve prime256v1, pubkey omitted) ----
165 // SEQUENCE(0x31) { INTEGER 1; OCTET STRING(32); [0]{ OID 1.2.840.10045.3.1.7 } }
166 let kd: *u8 = sys_mmap(128)
167 var ko: i64 = 0
168 kd[ko] = 0x30 as u8
169 ko = ko + 1
170 kd[ko] = 0x31 as u8
171 ko = ko + 1
172 kd[ko] = 0x02 as u8
173 ko = ko + 1
174 kd[ko] = 0x01 as u8
175 ko = ko + 1
176 kd[ko] = 0x01 as u8
177 ko = ko + 1
178 kd[ko] = 0x04 as u8
179 ko = ko + 1
180 kd[ko] = 0x20 as u8
181 ko = ko + 1
182 var ki: i64 = 0
183 while ki < 32 { kd[ko] = key[ki]; ko = ko + 1; ki = ki + 1 }
184 kd[ko] = 0xa0 as u8
185 ko = ko + 1
186 kd[ko] = 0x0a as u8
187 ko = ko + 1
188 kd[ko] = 0x06 as u8
189 ko = ko + 1
190 kd[ko] = 0x08 as u8
191 ko = ko + 1
192 kd[ko] = 0x2a as u8
193 ko = ko + 1
194 kd[ko] = 0x86 as u8
195 ko = ko + 1
196 kd[ko] = 0x48 as u8
197 ko = ko + 1
198 kd[ko] = 0xce as u8
199 ko = ko + 1
200 kd[ko] = 0x3d as u8
201 ko = ko + 1
202 kd[ko] = 0x03 as u8
203 ko = ko + 1
204 kd[ko] = 0x01 as u8
205 ko = ko + 1
206 kd[ko] = 0x07 as u8
207 ko = ko + 1
208 let kpem: *u8 = sys_mmap(K_MAGIC_4096)
209 let kpo: *i64 = sys_mmap(8) as *i64
210 kpo[0] = 0
211 cs_append(kpem, kpo, "-----BEGIN EC PRIVATE KEY-----\n" as *u8)
212 cs_b64(kd, ko, kpem, kpo)
213 cs_append(kpem, kpo, "-----END EC PRIVATE KEY-----\n" as *u8)
214
215 // ---- write the three PEMs into outdir ----
216 let pbuf: *u8 = sys_mmap(K_MAGIC_1024)
217 cs_path(outd, "fullchain.pem" as *u8, pbuf)
218 if cs_write(pbuf, full, fo[0]) < 0 { cs_puts("FAIL write fullchain.pem\n" as *u8); return 1 }
219 cs_path(outd, "cert.pem" as *u8, pbuf)
220 if cs_write(pbuf, leaf, lo[0]) < 0 { cs_puts("FAIL write cert.pem\n" as *u8); return 1 }
221 cs_path(outd, "privkey.pem" as *u8, pbuf)
222 if cs_write(pbuf, kpem, kpo[0]) < 0 { cs_puts("FAIL write privkey.pem\n" as *u8); return 1 }
223 cs_puts("SYNCED: fullchain.pem(" as *u8)
224 cs_putn(fo[0])
225 cs_puts("B) cert.pem(" as *u8)
226 cs_putn(lo[0])
227 cs_puts("B) privkey.pem(" as *u8)
228 cs_putn(kpo[0])
229 cs_puts("B) -> " as *u8)
230 cs_puts(outd)
231 cs_puts("\n" as *u8)
232 return 0
233}