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}