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}