How it is built
Architecture overview
Content provenance signing attaches a verifiable history to a piece of media. A capture device or editing tool, the claim generator, collects statements about the asset (who or what made it, what edits happened, which sources were combined), hashes the content, signs the whole bundle with a key whose certificate chains to a trusted authority, and embeds the result in the file. Anyone later can check that the signature is intact, that the content still matches the hashes, that the certificate was valid at signing time, and whether the signer is on a list they trust. The standard for this is C2PA, and its consumer-facing name is Content Credentials.
The one idea to hold on to is that a manifest proves who signed which statements about exactly these bytes, not that the statements are true. Trust therefore moves to the signer's identity, to how well its key is protected, and to the validator's list of trusted authorities. When a manifest is removed from a file, a weaker second binding (a fingerprint or invisible watermark) can find it again in a repository, but it cannot prove the pixels are unchanged.
Parts:
- Signer identity — certificates for the signing key and the chain a validator must trust — x509-certificates (no page yet), ECDSA signatures
- Encoding and container — how claims and signatures are serialised and wrapped in the file — cbor-cose (no page yet)
- Hard binding — hashes tying the manifest to the exact content bytes, with tree hashing for large video — Cryptographic hashes (SHA-2, BLAKE3), merkle-tree (no page yet)
- Soft binding — content-derived identifiers that survive re-encoding and let a lost manifest be found — Perceptual hashing; invisible-watermarking (no page yet), built on Convolutional networks, discrete-cosine-transform (no page yet) and forward-error-correction (no page yet)
- Time and revocation — proof of when the signature was made and that the key was still good — trusted-timestamping (no page yet); certificate-revocation (no page yet), built on Bloom filter and HTTP caching semantics
- Protected signing environment — where the private key lives and how a validator knows the device is genuine — hardware-backed-keystore (no page yet); Secure boot and remote attestation, built on measured-boot (no page yet), trusted-execution-environments (no page yet) and csprng (no page yet)
CLAIM GENERATOR (camera, editor, cloud service) VALIDATOR (viewer, platform)
content bytes ─► hard binding: hash (SHA-2) / box hash / Merkle parse JUMBF ─► claim (CBOR)
assertions: actions, ingredients, metadata, thumbnail, source │
│ │ ├─ signature: COSE_Sign1 + x5chain ─► trust list?
▼ ▼ ├─ time-stamp (RFC 3161) ─► TSA trust list
claim (CBOR) ─► COSE_Sign1 signature (key in secure element) ├─ revocation: stapled OCSP / query
│ + x5chain (signer + intermediates) ├─ assertions + ingredients (recursive)
│ + time-stamp token + OCSP response └─ content hash ─► Valid / Trusted
▼
manifest in JUMBF box ─► embedded in file ──────────────► (optional) soft binding: fingerprint / watermark ─► manifest repository
How the parts fit together
Signer identity
Every claim signature is a COSE_Sign1 structure that carries the signer's credential in its protected header: an x5chain holding the signer's certificate and all intermediate CAs, but not the root (source: C2PA specification 2.1, section 14.5, building on RFC 9360). The validator holds the trust anchors and builds the path itself, treating the supplied chain as untrusted input. x509-certificates (no page yet) are the right credential because they bring issuance, validity periods, key usage constraints and a revocation story that a CA ecosystem already operates. The alternative, a bare public key published by the vendor, has no way to expire, rotate or revoke a single device.
Allowed signature algorithms in the 2.1 specification are ECDSA with SHA-256, SHA-384 or SHA-512 on the P-256, P-384 and P-521 curves, RSASSA-PSS, and Ed25519 (source: C2PA specification 2.1, section 13.2). ECDSA signatures are the common choice because secure elements and phone keystores support them natively. The contract with the next part: the signature covers the claim bytes, and nothing else may carry authority.
Encoding and container
cbor-cose (no page yet) gives a compact, canonical binary form: the claim is CBOR and must follow the core deterministic encoding rules, so the bytes that were signed are the bytes that are verified; the signature uses the COSE_Sign1 structure with the algorithm in the protected header and an empty external-data field (source: C2PA specification 2.1, sections 10 and 13.2). The manifest is wrapped in a JUMBF box (ISO 19566-5), chosen because it can hold several manifests, address elements by URI, carry JSON, CBOR and image data side by side, and already fits JPEG and ISO base media file formats (source: C2PA specification 2.1, section 11.1). A validator ignores boxes it does not recognise.
A manifest is a set of assertions, one claim that lists them by hashed URI, and the claim signature. Manifests in a manifest store can reference each other as ingredients, so a composite image can carry the credentials of its source images, and the last manifest is the active one whose bindings are checked.
Hard binding
The hard binding is a hash that matches only these bytes. A standard manifest must contain exactly one: a data hash over byte ranges, a box hash for JPEG and PNG-like box formats, a BMFF hash for MP4, or a collection hash for sets of files (source: C2PA specification 2.1, section 9.2). The hash algorithms permitted for new content are SHA-256, SHA-384 and SHA-512; SHA-3 is not allowed because COSE had not yet defined signature algorithms using it (source: C2PA specification 2.1, section 13.1). See Cryptographic hashes.
merkle-tree (no page yet) enters for video. A BMFF hash can include Merkle tree rows over fixed or variable blocks of the media data, so a player can validate fragments of a fragmented MP4 as they arrive instead of waiting for the entire file, and the leaf nodes may align to random-access points in the video (source: C2PA specification 2.1, sections 15.12 and A.4). The contract: a hard binding fails on any change to the covered bytes, including legitimate re-encoding, so it distinguishes "this exact file" from "something derived from it".
Soft binding
When social platforms strip metadata or re-encode, the hard-bound manifest is lost or no longer matches. A soft binding is an identifier computed from the content rather than its bytes: a fingerprint (perceptual hashing) or an invisible watermark (invisible-watermarking, no page yet) embedded in the pixels (source: C2PA specification 2.1, sections 2.3.13 and 9.3). Watermark payloads are short and must survive compression and cropping, so they are typically protected with an error-correcting code (forward-error-correction, no page yet) and embedded either in a transform domain such as the discrete cosine transform (discrete-cosine-transform, no page yet) or by a trained encoder and decoder network; Adobe's TrustMark uses neural models (source: TrustMark README), the convolutional-network family. A manifest may hold zero or more soft bindings, and they must come from the C2PA-registered algorithm list. Their job is lookup: the fingerprint or watermark is the key into a manifest repository to find the original credentials. A soft binding must never be used as a hard binding, because it is not statistically unique and cannot prove the content is unmodified.
Time and revocation
trusted-timestamping (no page yet) lets a signature remain verifiable after the signing certificate expires. The generator sends the signature hash to a time-stamping authority and stores the RFC 3161 token in the COSE headers. At validation the attested time, not the current time, is used to check the signer certificate's validity, and the TSA has its own trust list, separate from the signer list (source: C2PA specification 2.1, sections 14.4.2 and 15.8). If a device is offline when it signs, a separate Time-Stamp Manifest can add the token later.
certificate-revocation (no page yet) handles a stolen or misissued key. The generator may staple an OCSP response into the signature headers; a stapled response counts only when a valid time-stamp gives an attested time, and it must show the certificate was not revoked at that time. Without a stapled response the validator may query the OCSP responder (source: C2PA specification 2.1, section 15.9; RFC 6960). Stapling matters for scale and privacy, since viewers do not have to contact the CA when they open an image. Where a client must check offline, a compact revocation set such as a Bloom filter answers "possibly revoked" locally, and HTTP caching semantics decide how long a fetched response may be reused.
Protected signing environment
The private key is the entire security of the system. hardware-backed-keystore (no page yet) keeps it in a secure element or TEE so malware on the device cannot export it. The standard also lets an assertion state where its contents came from, with source types for a claim generator or data provider in a rich execution environment (an ordinary OS), in a trusted execution environment, from a remote provider, or entered by a human (source: C2PA specification 2.1, assertion metadata data source types). Secure boot and remote attestation is how a CA or validator becomes confident that a device key really lives in that hardware and the software running on it is the vendor's: the device proves a measured boot chain (measured-boot, no page yet) and a trusted execution environment (trusted-execution-environments, no page yet) over a fresh random challenge (csprng, no page yet). Issuing signing certificates on a vendor's word, the alternative, leaves any rooted device able to sign. Per the specification, certificates for hardware and software signers trusted to sign claims are rooted in the C2PA Trust List, and validators may add their own anchors (source: C2PA specification 2.1, section 14.4).
Data and control flow
Capture and sign on a camera or phone.
- The capture pipeline produces the image and assertions: capture action, device information, optional location, a thumbnail.
- The generator computes the hard binding over the image bytes with the manifest's own location excluded (or reserved at a fixed size).
- It builds the claim: hashed references to each assertion, the binding, and the generator information, encoded as deterministic CBOR.
- The secure element signs the claim with ECDSA inside a COSE_Sign1 structure; the headers carry the
x5chain, and a time-stamp token from a TSA, plus a stapled OCSP response, when online. - The manifest is wrapped in JUMBF and embedded in the file; optionally a watermark or fingerprint is registered with a repository.
Edit and re-sign.
- The editor reads the active manifest of the input and validates it.
- It writes a new manifest that lists the input as an ingredient, records actions (crop, composite, generative step), and binds the new bytes.
- If only metadata changes and the content does not, an update manifest documents the change without a new content binding; removed assertions are redacted with a record of what was removed (source: C2PA specification 2.1, sections 6.8 and 11.2.3).
- It signs with its own certificate: the new manifest vouches for the edit, the ingredient manifest for the original.
Validate in a viewer.
- Locate the manifest store; if the manifest is gone, try a soft binding lookup in a manifest repository.
- Check the claim signature against the signer's certificate and path to the trust list, then the time-stamp and revocation information.
- Check assertions, then each ingredient manifest recursively, then recompute the hard binding over the content.
- Report a state: well-formed, valid (signature, binding and validity period check out and the credential is not revoked), or trusted (valid and the signer chains to a trusted anchor) (source: C2PA specification 2.1, section 14.3).
Scaling
- Signing volume: one asymmetric signature per asset is cheap, but a service signing millions of generated images needs HSM capacity or batching. Lever: sign at the edge with short-lived credentials; keep the CA in a separate tier.
- Large and live video: hashing a multi-gigabyte file as one block delays validation. Lever: Merkle-tree BMFF hashing validated per fragment.
- Manifest size: nested ingredients grow with every edit. Lever: compressed manifests (Brotli), thumbnails only where needed, external manifests referenced by URI.
- Revocation traffic: viewers querying OCSP for every image overload responders and leak browsing habits. Lever: stapled responses and time-stamps.
- Soft-binding lookups: a repository indexed by fingerprint or watermark must answer near-match queries across billions of assets. Lever: approximate nearest-neighbour search over compact fingerprints; exact lookup for watermark payloads.
- Trust list distribution: anchors and revocation change over time. Lever: validators update trust lists regularly; allow user-configured lists.
Failure modes
| Failure | Symptom | Cause | Fix |
|---|---|---|---|
| Stripped credentials | image shows no provenance | platform or editing tool removes metadata or re-encodes | soft binding lookup in a repository; platforms preserve or re-attach manifests |
| Hard binding broken by benign edit | "tampered" on a resized or recompressed copy | the hash covers exact bytes | treat as a new derived asset: re-sign with the original as ingredient |
| Untrusted signer | manifest valid but not trusted | signer not on the validator's trust list | surface "valid, unknown signer" clearly; allow user trust anchors |
| Compromised signing key | forged credentials from a trusted signer | key exported from a software keystore | key in secure element, short-lived credentials, revocation with time-stamps |
| Expired certificate | old assets start to fail validation | validity checked against the current time | trusted time-stamps so validity is evaluated at signing time |
| Revocation unknown | validators disagree or fail open | no stapled response and the responder is unreachable | staple at signing; define the policy for "unknown" |
| False reassurance | users read "has credentials" as "is true" | assertions are statements by the signer | UI states who signed and what is claimed, not authenticity of the scene |
| Watermark removal or forgery | wrong or missing match from the repository | the watermark was attacked or reused on other content | treat soft matches as hints and verify with the hard binding |
| Redaction abuse | a damaging fact removed from the history | an editor redacts assertions in an ingredient | the claim lists what was redacted, by label; the actions assertion cannot be redacted |
| Privacy leak | location or identity exposed in the manifest | optional assertions embed personal data | make sensitive assertions opt-in; redact before sharing |
Real implementations
| name | what it is | repo | licence | notes |
|---|---|---|---|---|
| c2pa-rs | Rust SDK to create, sign, embed and validate C2PA manifests | contentauth/c2pa-rs | MIT OR Apache-2.0 | reference SDK of the Content Authenticity Initiative; C API for other languages |
| c2patool | command-line tool to display and add manifests | contentauth/c2patool | Apache-2.0 | reads manifest reports and adds manifests to supported formats |
| c2pa-js | JavaScript libraries: c2pa-web for browsers, c2pa-node for Node.js | contentauth/c2pa-js | MIT | browser WebAssembly bindings of c2pa-rs; Node can also add signed manifests |
| c2pa-python | Python binding for c2pa-rs | contentauth/c2pa-python | Apache-2.0 | scripting and pipelines |
| TrustMark | invisible watermarking for images, with an example of recording it in a C2PA manifest | adobe/trustmark | MIT | soft binding by watermark; Python, JS, Rust and C++ runners |
| C2PA Specification | the standard itself, with trust model, validation rules and assertion definitions | c2pa.org | — | version 2.1 used here; the C2PA Trust List defines the default anchors |
The alternative: detect instead of prove. Classifiers that guess whether media is synthetic yield probabilities that decay as generators improve, and say nothing about real media; signed provenance proves origin for participating tools, but says nothing about media from tools that do not sign. Systems use both.
Sources
- C2PA, Technical Specification 2.1 (manifests, bindings, claims, trust model, validation): c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html.
- IETF RFC 9360, COSE: Header Parameters for Carrying and Referencing X.509 Certificates: rfc-editor.org/rfc/rfc9360; RFC 9052, COSE: Structures and Process: rfc-editor.org/rfc/rfc9052; RFC 8949, CBOR: rfc-editor.org/rfc/rfc8949.
- IETF RFC 3161, Time-Stamp Protocol: rfc-editor.org/rfc/rfc3161; RFC 6960, OCSP: rfc-editor.org/rfc/rfc6960; RFC 5280, X.509 certificate profile: rfc-editor.org/rfc/rfc5280.
- Repositories: contentauth/c2pa-rs, contentauth/c2patool, contentauth/c2pa-js, contentauth/c2pa-python, adobe/trustmark.