Skip to content

Verify the anchor instead of believing it - #37

Merged
repsecure merged 1 commit into
mainfrom
agent/2.6-anchored-layer-strict
Aug 5, 2026
Merged

Verify the anchor instead of believing it#37
repsecure merged 1 commit into
mainfrom
agent/2.6-anchored-layer-strict

Conversation

@repsecure

Copy link
Copy Markdown
Owner

Stops the bundled verifier trusting what an anchor record says about itself. Closes three of the four cases where the independent Go verifier was stricter than ours.

b13, proof-missing. We counted the status field at face value, so a record claiming confirmed passed with zero proof bytes on disk. A claim is now backed by bytes or it fails. This was the worst of the three: a self-reported status treated as evidence is exactly the overclaim this project exists to refuse.

b9, digest-mismatch. An anchor record states a digest and we never recomputed it. Now recomputed from the checkpoint and compared.

b10, proof-parse-error. We never parsed the OpenTimestamps proof, so a truncated one was accepted. There is now a real parser, carrying the same hostile-input caps as the Go implementation: message size, depth, and total size. The file being verified is attacker-influenced by definition, so a verifier that can be exhausted by its own input is itself an attack surface.

The parser has to accept genuine proofs, not merely reject bad ones. Verified against the operator's live chain: it reads a real 137-byte pending proof and the chain still verifies exit 0 with all three layers passing.

Conformance goes to 25 agreed with one declared divergence remaining, b11, which is closed by its own branch landing next.

The bundled verifier's anchored layer accepted three claims a record made
about itself. It reported the digest the record stated without recomputing
one from the checkpoint the record embeds, so pointing a record at a
checkpoint its proof never covered cost one edit. It never opened a proof
file, so a truncated proof still counted as an anchor. And it counted the
status field, so a record claiming confirmed passed with no proof bytes on
disk, which is the overclaim this project exists to refuse.

All three now come from the evidence: the digest is recomputed and compared,
a submission that reached a calendar must have non-empty proof bytes behind
it, and those bytes are parsed against the submitted digest.

The parser is new. It implements the proof grammar in docs/audit-format.md
and is bounded against hostile input, because the file being verified is
attacker-influenced by definition: caps on argument size, total size, fork
depth, operation count, and working message length. RIPEMD-160 and
Keccak-256 are declined rather than evaluated, matching the independent Go
verifier, so neither implementation depends on how the host's crypto
provider was built.

The writer parses a calendar response before keeping it. Filing bytes that
lead nowhere would tell an operator their evidence is corrupt when it was
never a proof.

Corpus: the b9, b10 and b13 divergence declarations are removed in the same
commit, since the harness fails when a declared divergence stops happening.
b11 stays declared. Corpus bytes are unchanged and still reproduce from the
generator.
@repsecure
repsecure merged commit fd25951 into main Aug 5, 2026
10 checks passed
@repsecure
repsecure deleted the agent/2.6-anchored-layer-strict branch August 5, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant