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}