code wiki / (root) / nx_install_attest_signed_test.nx

nx_install_attest_signed_test.nx source

↩ module page · 267 lines · 9306 B

1// nx_install_attest_signed_test.nx -- smoke for EM-9 Ed25519 signing. 2// 3// Uses the RFC 8032 §7.1 Test 1 keypair (the only canonical Ed25519 4// test vector pair that's bit-stable across implementations): 5// priv = 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60 6// pub = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a 7// 8// Test plan: 9// 1. Sign a known install_hash; verify with correct pub -> OK 10// 2. Tamper install_hash bytes -> verify returns TAMPERED 11// 3. Tamper signature bytes -> verify returns TAMPERED 12// 4. Use WRONG public key -> verify returns TAMPERED 13// 5. Bad-input gates (null hash / null priv / null pub / null sig / 14// null out) 15// 6. Sealed-enum verdict gate 16// 7. Plan-level sign + verify roundtrip (compose with 17// nx_install_hash_compute on a real NxInstallPlan) 18 19import "nx_syscalls.nx" 20import "nx_probe.nx" 21import "nx_calibrate.nx" 22import "nx_select.nx" 23import "nx_select_joint.nx" 24import "nx_install_plan.nx" 25import "nx_install_hash.nx" 26import "nx_install_pipeline.nx" 27import "nx_install_attest_signed.nx" 28 29// Populate the 32-byte priv key from RFC 8032 §7.1 Test 1. 30func _rfc_priv(buf: *u8) -> i64 { 31 buf[0] = 0x9d as u8 32 buf[1] = 0x61 as u8 33 buf[2] = 0xb1 as u8 34 buf[3] = 0x9d as u8 35 buf[4] = 0xef as u8 36 buf[5] = 0xfd as u8 37 buf[6] = 0x5a as u8 38 buf[7] = 0x60 as u8 39 buf[8] = 0xba as u8 40 buf[9] = 0x84 as u8 41 buf[10] = 0x4a as u8 42 buf[11] = 0xf4 as u8 43 buf[12] = 0x92 as u8 44 buf[13] = 0xec as u8 45 buf[14] = 0x2c as u8 46 buf[15] = 0xc4 as u8 47 buf[16] = 0x44 as u8 48 buf[17] = 0x49 as u8 49 buf[18] = 0xc5 as u8 50 buf[19] = 0x69 as u8 51 buf[20] = 0x7b as u8 52 buf[21] = 0x32 as u8 53 buf[22] = 0x69 as u8 54 buf[23] = 0x19 as u8 55 buf[24] = 0x70 as u8 56 buf[25] = 0x3b as u8 57 buf[26] = 0xac as u8 58 buf[27] = 0x03 as u8 59 buf[28] = 0x1c as u8 60 buf[29] = 0xae as u8 61 buf[30] = 0x7f as u8 62 buf[31] = 0x60 as u8 63 return 0 64} 65 66// Populate the 32-byte public key from RFC 8032 §7.1 Test 1. 67func _rfc_pub(buf: *u8) -> i64 { 68 buf[0] = 0xd7 as u8 69 buf[1] = 0x5a as u8 70 buf[2] = 0x98 as u8 71 buf[3] = 0x01 as u8 72 buf[4] = 0x82 as u8 73 buf[5] = 0xb1 as u8 74 buf[6] = 0x0a as u8 75 buf[7] = 0xb7 as u8 76 buf[8] = 0xd5 as u8 77 buf[9] = 0x4b as u8 78 buf[10] = 0xfe as u8 79 buf[11] = 0xd3 as u8 80 buf[12] = 0xc9 as u8 81 buf[13] = 0x64 as u8 82 buf[14] = 0x07 as u8 83 buf[15] = 0x3a as u8 84 buf[16] = 0x0e as u8 85 buf[17] = 0xe1 as u8 86 buf[18] = 0x72 as u8 87 buf[19] = 0xf3 as u8 88 buf[20] = 0xda as u8 89 buf[21] = 0xa6 as u8 90 buf[22] = 0x23 as u8 91 buf[23] = 0x25 as u8 92 buf[24] = 0xaf as u8 93 buf[25] = 0x02 as u8 94 buf[26] = 0x1a as u8 95 buf[27] = 0x68 as u8 96 buf[28] = 0xf7 as u8 97 buf[29] = 0x07 as u8 98 buf[30] = 0x51 as u8 99 buf[31] = 0x1a as u8 100 return 0 101} 102 103// Construct a fixed install_hash (32 known bytes) for deterministic 104// sign+verify roundtrip without depending on probe/calibration. 105func _make_hash(buf: *u8) -> i64 { 106 var i: i64 = 0 107 while i < 32 { 108 buf[i] = ((i * 7) & 255) as u8 109 i = i + 1 110 } 111 return 0 112} 113 114func _make_probe() -> *NxProbeRecord { 115 let p: *NxProbeRecord = nx_probe_new() 116 p.actual_isa_family = NX_ISA_RV64 117 p.actual_endianness = NX_ENDIAN_LITTLE 118 p.actual_pointer_width_bits = 64 119 p.actual_page_size_bytes = 4096 120 p.actual_mmap_works = 1 121 p.actual_write_works = 1 122 p.actual_mono_clock_works = 1 123 return p 124} 125 126func _make_calib() -> *NxCalibrationRecord { 127 let c: *NxCalibrationRecord = nx_calibrate_new() 128 c.int_alu_ps_per_op = 500 129 c.mem_ns_per_access_l1 = 5 130 c.mem_ns_per_access_l2 = 20 131 c.mem_ns_per_access_ram = 300 132 c.mem_bw_mib_per_s = 5000 133 c.syscall_ns_clock_gettime = 50 134 c.syscall_ns_mmap = 1000 135 c.inferred_tier = NX_TIER_INF_LAPTOP 136 return c 137} 138 139func _mk(variant_id: i64, cost: i64, in_lay: i64, out_lay: i64) -> *NxManifest { 140 let m: *NxManifest = nx_manifest_new( 141 100, variant_id, 142 nx_isa_bit(NX_ISA_RV64), 143 0, 4096, 144 NX_TIER_INF_UNKNOWN, NX_TIER_INF_HPC, 145 cost, 0, 0, 0) 146 nx_manifest_set_layout(m, in_lay, out_lay) 147 return m 148} 149 150func main() -> i64 { 151 let priv: *u8 = sys_mmap(64) 152 _rfc_priv(priv) 153 let pub: *u8 = sys_mmap(64) 154 _rfc_pub(pub) 155 let install_hash: *u8 = sys_mmap(64) 156 _make_hash(install_hash) 157 let sig: *u8 = sys_mmap(128) 158 159 // ----- 1. Sign + verify roundtrip ----- 160 if nx_install_sign(install_hash, priv, sig) != NX_INSTALL_SIG_OK { return 1 } 161 if nx_install_verify_sig(install_hash, pub, sig) != NX_INSTALL_SIG_OK { return 2 } 162 163 // ----- 2. TAMPER install_hash -> verify returns TAMPERED ----- 164 let tampered_hash: *u8 = sys_mmap(64) 165 var i: i64 = 0 166 while i < 32 { 167 tampered_hash[i] = install_hash[i] 168 i = i + 1 169 } 170 tampered_hash[0] = ((tampered_hash[0] as i64) ^ 1) as u8 // flip bit 171 if nx_install_verify_sig(tampered_hash, pub, sig) != NX_INSTALL_SIG_TAMPERED { return 3 } 172 173 // ----- 3. TAMPER signature -> verify returns TAMPERED ----- 174 let tampered_sig: *u8 = sys_mmap(128) 175 var j: i64 = 0 176 while j < 64 { 177 tampered_sig[j] = sig[j] 178 j = j + 1 179 } 180 tampered_sig[0] = ((tampered_sig[0] as i64) ^ 1) as u8 // flip bit in R 181 if nx_install_verify_sig(install_hash, pub, tampered_sig) != NX_INSTALL_SIG_TAMPERED { return 4 } 182 183 // Flip a bit in S half (offset 32). 184 var jj: i64 = 0 185 while jj < 64 { 186 tampered_sig[jj] = sig[jj] 187 jj = jj + 1 188 } 189 tampered_sig[40] = ((tampered_sig[40] as i64) ^ 1) as u8 190 if nx_install_verify_sig(install_hash, pub, tampered_sig) != NX_INSTALL_SIG_TAMPERED { return 5 } 191 192 // ----- 4. WRONG public key -> verify returns TAMPERED ----- 193 let wrong_pub: *u8 = sys_mmap(64) 194 var w: i64 = 0 195 while w < 32 { 196 wrong_pub[w] = pub[w] 197 w = w + 1 198 } 199 wrong_pub[0] = ((wrong_pub[0] as i64) ^ 1) as u8 200 let rc_wp: i64 = nx_install_verify_sig(install_hash, wrong_pub, sig) 201 // Wrong pub may produce TAMPERED (signature math fails) or 202 // BAD_KEY (decompress fails). Either is a refusal verdict. 203 if rc_wp == NX_INSTALL_SIG_OK { return 6 } 204 205 // ----- 5. Bad-input gates ----- 206 let np: *u8 = (0 as i64) as *u8 207 if nx_install_sign(np, priv, sig) != NX_INSTALL_SIG_BAD_INPUT { return 7 } 208 if nx_install_sign(install_hash, np, sig) != NX_INSTALL_SIG_BAD_INPUT { return 8 } 209 if nx_install_sign(install_hash, priv, np) != NX_INSTALL_SIG_BAD_INPUT { return 9 } 210 if nx_install_verify_sig(np, pub, sig) != NX_INSTALL_SIG_BAD_INPUT { return 10 } 211 if nx_install_verify_sig(install_hash, np, sig) != NX_INSTALL_SIG_BAD_INPUT { return 11 } 212 if nx_install_verify_sig(install_hash, pub, np) != NX_INSTALL_SIG_BAD_INPUT { return 12 } 213 214 // ----- 6. Sealed-enum verdict gate ----- 215 if nx_install_sig_verdict_is_valid(NX_INSTALL_SIG_OK) != 1 { return 13 } 216 if nx_install_sig_verdict_is_valid(-1) != 0 { return 14 } 217 if nx_install_sig_verdict_is_valid(NX_INSTALL_SIG_N) != 0 { return 15 } 218 219 // ----- 7. Plan-level sign + verify ----- 220 // Build a real plan via the orchestrator, then sign + verify. 221 let probe: *NxProbeRecord = _make_probe() 222 let calib: *NxCalibrationRecord = _make_calib() 223 let policy: *NxPolicy = nx_policy_throughput_default() 224 let m_a: *NxManifest = _mk(8001, 300, NX_LAYOUT_OPAQUE_PASS, NX_LAYOUT_COL_MAJOR) 225 let m_b: *NxManifest = _mk(8002, 200, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_COL_MAJOR) 226 let m_c: *NxManifest = _mk(8003, 100, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_OPAQUE_PASS) 227 let a_ptrs: *i64 = (sys_mmap(8)) as *i64 228 a_ptrs[0] = m_a as i64 229 let b_ptrs: *i64 = (sys_mmap(8)) as *i64 230 b_ptrs[0] = m_b as i64 231 let c_ptrs: *i64 = (sys_mmap(8)) as *i64 232 c_ptrs[0] = m_c as i64 233 let nodes: *i64 = (sys_mmap(24)) as *i64 234 nodes[0] = (nx_joint_node_new(100, a_ptrs, 1)) as i64 235 nodes[1] = (nx_joint_node_new(200, b_ptrs, 1)) as i64 236 nodes[2] = (nx_joint_node_new(300, c_ptrs, 1)) as i64 237 238 let plan: *NxInstallPlan = nx_install_plan_new() 239 let plan_hash: *u8 = sys_mmap(64) 240 if nx_install_pipeline_run_with_inputs(probe, calib, policy, 241 nodes, 3, plan, plan_hash) 242 != NX_PIPELINE_OK { return 16 } 243 244 // Sign the plan + verify. 245 let plan_sig: *u8 = sys_mmap(128) 246 let plan_hash_back: *u8 = sys_mmap(64) 247 if nx_install_sign_plan(plan, priv, plan_hash_back, plan_sig) 248 != NX_INSTALL_SIG_OK { return 17 } 249 // The hash recomputed inside sign_plan must match what the 250 // pipeline emitted. 251 var hi: i64 = 0 252 while hi < 32 { 253 if (plan_hash_back[hi] as i64) != (plan_hash[hi] as i64) { return 18 } 254 hi = hi + 1 255 } 256 if nx_install_verify_sig_plan(plan, pub, plan_sig) 257 != NX_INSTALL_SIG_OK { return 19 } 258 259 // Mutate the plan (e.g., change calib_inferred_tier) and verify 260 // the signature now fails -- THIS proves plan-level tamper 261 // detection composes with hash-recomputation. 262 plan.calib_inferred_tier = NX_TIER_INF_HPC // was LAPTOP 263 if nx_install_verify_sig_plan(plan, pub, plan_sig) 264 != NX_INSTALL_SIG_TAMPERED { return 20 } 265 266 return 0 267}