Sample files anyone can check without building the app - #70
Merged
Conversation
Every claim on the project page is checkable, and until now checking one meant building the app first. Five files: a sealed photo, a sealed video, one bit flipped inside each picture, and one bit flipped inside a credential. The failing files matter more than the sealed pair — a file that verifies proves the verifier says yes to something, and a set with only good files demonstrates nothing an empty verifier could not. Two kinds of failure, because they are not the same failure. Change the picture and the signature still checks out while the media no longer matches what it covers. Change an assertion and the claim's own hash of it stops matching: the credential contradicts itself. A verifier that collapses those into one word tells a reader less than it knows. The verdict table records what c2patool actually printed, not what anyone expected it to print, because the codes differ by container and by which part of the credential was attacked. Writing them from memory put two wrong claims in the document that exists to be checked, which is how they were found. The tool refuses to write a shippable set when any file does not do what the table says.
Five files anyone can download and run through a verifier without building the app: a sealed photo, a sealed video, one bit flipped inside each picture, and one bit flipped inside a credential. They live in the tree rather than as release assets, so a clone gets them and a link to them never expires. README.md rather than a bare listing, so browsing to the directory renders the verdict table. The hashes in that table are of these exact files and not of a reproducible build: an ECDSA signature varies in DER length run to run and a capture carries the time it was made. They are there so a reader can tell whether the file they downloaded is the file that was published, which is verified above.
'A sealed capture' invites the reading that something was photographed. Nothing was. The pictures are generated test patterns and a lab key sealed them, so there is no Enclave signature, no Apple attestation and no sensor record behind any of them. That changes nothing about what they demonstrate — INTACT has always meant these bytes are the bytes that were signed, never that a real thing was in front of a real lens, and a test pattern verifies exactly as cleanly as a photograph would. But leaving it unsaid lets a reader take INTACT for a claim about the world, which is the overclaim this project spends most of its copy avoiding. A capture from an actual device carries more, and cannot be generated here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every claim on the project page is checkable, and until now checking one meant building an iOS app first. This makes it a download.
The files
sealed-photo.jpgaltered-photo.jpgassertion.dataHash.mismatchsealed-video.mp4altered-video.mp4assertion.bmffHash.mismatchattacked-manifest.jpgassertion.hashedURI.mismatchIn
samples/, with aREADME.mdGitHub renders when you browse to the directory, and averdicts.jsonfor anything scripted.Two failures, not one
The failing files matter more than the sealed pair. A file that verifies proves the verifier says yes to something; a set with only good files demonstrates nothing an empty verifier could not.
And the two failures are not the same failure. Flip a bit inside the picture and the signature still checks out while the media no longer matches what it covers. Flip one inside an assertion and the claim's own hash of it stops matching — the credential contradicts itself. A verifier that collapses those into one word tells a reader less than it knows, so the set demonstrates both.
The table records what the tool printed
tests/tool-samples.mtsbuilds the files, verifies each one, and runs c2patool against it when it is present. The failure codes in the table are what c2patool actually printed, not what anyone expected it to print.That matters because writing them from memory put two wrong claims in the very document that exists to be checked. The video path emits
assertion.bmffHash.mismatch, notdataHash; the manifest attack emitsassertion.hashedURI.mismatch, not a bad-signature error. Both were wrong until the tool was asked.The tool exits non-zero and refuses to write a shippable set when any file disagrees with its table.
Two things found while building it
The verdict vocabulary. The first pass expected
TAMPERED; the verifier saysCONTENT_MODIFIED. Comparing expected against actual caught it on the first run, which is why it is built that way.Where to attack the manifest. The first attempt flipped a byte at the segment midpoint, which landed in the RFC 3161 token. c2patool cannot parse the corrupted DER and exits with a stack backtrace instead of a verdict — a useless sample. It now targets a quarter into the store, where the claim and assertions sit, and walks to the first byte whose flip actually breaks the signature rather than assuming one is load-bearing.
On the hashes
They are of these exact files, not of a reproducible build: an ECDSA signature varies in DER length run to run, and a capture carries the time it was made. They are there so a reader can tell whether the file they downloaded is the file that was published. The committed files were checked against them.
Files live in the tree rather than as release assets, so a clone gets them and a link never expires.
tscclean, knip clean, dependency budget clean, 31/31 suites.Generated by Claude Code