Skip to content

Evidence pack integrity hash does not cover meta, so the date range and producer can be rewritten #75

Description

@blitzcrieg1

build_evidence_pack computes meta.integrity_sha256 over the body only:

pack["meta"]["integrity_sha256"] = _integrity_hash(body)

verify_evidence_pack recomputes over the same _BODY_KEYS. Nothing in meta is covered, so every field in it can be rewritten and the pack still verifies clean.

Reproduce

pack = build_evidence_pack(date(2000, 1, 1), date(2000, 1, 2))
pack["meta"]["producer"] = "somebody-else/9.9.9"
pack["meta"]["date_to"] = "2031-12-31"
verify_evidence_pack(pack)   # -> (True, "integrity verified")

agentmetry verify <pack> prints OK — integrity verified and exits 0.

Why it matters

The date range is the worst of them. A pack can be relabelled to claim a period it does not cover, and the events inside it are unchanged so nothing else contradicts the label. An auditor reading the pack has no way to tell.

meta.producer is next. The comment sitting directly above it in evidence_pack.py says:

An auditor reading this pack in 2028 needs to know which build produced it: rules, redaction, and mappings all move between versions. Provenance, not decoration.

tests/test_version.py makes the same argument: "Evidence packs record the producing version, so a version that disagrees with the tag is a provenance defect, not cosmetics." Both are right, and neither is enforced.

meta.trail_chain is also uncovered, which is the chain head state the pack asserts about the trail it was drawn from.

The awkward part

Widening the hash to include meta invalidates every pack already issued, because their stored digest was computed over the body alone. Options, roughly in order of how much I like them:

  1. Hash meta minus integrity_sha256, and bump the pack schema version. verify accepts the old scope for packs below the new version and the new scope at or above it. Old packs keep verifying, new ones are properly covered, and the version says which rule applies.
  2. Add a second field (meta_sha256) beside the existing one. Simpler, but leaves two digests and an obvious question about why.
  3. Leave it and document the limit in SECURITY.md. Cheapest, and wrong for an artifact whose whole purpose is being handed to somebody who does not trust you.

Option 1 unless there is a reason not to.

Test

tests/test_cli_commands.py::test_verify_accepts_a_pack_whose_meta_was_edited currently asserts the broken behaviour, deliberately, with a docstring pointing here. When this is fixed that test flips to asserting failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions