code wiki / _hdl_build / nx_fw_signed_robust.nx

nx_fw_signed_robust.nx source

↩ module page · 115 lines · 6072 B

1// nx_fw_signed_robust.nx -- the fusion: S-class never-brick (A/B + immutable factory + atomic 2// commit) WHERE BOOTABILITY = ed25519 SIGNATURE, not just a checksum. 3// 4// module: nishi-core.genealogy.fw_signed_robust 5// capability: CORE_COMPUTE (forgery-resistant AND fault-resistant firmware selection) 6// 7// Rung 6 (nx_fw_robust_flash) survives power loss + double faults but would boot any image with a 8// valid checksum. Rung 8 (nx_fw_capsule) rejects forgery but had no fallback. FUSED here: the banks 9// + factory hold SIGNED capsules, and sr_select boots a bank ONLY if cap_verify passes (signature 10// valid against the trusted platform key). So a FORGED image planted in the active bank is rejected 11// at select time and the system falls back to the other bank or the signed immutable factory. 12// Sandboxed (/tmp). Sovereign: nx_fw_capsule (-> nx_ed25519) + nx_syscalls. license_tier: ORIGINAL 13import "nx_fw_capsule.nx" 14import "nx_syscalls.nx" 15 16const SR_BANK_A: i64 = 65 17const SR_BANK_B: i64 = 66 18const SR_OK: i64 = 0 19const SR_REJECTED: i64 = 1 // unsigned/forged/corrupt source -> not written 20const SR_NO_ANCHOR: i64 = 3 // no signature-valid fallback (active bank nor factory) -> refuse 21const SR_VIOLATION: i64 = 4 // a path not under /tmp/ 22const SR_HELD: i64 = 6 // inactive write failed verify -> not committed, active retained 23 24func sr_is_sandbox(p: *u8) -> i64 { 25 let pre: *u8 = "/tmp/" 26 var i: i64 = 0 27 while pre[i] != 0 as u8 { if p[i] != pre[i] { return 0 } i = i + 1 } 28 return 1 29} 30func sr_tmp(sel: *u8, out: *u8) -> i64 { var o: i64 = cp_cat(out, 0, sel); o = cp_cat(out, o, ".tmp\x00" as *u8); out[o] = 0 as u8; return o } 31func sr_read_sel(sel: *u8) -> i64 { 32 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 33 let b: *u8 = cp_read(sel, lb) 34 if (b as i64) == 0 { return 0 } 35 if lb[0] < 1 { return 0 } 36 let c: i64 = b[0] as i64 37 if c == SR_BANK_A { return SR_BANK_A } 38 if c == SR_BANK_B { return SR_BANK_B } 39 return 0 40} 41func sr_write_sel_atomic(sel: *u8, bankc: i64) -> i64 { 42 if sr_is_sandbox(sel) == 0 { return 0 - 1 } 43 let tmp: *u8 = sys_mmap(512); sr_tmp(sel, tmp) 44 let buf: *u8 = sys_mmap(2); buf[0] = bankc as u8; buf[1] = 0 as u8 45 cp_write(tmp, buf, 1) 46 sys_renameat(tmp, sel) 47 return 0 48} 49func sr_other(bankc: i64) -> i64 { if bankc == SR_BANK_B { return SR_BANK_A } return SR_BANK_B } 50func sr_copy(src: *u8, dst: *u8) -> i64 { 51 let lb: *i64 = sys_mmap(16) as *i64; lb[0] = 0 52 let b: *u8 = cp_read(src, lb) 53 if (b as i64) == 0 { return 0 - 1 } 54 cp_write(dst, b, lb[0]) 55 return 0 56} 57 58// SELECT a SIGNATURE-VERIFIED image: preferred bank -> other bank -> signed immutable factory. 59// Returns a path whose capsule verifies against the trusted platform key, or null if none does. 60func sr_select(sel: *u8, ba: *u8, bb: *u8, fac: *u8) -> *u8 { 61 let pref: i64 = sr_read_sel(sel) 62 if pref == SR_BANK_B { if cap_verify(bb) == 1 { return bb } } else { if cap_verify(ba) == 1 { return ba } } 63 if pref == SR_BANK_B { if cap_verify(ba) == 1 { return ba } } else { if cap_verify(bb) == 1 { return bb } } 64 if cap_verify(fac) == 1 { return fac } 65 return 0 as *u8 66} 67func sr_selectable(sel: *u8, ba: *u8, bb: *u8, fac: *u8) -> i64 { 68 if (sr_select(sel, ba, bb, fac) as i64) != 0 { return 1 } 69 return 0 70} 71 72// flash a SIGNED capsule into the inactive bank, verify its signature, atomic-commit. A forged or 73// unsigned source is rejected; a fallback (active bank OR signed factory) must exist. 74func sr_safe_flash(sel: *u8, ba: *u8, bb: *u8, fac: *u8, newcap: *u8, corrupt: i64) -> i64 { 75 if sr_is_sandbox(sel) == 0 { return SR_VIOLATION } 76 if sr_is_sandbox(ba) == 0 { return SR_VIOLATION } 77 if sr_is_sandbox(bb) == 0 { return SR_VIOLATION } 78 if sr_is_sandbox(newcap) == 0 { return SR_VIOLATION } 79 if cap_verify(newcap) == 0 { return SR_REJECTED } // unsigned/forged/corrupt -> never write 80 var act: i64 = sr_read_sel(sel) 81 if act == 0 { act = SR_BANK_A } 82 var actpath: *u8 = ba 83 if act == SR_BANK_B { actpath = bb } 84 if cap_verify(actpath) == 0 { if cap_verify(fac) == 0 { return SR_NO_ANCHOR } } 85 let tgtc: i64 = sr_other(act) 86 var tgt: *u8 = ba 87 if tgtc == SR_BANK_B { tgt = bb } 88 sr_copy(newcap, tgt) 89 if corrupt == 1 { cap_corrupt(tgt) } 90 if cap_verify(tgt) == 0 { return SR_HELD } 91 sr_write_sel_atomic(sel, tgtc) 92 return SR_OK 93} 94 95func main() -> i64 { 96 cp_puts("=== NISHI SIGNED ROBUST FLASH (A/B + immutable factory + atomic commit, SIGNATURE-gated) ===\n") 97 let epoch: i64 = sys_now_realtime_sec() 98 let pid: i64 = __syscall(39, 0, 0, 0, 0, 0, 0) 99 let plat: *u8 = sys_mmap(32); cap_plat_seed(plat) 100 let forged: *u8 = sys_mmap(32); var i: i64 = 0; while i < 32 { forged[i] = ((i * 3 + 99) & 0xff) as u8; i = i + 1 } 101 let a: *u8 = sys_mmap(256); let b: *u8 = sys_mmap(256); let fac: *u8 = sys_mmap(256); let sel: *u8 = sys_mmap(256) 102 cap_path(a, "/tmp/sr_a." as *u8, epoch, pid); cap_path(b, "/tmp/sr_b." as *u8, epoch, pid) 103 cap_path(fac, "/tmp/sr_fac." as *u8, epoch, pid); cap_path(sel, "/tmp/sr_sel." as *u8, epoch, pid) 104 105 cap_make(fac, "NISHI-fw-signed-image" as *u8, 21, plat) // signed factory 106 cap_make(a, "NISHI-fw-signed-image" as *u8, 21, plat) // signed bank A 107 cap_make(b, "NISHI-fw-signed-image" as *u8, 21, plat) // signed bank B 108 sr_write_sel_atomic(sel, SR_BANK_A) 109 cp_puts(" start: selectable="); cp_putn(sr_selectable(sel, a, b, fac)); cp_puts(" active="); cp_putn(sr_read_sel(sel)); cp_puts("\n") 110 // plant a FORGED image into the ACTIVE bank A 111 cap_make(a, "NISHI-fw-signed-image" as *u8, 21, forged) 112 cp_puts(" FORGED image planted in active bank A -> cap_verify(A)="); cp_putn(cap_verify(a)); cp_puts(" selectable="); cp_putn(sr_selectable(sel, a, b, fac)); cp_puts(" (falls back to signed B/factory)\n") 113 cp_puts(" INVARIANT: sr_select boots ONLY a signature-verified image -> forgery can't be selected even if planted in a bank; A/B+factory keeps it never-brick\n") 114 sys_exit(0); return 0 115}