This walkthrough demonstrates how a HATS verifier detects post-signing modification of a governance node. The input bundle is identical to the valid bundle in HATS-VW-001, except that node 1's action_type field has been changed from COMPUTE_EXECUTE to DATA_EXPORT after the node was signed. This modification invalidates the node hash and, consequently, all signatures over that hash.
The expected verifier output is FAILED, with the failure localized to node 1.
node_index, action_type, payload_hash, timestamp, predecessor_hash. Whitespace is normalized. Changing any field produces a different serialization and therefore a different hash.node_hash field. Indicates that the node's content was modified after the hash was computed.node_hash, and the node_hash is computed over the canonical serialization, any content change invalidates both the hash and all signatures.The following bundle is identical to the valid bundle except for node 1. The action_type field has been changed. The modification is highlighted below.
{
"node_index": 1,
"action_type": "COMPUTE_EXECUTE",
"action_type": "DATA_EXPORT",
"payload_hash": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
"timestamp": "2026-05-15T14:30:01.247Z",
"predecessor_hash": "c7a8b9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7",
"node_hash": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
"signatures": {
"ml_dsa_65": "ML-DSA-65:3045022100...truncated...c9d0e1f2a3",
"falcon_512": "FALCON-512:3045022100...truncated...b2c3d4e5f6",
"slh_dsa": "SLH-DSA-SHA2-128f:3045022100...truncated...a7b8c9d0e1"
}
}The attacker changed COMPUTE_EXECUTE to DATA_EXPORT but did not (and could not, without the private keys) recompute the node_hash or re-sign the node. The node_hash and all three signatures still correspond to the original COMPUTE_EXECUTE content.
$ hats verify tampered-receipt.jsonThe verifier computes the canonical serialization of node 1 using the current field values (including the tampered action_type = DATA_EXPORT). It then computes SHA3-256 over this serialization.
| Value | SHA3-256 Digest |
|---|---|
Declared node_hash | e5f6a7b8c9d0e1f2a3b4c5d6e7f8...d4e5 |
Recomputed (with DATA_EXPORT) | 1a2b3c4d5e6f7a8b9c0d1e2f3a4b...8c9d |
The digests differ. The verifier reports a node hash mismatch at node 1.
Once the node hash mismatch is detected, the verifier also notes that:
node_hash, not the recomputed digest.predecessor_hash chain remains intact (it references node 1's declared node_hash, which has not been changed). However, the declared node_hash itself is invalid, so the chain is semantically broken even though the predecessor link is syntactically correct.The verifier halts at the first failure by default. With --continue-on-error, it reports all failures but still returns a non-zero exit code. The hash mismatch is always reported before signature failures because hash verification precedes signature verification in the check order.
$ hats verify tampered-receipt.json
HATS Verifier v1.0.0
Bundle: b8f3c2a1-4e5d-4a6b-9c8d-7e6f5a4b3c2d
Session: s-20260515-143000-a1b2c3
Checking schema .............. OK
Checking chain integrity ..... OK (3 links)
Checking node hashes ......... FAILED
Node 1 [DATA_EXPORT]: hash mismatch
declared: e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5
recomputed: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0a1
Result: FAILED
Failure: NODE_HASH_MISMATCH at node 1
Field: action_type (declared: DATA_EXPORT)
Impact: All signatures on node 1 are invalid (signed over different content)
Duration: 12ms{
"status": "FAILED",
"bundle_id": "b8f3c2a1-4e5d-4a6b-9c8d-7e6f5a4b3c2d",
"failure": {
"type": "NODE_HASH_MISMATCH",
"node_index": 1,
"action_type": "DATA_EXPORT",
"declared_hash": "e5f6a7b8c9d0e1f2...d4e5",
"recomputed_hash": "1a2b3c4d5e6f7a8b...e0a1",
"message": "Node 1 canonical serialization hash does not match declared node_hash. Content was modified after hashing."
},
"nodes_checked": 2,
"nodes_passed": 1,
"duration_ms": 12
}The HATS governance model binds content to cryptographic identity through a two-layer commitment:
node_hash is the SHA3-256 digest of the node's canonical serialization. Changing any field changes the serialization, which changes the digest, which creates a mismatch with the declared node_hash.node_hash. Even if an attacker recomputes the node_hash after modification, they cannot produce valid signatures without the private keys for all three families.An attacker would need to compromise ML-DSA-65, FALCON-512, and SLH-DSA-SHA2-128f private keys simultaneously to forge a tampered node that passes verification. This requires breaking three independent post-quantum hardness assumptions.
| Check | Status | Detail |
|---|---|---|
| Schema validation | PASS | DATA_EXPORT is a valid action type |
| Chain integrity | PASS | Predecessor hashes link correctly (syntactically) |
| Node 0 hash | PASS | Unmodified node |
| Node 1 hash | FAIL | Hash mismatch: content changed post-hashing |
| Node 1 ML-DSA-65 | FAIL | Signature over wrong hash (skipped: hash already failed) |
| Node 1 FALCON-512 | FAIL | Signature over wrong hash (skipped: hash already failed) |
| Node 1 SLH-DSA | FAIL | Signature over wrong hash (skipped: hash already failed) |
| Node 2 hash | SKIPPED | Verification halted at node 1 |
Conformance test vector ID: HATS-VW-002-TAMPERED-RECEIPT