nx_gcode_manifest_test.nx source
↩ module page · 124 lines · 4864 B
1// nx_gcode_manifest_test.nx -- exercise manifest emit + finalize
2// + determinism on synthetic blobs.
3//
4// Closed-form invariants:
5// (a) Fresh manifest contains the magic line.
6// (b) After add_blob x 4, the buffer grew + non-empty.
7// (c) Finalize sets finalized=1 and appends the self-hash line.
8// (d) Post-finalize, add_blob returns -2 (refused).
9// (e) Two manifests built with the same inputs produce
10// byte-identical buffers (determinism for reproducible builds).
11// (f) Tampering with the buffer breaks the self-attestation:
12// recomputing blake2b over the pre-final bytes no longer
13// matches the embedded final hash.
14//
15// expect_exit: 0
16// license_tier: ORIGINAL
17
18import "nx_syscalls.nx"
19import "nx_blake2b.nx"
20import "nx_gcode_manifest.nx"
21
22// Build a manifest with 4 standard entries from caller-provided blobs.
23func build_manifest(b1: *u8, l1: i64, b2: *u8, l2: i64,
24 b3: *u8, l3: i64, b4: *u8, l4: i64) -> *NxGcodeManifest {
25 let m: *NxGcodeManifest = nx_gmanifest_new()
26 nx_gmanifest_add_blob(m, "mesh.blake2b", b1, l1)
27 nx_gmanifest_add_blob(m, "config.blake2b", b2, l2)
28 nx_gmanifest_add_blob(m, "machine.blake2b", b3, l3)
29 nx_gmanifest_add_blob(m, "material.blake2b", b4, l4)
30 return m
31}
32
33func main() -> i64 {
34 // Synthetic blobs.
35 let mesh_blob: *u8 = sys_mmap(64)
36 var i: i64 = 0
37 while i < 64 { mesh_blob[i] = (i * 7 + 11) & 0xff; i = i + 1 }
38 let config_blob: *u8 = sys_mmap(32)
39 i = 0
40 while i < 32 { config_blob[i] = (i * 13 + 3) & 0xff; i = i + 1 }
41 let machine_blob: *u8 = sys_mmap(48)
42 i = 0
43 while i < 48 { machine_blob[i] = (i * 5 + 17) & 0xff; i = i + 1 }
44 let material_blob: *u8 = sys_mmap(40)
45 i = 0
46 while i < 40 { material_blob[i] = (i * 3 + 29) & 0xff; i = i + 1 }
47
48 // --- (a) Fresh manifest has magic line ---
49 let m: *NxGcodeManifest = nx_gmanifest_new()
50 if m.len < 20 { return 10 }
51 if m.buf[0] != 59 { return 11 } // ';'
52 if m.buf[1] != 32 { return 12 } // ' '
53 if m.buf[2] != 78 { return 13 } // 'N'
54
55 // --- (b) Add entries + grow ---
56 let len_before: i64 = m.len
57 nx_gmanifest_add_blob(m, "mesh.blake2b", mesh_blob, 64)
58 nx_gmanifest_add_blob(m, "config.blake2b", config_blob, 32)
59 nx_gmanifest_add_blob(m, "machine.blake2b", machine_blob, 48)
60 nx_gmanifest_add_blob(m, "material.blake2b", material_blob, 40)
61 if m.len <= len_before { return 20 }
62 // Each entry adds: "; <key> <128 hex>\n" >= 132 bytes. 4 entries >= 528 bytes.
63 if m.len - len_before < 528 { return 21 }
64
65 // --- (c) Finalize ---
66 let rc: i64 = nx_gmanifest_finalize(m)
67 if rc != 0 { return 30 }
68 if m.finalized != 1 { return 31 }
69 // Final line "; manifest.blake2b <128 hex>\n" >= 148 bytes.
70 if m.len - len_before < 528 + 148 { return 32 }
71
72 // --- (d) Post-finalize refuses ---
73 let rc2: i64 = nx_gmanifest_add_blob(m, "x.blake2b", mesh_blob, 64)
74 if rc2 != -2 { return 40 }
75
76 // --- (e) Determinism ---
77 let a: *NxGcodeManifest = build_manifest(mesh_blob, 64, config_blob, 32,
78 machine_blob, 48, material_blob, 40)
79 nx_gmanifest_finalize(a)
80 let b: *NxGcodeManifest = build_manifest(mesh_blob, 64, config_blob, 32,
81 machine_blob, 48, material_blob, 40)
82 nx_gmanifest_finalize(b)
83 if a.len != b.len { return 50 }
84 var k: i64 = 0
85 while k < a.len {
86 if a.buf[k] != b.buf[k] { return 51 }
87 k = k + 1
88 }
89 // Final hashes byte-identical
90 k = 0
91 while k < NX_GMANIFEST_DIGEST_LEN {
92 if a.final_hash[k] != b.final_hash[k] { return 52 }
93 k = k + 1
94 }
95
96 // --- (f) Tamper detection ---
97 // Hash the bytes BEFORE the "; manifest.blake2b " line and verify
98 // they match the recorded final_hash. Then flip one byte and
99 // verify the recompute no longer matches.
100 let tail_prefix_len: i64 = 19 // "; manifest.blake2b "
101 let prefinal_len: i64 = a.len - tail_prefix_len - (NX_GMANIFEST_DIGEST_LEN * 2) - 1
102 let recomputed: *u8 = sys_mmap(NX_GMANIFEST_DIGEST_LEN)
103 gmanifest_blake2b_oneshot(a.buf, prefinal_len, recomputed, NX_GMANIFEST_DIGEST_LEN)
104 var matches: i64 = 1
105 var j: i64 = 0
106 while j < NX_GMANIFEST_DIGEST_LEN {
107 if recomputed[j] != a.final_hash[j] { matches = 0 }
108 j = j + 1
109 }
110 if matches != 1 { return 60 }
111
112 // Flip a byte in the body and recompute -- now must NOT match.
113 a.buf[100] = a.buf[100] ^ 0xff
114 gmanifest_blake2b_oneshot(a.buf, prefinal_len, recomputed, NX_GMANIFEST_DIGEST_LEN)
115 var still_matches: i64 = 1
116 j = 0
117 while j < NX_GMANIFEST_DIGEST_LEN {
118 if recomputed[j] != a.final_hash[j] { still_matches = 0 }
119 j = j + 1
120 }
121 if still_matches != 0 { return 61 }
122
123 return 0
124}