// statehandoff v1.0.1 独立复算(Monday / 2026-11-05) // 腿A:part9 E1 固定向量(三叶 MTH root + node01 + leafHashes + Z-4 乱序 + Z-5 篡改必红) // 腿B:C-1 实测重跑(part10 R-1:454f9d…i0 registration payload → JCS → SHA-256) import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; const sha = (buf) => createHash("sha256").update(buf).digest("hex"); // JCS(RFC 8785) for this payload family: recursively sort object keys by UTF-16 code-unit order, // then JSON.stringify (minimal escaping — matches JCS for control chars; no numbers in scope). const jcs = (v) => { if (v === null || typeof v !== "object") return JSON.stringify(v); if (Array.isArray(v)) return "[" + v.map(jcs).join(",") + "]"; const keys = Object.keys(v).sort(); // JS default sort = UTF-16 code unit comparison return "{" + keys.map((k) => JSON.stringify(k) + ":" + jcs(v[k])).join(",") + "}"; }; // ---------- 腿A:E1 向量(夹具见 statehandoff part9 E1_offer) ---------- const items = [ { pinId: "35e9c870025e21305a62f8d7c81df71374d636a53a75a9e6201689a277a3add9i0", cHash: "sha256:1b49b5ebca7e32394d732cf58e94a9b9204daeba3f341121cbfb9bec0277e43b" }, { pinId: "0b3321f4f845c5f216ff7cf6a2a6cf0e7da3922f7e3ced18b6cb9493deb00375i0", cHash: "sha256:d548f39bc75dddb3ccb046bd7314db7679495fad18eee95027c000c2f8fc3933" }, { pinId: "d33d43be6968890cefe1accc2299ee7f81bfbf9abffd707cd942a2b909138150i0", cHash: "sha256:90bea7928504c5d49a04046e9438c6b796a672660c28ed17a41d4264e1ae4281" }, ]; const leaves = (arr) => [...arr] // 输入顺序无关:先规范化(按 pinId UTF-16 码元序) .slice() .sort((a, b) => (a.pinId < b.pinId ? -1 : a.pinId > b.pinId ? 1 : 0)) .map((it) => Buffer.from(jcs({ cHash: it.cHash, pinId: it.pinId }), "utf8")); // RFC 6962 §2.1 MTH:单叶 0x00 前缀;内节点 0x01 || 左 || 右;k = 严格小于 n 的最大 2 的幂 const mth = (leavesArr) => { const n = leavesArr.length; if (n === 0) return sha(Buffer.alloc(0)); if (n === 1) return sha(Buffer.concat([Buffer.from([0x00]), leavesArr[0]])); let k = 1; while (k * 2 < n) k *= 2; // largest power of two strictly less than n return sha(Buffer.concat([Buffer.from([0x01]), Buffer.from(mth(leavesArr.slice(0, k)), "hex"), Buffer.from(mth(leavesArr.slice(k)), "hex")])); }; const L = leaves(items); const hashes = [sha(Buffer.concat([Buffer.from([0x00]), L[0]])), sha(Buffer.concat([Buffer.from([0x00]), L[1]])), sha(Buffer.concat([Buffer.from([0x00]), L[2]]))]; const node01 = sha(Buffer.concat([Buffer.from([0x01]), Buffer.from(hashes[0], "hex"), Buffer.from(hashes[1], "hex")])); const root = mth(L); const docLeaf = ["b5a4422a926dcf1326be3e336045ca0ec796bba69496f75687c1dbcdd71564ca", "6fa5241b408ebe947a318bfda11bddc0488317de526481fbd13ce746500dc3d7", "2d2cd4da5ea3b7701bf7c8cfbda31cc79c69b66ad3ed85d3551945bc86c53331"]; const doc = { node01: "12a0d22255122e794c5413b1cf18e74c1e049d24fb00ebd04509f69dc564fec0", root: "344e0804d2d79da8f60846c7bbd8bf5c346be8f077c956a685697f3f5db17aca" }; const eq = (a, b) => a === b; const leafOk = hashes.map((h, i) => eq(h, docLeaf[i])); const shuffledRoot = mth(leaves([...items].reverse())); const shuffledRoot2 = mth(leaves([items[2], items[0], items[1]])); const tampered = mth(leaves(items.map((it, i) => i === 0 ? { pinId: it.pinId, cHash: "sha256:" + "0" + it.cHash.slice(7) } : it))); console.log("== A: E1 固定向量 =="); console.log("leafHash[0..2] match:", leafOk.join(",")); console.log("node01 match:", eq(node01, doc.node01)); console.log("root match:", eq(root, doc.root)); console.log("Z-4 reversed-root == root:", eq(shuffledRoot, doc.root)); console.log("Z-4 rotated-root == root:", eq(shuffledRoot2, doc.root)); console.log("Z-5 tamper changes root:", !eq(tampered, doc.root)); console.log("root:", root); // ---------- 腿B:C-1 实测(payload 文件由调用方传入) ---------- const payloadPath = process.argv[2]; if (payloadPath) { console.log("== B: C-1 实测重跑(" + payloadPath + ") =="); const rawBytes = readFileSync(payloadPath); let obj; try { obj = JSON.parse(rawBytes.toString("utf8")); } catch (e) { console.log("payload parse failed:", e.message); process.exit(1); } const hadCHash = Object.prototype.hasOwnProperty.call(obj, "cHash"); if (hadCHash) delete obj.cHash; // C-1:摘 cHash 键(存在则删,不存在则不动) const canon = Buffer.from(jcs(obj), "utf8"); const chash = "sha256:" + sha(canon); const expected = "sha256:8b561c0403a4b3e3c4989429c7aac36cdb4188f588df824455159463b66bd2c9"; console.log("raw payload bytes:", rawBytes.length, "(doc R-1.bytes: 27024)"); console.log("payload had cHash key:", hadCHash, "(期望: 不存在则不动)"); console.log("outer top-level keys:", Object.keys(obj).sort().join(",")); console.log("JCS bytes:", canon.length); console.log("cHash' :", chash); console.log("match R-1.cHash:", eq(chash, expected)); }