C2PA Content Credential Photo Forensics
C2PA Photo Forensics in Forensically
I have added support for reading C2PA metadata to my photo forensics tool Forensically. C2PA is the Coalition for Content Provenance and Authenticity. In their own words:
The Coalition for Content Provenance and Authenticity (C2PA) addresses the prevalence of misleading information online through the development of technical standards for certifying the source and history (or provenance) of media content.
The C2PA metadata is signed and optionally timestamped using a timestamp authority.
Cryptography & PKI
It’s important to note that digital signatures do not inherently guarantee trustworthiness. Even if the scheme is cryptographically sound you still need to trust everyone and everything with access to the keys.
Sadly the C2PA Javascript library doesn’t reveal a lot of useful information, essentially just the Organization of the Subject from what I can tell.
"signatureInfo": {
"alg": "Es256",
"issuer": "C2PA Test Signing Cert",
"cert_serial_number": "640229841392226413189608867977836244731148734950",
"time": "2025-04-06T20:32:57+00:00"
}
Neither does contentcredentials.org/verify.
There is a “known certificate list” and an email address to send in your certificate to be included. I’m not exactly sure what information is validated before a certificate is included.
As is, I’d take the signatures displayed in Forensically with a big pinch of salt.
Privacy and Tracking Concerns
C2PA includes support for remote manifests.
There are definitely use cases where having remote manifests makes sense, for instance to keep the file size of an asset small. They can however be used as beacons to track a person viewing the metadata.
For Forensically I have decided to disable support for remote manifests, both using configuration options and by blocking requests using a Content Security Policy.
File Sizes
Another little headache while implementing this was the size of the c2pa-js library. Using my Vite setup it compiles down to about 350 kB for the main ES module, 165 kB for a worker and 5.4 MB for the WebAssembly module.
To mitigate the impact of this I’m lazy loading the library only when needed. This brought up another issue, how do I figure out whether an image contains C2PA data without loading all of that code?
There is the @contentauth/detector package for that but it also weighs about 70 kB of WASM and JavaScript.
Ultimately, I wrote my own detection code. Luckily the pattern to be searched for doesn’t repeat the initial byte anywhere. So searching for it can be done efficiently using a simplified version of Knuth-Morris-Pratt.
My JavaScript implementation weighs about 300 bytes after minification.