code wiki / _hdl_build / nx_fw_capsule.nx

nx_fw_capsule.nx source

↩ module page · 159 lines · 7502 B

1// nx_fw_capsule.nx -- Secure-Boot-grade signed firmware capsule (sovereign ed25519). 2// 3// module: nishi-core.genealogy.fw_capsule 4// capability: CORE_COMPUTE (authenticity + integrity for firmware images and golden snapshots) 5// 6// sha256 (rungs 4-6) detects CORRUPTION but not FORGERY -- an attacker can recompute the hash over a 7// malicious image. A real capsule is SIGNED: only an image signed by the trusted PLATFORM key is 8// bootable. This is the Secure-Boot model, sovereign (our own ed25519, no vendor blob): 9// capsule "NXC1" = magic(4) + payload_len(4) + payload + ed25519_signature(64) over magic||len||payload. 10// cap_verify(path) = 1 ONLY IF the magic/len are intact AND the signature verifies against the 11// baked TRUSTED platform public key. A corrupted OR forged (wrong-key) capsule -> 0 (rejected). 12// In production the verifier holds ONLY the public key (the private seed is offline with the signer); 13// here a fixed test seed lets the gate both sign and anchor -- ed25519 is deterministic (RFC 8032), 14// so signatures + this gate are reproducible. Sovereign: nx_ed25519 + nx_syscalls. license_tier: ORIGINAL 15import "nx_ed25519_signature.nx" 16import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 17import "nx_syscalls.nx" 18const CAP_MAGIC_65536: i64 = 65536 19const CAP_MAGIC_16777216: i64 = 16777216 20 21const CAP_HDR: i64 = 8 // magic(4) + payload_len(4) 22const CAP_SIG: i64 = 64 // ed25519 signature 23const CAP_MAXIMG: i64 = 16777216 24 25func cp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 as u8 { n = n + 1 } sys_write(1, s, n); return 0 } 26// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 27// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 28// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 29// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 30func cp_putn(v: i64) -> i64 { nxi_out(v); return 0 } 31func cp_read(path: *u8, lb: *i64) -> *u8 { 32 let fd: i64 = sys_openat_rd(path) 33 if fd < 0 { lb[0] = 0; return 0 as *u8 } 34 let buf: *u8 = sys_mmap(CAP_MAXIMG + 16) 35 var n: i64 = 0; var go: i64 = 1 36 while go == 1 { 37 let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, CAP_MAXIMG - n) 38 if r <= 0 { go = 0 } else { n = n + r } 39 if n >= CAP_MAXIMG { go = 0 } 40 } 41 sys_close(fd); buf[n] = 0 as u8; lb[0] = n 42 return buf 43} 44func cp_write(path: *u8, buf: *u8, n: i64) -> i64 { 45 let fd: i64 = sys_openat_wr(path, 0x1a4) 46 if fd < 0 { return 0 - 1 } 47 sys_write(fd, buf, n); sys_close(fd); return 0 48} 49func cp_wr_u32(buf: *u8, off: i64, v: i64) -> i64 { 50 buf[off + 0] = (v & 0xff) as u8; buf[off + 1] = ((v >> 8) & 0xff) as u8 51 buf[off + 2] = ((v >> 16) & 0xff) as u8; buf[off + 3] = ((v >> 24) & 0xff) as u8 52 return 0 53} 54func cp_rd_u32(buf: *u8, off: i64) -> i64 { 55 var v: i64 = buf[off + 0] as i64 56 v = v + (buf[off + 1] as i64) * 256 57 v = v + (buf[off + 2] as i64) * CAP_MAGIC_65536 58 v = v + (buf[off + 3] as i64) * CAP_MAGIC_16777216 59 return v 60} 61 62// the baked PLATFORM key (Secure-Boot "PK"). Fixed test seed -> derived trusted public key. 63func cap_plat_seed(out: *u8) -> i64 { var i: i64 = 0; while i < 32 { out[i] = ((i * 7 + 13) & 0xff) as u8; i = i + 1 } return 0 } 64func cap_trusted_pub(out: *u8) -> i64 { 65 let seed: *u8 = sys_mmap(32); cap_plat_seed(seed) 66 ed25519_pub_from_priv(seed, out) 67 return 0 68} 69 70// build a signed capsule: magic + len + payload + ed25519_sign(seed, magic||len||payload). 71func cap_make(path: *u8, payload: *u8, plen: i64, seed32: *u8) -> i64 { 72 let n: i64 = CAP_HDR + plen 73 let buf: *u8 = sys_mmap(n + CAP_SIG + 16) 74 buf[0] = 78 as u8; buf[1] = 88 as u8; buf[2] = 67 as u8; buf[3] = 49 as u8 // 'N''X''C''1' 75 cp_wr_u32(buf, 4, plen) 76 var i: i64 = 0 77 while i < plen { buf[8 + i] = payload[i]; i = i + 1 } 78 let sig: *u8 = sys_mmap(CAP_SIG) 79 ed25519_sign_full(seed32, buf, n, sig) 80 i = 0 81 while i < CAP_SIG { buf[n + i] = sig[i]; i = i + 1 } 82 cp_write(path, buf, n + CAP_SIG) 83 return 0 84} 85 86// 1 ONLY IF magic/len intact AND signature verifies against the TRUSTED platform pubkey; else 0. 87func cap_verify(path: *u8) -> i64 { 88 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 89 let buf: *u8 = cp_read(path, lb) 90 if (buf as i64) == 0 { return 0 } 91 let total: i64 = lb[0] 92 if total < (CAP_HDR + CAP_SIG) { return 0 } 93 if buf[0] != 78 as u8 { return 0 } 94 if buf[1] != 88 as u8 { return 0 } 95 if buf[2] != 67 as u8 { return 0 } 96 if buf[3] != 49 as u8 { return 0 } 97 let plen: i64 = cp_rd_u32(buf, 4) 98 if plen < 0 { return 0 } 99 let n: i64 = CAP_HDR + plen 100 if (n + CAP_SIG) > total { return 0 } 101 let pub: *u8 = sys_mmap(32); cap_trusted_pub(pub) 102 let sig: *u8 = ((buf as i64) + n) as *u8 103 if ed25519_verify_full(pub, buf, n, sig) == NX_ED25519_SIG_OK { return 1 } 104 return 0 105} 106 107func cp_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { dst[off + i] = s[i]; i = i + 1 } return off + i } 108func cp_catn(dst: *u8, off: i64, v: i64) -> i64 { 109 var m: i64 = v; var o: i64 = off 110 if m < 0 { m = 0 - m } 111 let t: *u8 = sys_mmap(28); var k: i64 = 0 112 if m == 0 { t[0] = 48 as u8; k = 1 } 113 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 114 var i: i64 = 0 115 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 116 return o + k 117} 118func cap_path(out: *u8, stem: *u8, epoch: i64, pid: i64) -> i64 { 119 var o: i64 = cp_cat(out, 0, stem) 120 o = cp_catn(out, o, epoch); o = cp_cat(out, o, "." as *u8); o = cp_catn(out, o, pid); o = cp_cat(out, o, ".cap\x00" as *u8) 121 out[o] = 0 as u8 122 return o 123} 124func cap_corrupt(path: *u8) -> i64 { 125 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 126 let buf: *u8 = cp_read(path, lb) 127 if (buf as i64) == 0 { return 0 - 1 } 128 let n: i64 = lb[0] 129 if n > 8 { buf[8] = ((buf[8] as i64) + 1) as u8 } 130 cp_write(path, buf, n) 131 return 0 132} 133 134func main() -> i64 { 135 cp_puts("=== NISHI SIGNED CAPSULE (Secure-Boot-grade: sovereign ed25519) ===\n") 136 let epoch: i64 = sys_now_realtime_sec() 137 let pid: i64 = __syscall(39, 0, 0, 0, 0, 0, 0) 138 let pub: *u8 = sys_mmap(32); cap_trusted_pub(pub) 139 cp_puts(" trusted platform pubkey[0..4]="); cp_putn(pub[0] as i64); cp_puts(","); cp_putn(pub[1] as i64); cp_puts(","); cp_putn(pub[2] as i64); cp_puts(","); cp_putn(pub[3] as i64); cp_puts("\n") 140 141 let plat: *u8 = sys_mmap(32); cap_plat_seed(plat) 142 let forged: *u8 = sys_mmap(32); var i: i64 = 0; while i < 32 { forged[i] = ((i * 3 + 99) & 0xff) as u8; i = i + 1 } 143 144 let cap: *u8 = sys_mmap(256) 145 cap_path(cap, "/tmp/nxcap_demo." as *u8, epoch, pid) 146 147 cap_make(cap, "NISHI-fw-image-payload-authentic" as *u8, 32, plat) 148 cp_puts(" authentic capsule -> verify="); cp_putn(cap_verify(cap)); cp_puts(" (1=signed by platform key)\n") 149 150 // corrupt the payload in place 151 cap_corrupt(cap) 152 cp_puts(" corrupted payload -> verify="); cp_putn(cap_verify(cap)); cp_puts(" (0=signature fails)\n") 153 154 // forged: signed with a DIFFERENT key 155 cap_make(cap, "NISHI-fw-image-payload-authentic" as *u8, 32, forged) 156 cp_puts(" FORGED (wrong key) -> verify="); cp_putn(cap_verify(cap)); cp_puts(" (0=not the platform key -- sha256 could NOT catch this)\n") 157 cp_puts(" INVARIANT: only an image SIGNED by the platform key is bootable -> authenticity + integrity\n") 158 sys_exit(0); return 0 159}