Article

How do you know a C2PA verifier is right?

A verifier that wrongly says Valid is worse than no verifier at all. So "it passes its own tests" can't be the bar. This is how I've tried to earn the other kind of confidence for a new C2PA verifier in PHP, and what turned up when I did.

My first library signs through a separate service, which is the right call for the private key. It also means most WordPress sites can't read a credential at all. Shared hosting runs no second process, no PHP extension and no binary, so no c2patool, no ext-c2pa and no Docker. The PHP plugins that exist today find the manifest in the file and read a string out of it. Finding a manifest isn't verifying it.

So there is now a second library that only verifies, and runs anywhere PHP 8.3 runs: c2pa-verifier. It checks the signature, the hash binding to the actual bytes of a JPEG, PNG, WebP, MP4, MOV, AVIF or HEIC file, the certificate chain against a trust list you supply, the timestamp, and the earlier manifests a file carries as ingredients. It makes no network connection and holds no keys.

$report = (new Verifier)->verify($stream, $settings);
$report->result->state;   // Trusted, Valid or Invalid

Same answer as the reference

The bar I picked is simple to state: it should give the same answer as c2patool, the reference tool built on c2pa-rs. Same verdict, same status codes, file by file. Every fixture in the repository is compared with what c2patool says, and those comparisons run again on every commit. Every rule added since c2patool 0.28 came out was first measured on signed test files under both 0.27 and 0.28, then written down as a specification, then built.

Agreeing with one oracle twice isn't independence, though. A mistake c2pa-rs and I happened to share would stay invisible. So I also ran an independently written verifier in Go over the same 257 files. Where we differed, it was either one of us being deliberately stricter, or, once, a real mistake. That one turned out to be on their side, in how an appended update manifest shifts the data hash. It's reported now.

The last part is being honest about what isn't done. C2PA 2.4 has 111 obligations that apply to these formats. docs/conformance.md goes through them one by one, and names the 11 this verifier doesn't meet yet and what each would cost. Every place it answers differently from c2patool is listed, with the reason, in docs/comparison.md.

The time the reference was wrong

It has said Valid when it shouldn't three times, all found here, before anyone used it. The third one is the instructive one.

A C2PA data hash covers the whole file except the bytes where the manifest itself lives. That gap is declared as an exclusion. Three of the official test files, from Truepic, declare an exclusion that holds the manifest and the whole EXIF block next to it. My verifier accepted that, because the exclusion covered the manifest. So did c2patool 0.27. I took a copy, changed the capture date in the EXIF, and the file stayed Trusted. The date a photo was taken, changed, with a green check next to it.

The specification says an exclusion that holds the manifest may hold nothing else, and c2patool 0.28 had started enforcing that. Measuring the whole corpus against the new version is how the difference showed up. It was fixed the same day. Over 864 runs the fix changed the verdict of those three files and no others.

That's the point of measuring rather than believing. My own tests were green the whole time. They tested what I thought the rule was.

How it's built

Like the first library, it's spec-driven. Every feature starts as a written specification, and the tests are seen failing before any code exists. I built it with Claude Code, and the repository keeps a log of what the AI produced in each step (AI-LOG.md). The measurements against c2patool are what make that safe to rely on, whoever wrote the code.

Limits, up front

Where to start

As with the first library, I'd rather have a handful of careful users than a splash. If you work on the WordPress side of this, there's a proposal for keeping credentials on generated image sizes in WordPress/ai#1058.

provemark/c2pa-verifier is an open-source (MIT) C2PA verifier in pure PHP for JPEG, PNG, WebP and ISOBMFF, with c2patool's verdicts. Its NOTES.md is the full record of every step and measurement above.