Example
Left: original clip. Right: same clip with one person covered by a moving black rectangle, a privacy-style redaction. The point of the proof is that only that edit happened, not a person added or removed, a sign rewritten, or the background altered.
Original
Edited
One control plays and scrubs both clips together.
Scrub to about 2.0s–4.5s to see the box follow that person on the edited clip.
This example demonstrates the edit-proof step, not the full pipeline. There is no hardware-rooted capture signature here (no secure-element signing in a real camera). The left clip is an original recording; the right carries a proof that only the redaction was applied. For capture signing, cameras, and the rest of the path, see How it works and Hardware.
Clone the repository, run prove-edit on the same demo clip, then verify the proof file.
Prove edit
cargo run -p fightfake-cli --release --features eva-backend -- prove-edit \ --input testdata/videos/input/demos1.mp4 \ --gadget redact \ --redact-track out/demos1-track/redact-track.json \ --redact-frame-start 48 --redact-frame-end 108 \ --redact-fill 0 \ --touched-window --blocks-per-step 720 \ --out-dir out/demos1-track
Verify proof
./target/release/fightfake verify-proof --proof out/demos1-track/proof.bin
The zero-knowledge proof does not say “trust us, we blurred someone.” It says, mathematically:
The published edited video is the result of applying only the declared redact gadget (a moving black rectangle over a declared frame range) to the original clip. The original pixels are fingerprinted as hash h1; the edited pixels as hash h2. The proof says the edit turns that h1 into that h2, and that no other pixel was changed.
The table below summarizes this specific example edit: redaction is active only on a limited frame window, and everything outside those frames remains unchanged.
| Claim | Meaning |
|---|---|
| Gadget = redact (tracked) | Only macroblocks inside the per-frame rectangle are replaced with solid black (Y=0, i.e. luminance set to zero). |
| Frame window 48–107 | Redaction is active only in that range (~2.0s–4.5s at 23.98 fps). |
| 30 keyframes + interpolation | The box moves smoothly between keyframes; outside the window the edit is identity. |
| h1 → h2 linkage | h1 is the fingerprint of the original pixels; h2 of the edited ones. The C2PA assertion ties the proof to both; the verifier checks they match the manifest. |
| Proof system | The proof system confirms the declared edit rules were followed on the original clip to produce this edited clip, without revealing private intermediate data. |
| Claim | Meaning |
|---|---|
| Gadget = redact (tracked) | Only macroblocks inside the per-frame rectangle are replaced with solid black (Y=0, i.e. luminance set to zero). |
| Frame window 48–107 | Redaction is active only in that range (~2.0s–4.5s at 23.98 fps). |
| 30 keyframes + interpolation | The box moves smoothly between keyframes; outside the window the edit is identity. |
| h1 → h2 linkage | h1 is the fingerprint of the original pixels; h2 of the edited ones. The C2PA assertion ties the proof to both; the verifier checks they match the manifest. |
| Proof system | The proof system confirms the declared edit rules were followed on the original clip to produce this edited clip, without revealing private intermediate data. |
What this demo does not include: Hardware-rooted capture signing (secure element on a camera). The left clip is a normal recording; the proof covers the edit step only. See how it works for the full capture → edit → verify path.
A 4K clip (3840×2160, 168 frames) has ~5.44 million 16×16 macroblocks. Our redaction only changes 60 frames (48–107). Proving every macroblock in the whole clip just to attest a ~2.5s edit is wasteful.
With --touched-window (redact only), the prover splits the video into three segments. Published h1 / h2 (fingerprints of the original and edited clips) combine all three segment hashes. The expensive SNARK runs only on MID, roughly a ~2.8× reduction vs full-clip proving for this window.
| Segment | Frames | Macroblocks (approx.) | How it’s attested |
|---|---|---|---|
| PRE | 0–47 | ~1.54M | Plain SHA-256 (bytes unchanged → hash proves identity) |
| MID (touched) | 48–107 | ~1.94M | Full Nova IVC + Groth16 with per-macroblock RedactRectCfg (moving box) |
| POST | 108–167 | ~1.94M | Plain SHA-256 |
| Segment | Frames | Macroblocks (approx.) | How it’s attested |
|---|---|---|---|
| PRE | 0–47 | ~1.54M | Plain SHA-256 (bytes unchanged → hash proves identity) |
| MID (touched) | 48–107 | ~1.94M | Full Nova IVC + Groth16 with per-macroblock RedactRectCfg (moving box) |
| POST | 108–167 | ~1.94M | Plain SHA-256 |
The proof checks decoded pixels (not just file bytes): applying only the declared redaction in frames 48–107 turns h1 into h2 with no other pixel changes.
A useful mental model is to separate encoded bytes from decoded pixels. The proof is about pixel-level edit correctness; C2PA binds the published assets.
Input
Original MP4
compressed bytes
Proved domain
Frames / macroblocks
redact only in touched window
Output
Edited MP4
compressed bytes
ZK statement: from the original decoded pixels (h1), applying only the declared tracked redaction on frames 48–107 yields the edited decoded pixels (h2), with no other pixel changes.
C2PA statement: the published files and assertions are cryptographically bound and integrity-checked.
Generating the edit proof for this 4K clip takes on the order of ~1–1.5 hours when scoped to the touched frame window (--touched-window). The prover checks only the macroblocks where the redaction can differ; untouched frames are hash-anchored instead.
Speeding this up is a focus for the coming months; progress will be reported on the blog.
~87–91 min ZK proving (~88–92 min total) for a smaller fixed-box window on Apple M1 with eva-backend
Other phases (typical): ffmpeg decode ~1s, edit + hashing ~35s, re-encode ~10s, C2PA ~0.2s.
Readers (or fightfake.ai’s verifier / a browser extension) check the C2PA manifest and run a cryptographic verifier on proof.bin in about 1–2 seconds, with no trust in the publisher’s server required.
Verification is intentionally fast: proving is heavy once; checking is cheap enough for a browser extension.