code wiki / (root) / nx_acme_directory_live.nx

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}