code wiki / (root) / nx_gcode_manifest_test.nx

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}