nx_acme_directory_live.nx source
↩ module page · 109 lines · 4973 B
1// nx_acme_directory_live.nx -- FIRST live ACME network step: GET the
2// Let's Encrypt directory over the sovereign TLS 1.3 client and print the
3// JSON. Unauthenticated + not rate-limited (it is the API entry point),
4// so it is the safe bottom-up milestone: it proves TLS to LE (cert chains
5// to ISRG Root X1, present in the Mozilla store) + HTTP parse + gives real
6// directory JSON to build the URL extractor against.
7//
8// Reuses the proven live-HTTPS path (nx_https_get_live_real_ca_test):
9// load trust store -> url_for_fetch -> url_connect -> session_run ->
10// get_complete -> http_response_parse.
11//
12// Default target: STAGING is "acme-staging-v02.api.letsencrypt.org" but
13// staging chains to the "(STAGING) Pretend Pear X1" root which is NOT in
14// the Mozilla bundle -> validation would fail. PRODUCTION directory GET
15// is harmless + Mozilla-trusted, so we hit production here just to read
16// endpoint URLs; actual issuance picks the env later.
17//
18// license_tier: ORIGINAL (composes the shipped TLS client + HTTP parse)
19
20import "nx_syscalls.nx"
21import "nx_x509_trust_store.nx"
22import "nx_trust_store_load_from_certdata.nx"
23import "nx_tls13_client_validate_certificate.nx"
24import "nx_tls13_client_session_run.nx"
25import "nx_https_url_for_fetch.nx"
26import "nx_https_url_connect.nx"
27import "nx_https_get_complete.nx"
28import "nx_http_response_parse.nx"
29import "nx_csprng.nx"
30const K_MAGIC_4194304: i64 = 4194304
31const K_MAGIC_1779284141: i64 = 1779284141
32const K_MAGIC_65536: i64 = 65536
33const K_MAGIC_16384: i64 = 16384
34
35func main() -> i64 {
36 // ---- trust store from Mozilla certdata.txt ----
37 let cdpath: *u8 = "/tmp/mozilla_certdata.txt\x00"
38 let lr: i64 = nx_trust_store_load_from_certdata(cdpath, 300, K_MAGIC_4194304)
39 if lr <= 0 { sys_write(2, "trust load FAIL\n" as *u8, 16); return 1 }
40 let store: *TrustStore = lr as *TrustStore
41 if trust_store_count(store) < 50 { sys_write(2, "too few CAs\n" as *u8, 12); return 2 }
42
43 // ---- target: LE production directory ----
44 let url: *u8 = "https://acme-v02.api.letsencrypt.org/directory\x00"
45 let url_p: *NxUrl = nx_url_new()
46 let target_raw: *u8 = sys_mmap(32)
47 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget
48 target.url = url_p
49 target.port = 0
50 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { sys_write(2, "url FAIL\n" as *u8, 9); return 3 }
51
52 let fd_p: *i64 = sys_mmap(16) as *i64
53 if nx_https_url_connect(target, url, K_MAGIC_1779284141, fd_p) != NX_HTTPS_CONNECT_OK { sys_write(2, "connect FAIL\n" as *u8, 13); return 4 }
54 let fd: i64 = *fd_p
55
56 // ephemeral client random + key share scalar (handshake-only)
57 let cr: *u8 = sys_mmap(32)
58 let priv: *u8 = sys_mmap(32)
59 var i: i64 = 0
60 nx_csprng_fill(cr, 32); nx_csprng_fill(priv, 32) // CWE-330 (debt 1785970852): were the constants 0x11../0x31.. on EVERY session
61
62 let val_ctx_raw: *u8 = sys_mmap(64)
63 let val_ctx: *TlsValidationContext = val_ctx_raw as *TlsValidationContext
64 val_ctx.store = store
65 val_ctx.sni_host = url + target.url.host_off
66 val_ctx.sni_host_len = target.url.host_len
67 val_ctx.now_epoch = sys_now_realtime_sec() // REAL clock: hardcoded epochs rot when live sites renew certs (B1 root cause)
68
69 let sr: i64 = nx_tls13_client_session_run(fd, url + target.url.host_off, target.url.host_len, cr, priv, val_ctx)
70 if sr <= 0 { sys_close(fd); sys_write(2, "TLS handshake FAIL\n" as *u8, 19); return 200 + (0 - sr) }
71 let session: *Tls13ClientSession = sr as *Tls13ClientSession
72
73 // ---- GET /directory ----
74 let buf: *u8 = sys_mmap(K_MAGIC_65536)
75 let gc: i64 = nx_https_get_complete(session, fd,
76 url + target.url.path_off, target.url.path_len,
77 url + target.url.host_off, target.url.host_len,
78 buf, K_MAGIC_65536)
79 sys_close(fd)
80 if gc < 0 { sys_write(2, "get_complete FAIL\n" as *u8, 18); return 100 + (0 - gc) }
81
82 let pr: *i64 = sys_mmap(128) as *i64
83 if nx_http_response_parse(buf, gc, pr) != 0 { sys_write(2, "parse FAIL\n" as *u8, 11); return 50 }
84 let status: i64 = pr[1]
85 let body_off: i64 = pr[6]
86 let body_kind: i64 = pr[8]
87
88 // status line for the operator
89 sys_write(1, "LE directory HTTP status: " as *u8, 26)
90 let sb: *u8 = sys_mmap(8)
91 sb[0] = (0x30 + (status / 100)) as u8
92 sb[1] = (0x30 + ((status / 10) % 10)) as u8
93 sb[2] = (0x30 + (status % 10)) as u8
94 sb[3] = 0x0A as u8
95 sys_write(1, sb, 4)
96 if status != 200 { return 51 }
97
98 // body (directory JSON). kind 2 == chunked.
99 if body_kind == 2 {
100 let html: *u8 = sys_mmap(K_MAGIC_16384)
101 let hn: i64 = nx_http_dechunk(buf + body_off, gc - body_off, html, K_MAGIC_16384)
102 if hn < 0 { return 60 }
103 sys_write(1, html, hn)
104 } else {
105 sys_write(1, buf + body_off, gc - body_off)
106 }
107 sys_write(1, "\n" as *u8, 1)
108 return 0
109}