Skip to content

AI Content Authenticity

**AI · Media provenance · C2PA+ · Editing chain of custody**

Overview

AI Content Authenticity

AI · Media provenance · C2PA+ · Editing chain of custody

The problem

Photos, videos, and audio are becoming indistinguishable from synthetic generation. The C2PA (Coalition for Content Provenance and Authenticity) standard embeds signed metadata in media files claiming “this was captured by camera X at time T”, but it has gaps:

  1. Identity root of trust. C2PA manifests are signed by a cert chain. Who validates the chain and says “yes, this cert is really Reuters’”? In practice, a handful of centralized issuers, reverting to a PKI trust model.
  2. Editing trust. A news photo is captured, cropped, color-graded, and published. Each edit adds a C2PA signature, but there’s no clear “did I trust this specific editor’s cert” answer.
  3. Cross-platform reuse. A photo captured on a Canon camera, edited in Adobe Lightroom, and published to a news site crosses three trust domains. C2PA handles the chain but consumers don’t see the trust implications differently.
  4. Revocation is slow. If a camera maker’s cert is compromised, revoking it takes days; everything signed in the interim is ambiguous.
  5. Per-consumer trust. A news organization trusts Reuters editors; a meme site trusts its own community. C2PA treats trust as binary (signature valid or not).

Why Quidnug fits

Media assets have natural identities (the asset itself is a thing), and provenance is a chain of signed operations on that thing. That’s title + events + relational trust.

ProblemQuidnug primitive
”Is this the original capture?”TITLE with camera manufacturer as creator
”Has it been edited?”capture, crop, grade, publish events
”Who edited it and when?”Event signed by editor’s quid
”Does this consumer trust that editor?”Relational trust
”Compromised editor’s key”Guardian recovery rotates
”Revoke a whole issuer line”Trust edge set to 0; gossip propagates
”Cross-platform trust federation”Domain hierarchy + cross-domain anchors

High-level architecture

┌─────────────────────────────────────┐
│ media.provenance.news (domain) │
│ │
│ Trust roots: Reuters, AP, camera │
│ manufacturers, editing software │
│ providers │
└─────────────────────────────────────┘
Asset-level flow:
Camera ──────capture──────> Asset (TITLE)
│ │
│ │ Edit events:
▼ ▼
Camera's quid ┌──────────┐
signs the │ crop │ ← photographer's quid
initial capture │ grade │ ← editor's quid
│ overlay │
│ caption │
└──────────┘
┌──────────┐
│ publish │ ← news org's quid
└──────────┘

Data model

Quids

  • Camera manufacturer (e.g., Canon, Sony, RED). Each signs a capture attestation event.
  • Photographer/videographer: individual who captured.
  • Editor (human or software): performs edits.
  • Publisher (news org, agency, platform): final endorser.
  • Fact-checker / integrity-assessor: independent validators that sign assessments.

Domain

media.provenance
├── media.provenance.news
├── media.provenance.entertainment
├── media.provenance.evidentiary (legal, police body cams)
└── media.provenance.social (user-generated)

Asset as title

{
"type":"TITLE",
"assetId":"photo-canon-capture-a1b2c3",
"domain":"media.provenance.news",
"titleType":"media-asset",
"owners":[{"ownerId":"photographer-jane-doe","percentage":100.0}],
"attributes":{
"assetType":"photo",
"format":"JPEG",
"capturedAt":1713400000,
"captureDevice":"canon-5d-mark-iv-serial-123",
"captureGeoHash":"9q8zn..." /* or "hidden" */,
"assetContentHash":"<sha256 of the original pixel data>",
"captureLocation":"Austin, TX",
"cameraSignatureAlgorithm":"ECDSA-P256"
},
"creator":"photographer-jane-doe",
"signatures":{
"photographer-jane-doe":"<sig>"
}
}

Capture event

Cameras with C2PA hardware sign a capture event as soon as the shutter fires:

eventType: "media.captured"
subjectId: "photo-canon-capture-a1b2c3"
payload:
captureDevice: "canon-5d-mark-iv-serial-123"
originalHash: <sha256 of raw image>
captureParams: { iso, aperture, shutter, lens }
timestamp: 1713400000
signer: canon-5d-mark-iv-serial-123 (each camera has a quid,
signed with per-device key)

The camera itself is a quid with its own signing key. Canon operates guardian recovery infrastructure for when a camera’s key chips fail.

Edit events

Each edit is a signed event:

eventType: "media.cropped"
payload:
editor: "photographer-jane-doe"
cropBox: { x1: 100, y1: 200, x2: 2000, y2: 1500 }
inputHash: <prior state>
outputHash: <new state>
software: "Adobe Lightroom 13.0.1"
signer: photographer-jane-doe
eventType: "media.color-graded"
payload:
editor: "editor-reuters-staff-mark"
gradeParams: { exposure: -0.3, shadows: +0.5, ... }
inputHash, outputHash
signer: editor-reuters-staff-mark
eventType: "media.captioned"
payload:
caption: "Protesters gather outside capitol building..."
signer: editor-reuters-staff-mark
eventType: "media.published"
payload:
publisher: "reuters"
storyID: "reuters-story-12345"
publishedAt: 1713500000
signer: reuters

Fact-checker endorsement (optional)

eventType: "media.fact-checked"
payload:
checker: "fact-check-org-maldita"
assessment: "consistent-with-contextual-evidence"
evidence: <hash of fact-check report>
signer: fact-check-org-maldita

Per-consumer trust evaluation

A news organization viewing an asset:

type AssetTrust struct {
AssetID string
CaptureTrust float64 // trust in camera manufacturer + photographer
EditTrust float64 // trust in all editors in the chain
PublisherTrust float64
FactCheckBonus float64 // additional if trusted fact-checker endorses
Overall float64
}
func (org *NewsOrg) EvaluateAsset(ctx context.Context, assetID string) (AssetTrust, error) {
title, _ := org.client.GetTitle(ctx, assetID)
events, _ := org.client.GetSubjectEvents(ctx, assetID, "TITLE")
result := AssetTrust{AssetID: assetID}
// Capture step
capturer := title.Creator
captureTrust, _ := org.client.GetTrust(ctx, org.quid, capturer,
"media.provenance.news", nil)
result.CaptureTrust = captureTrust.TrustLevel
// Editors, take MINIMUM trust across all editors in the chain
// (a single low-trust editor taints the chain)
minEditTrust := 1.0
for _, ev := range events {
if strings.HasPrefix(ev.EventType, "media.") &&
ev.EventType != "media.captured" &&
ev.EventType != "media.published" &&
ev.EventType != "media.fact-checked" {
editor := ev.Payload["editor"].(string)
editorTrust, _ := org.client.GetTrust(ctx, org.quid, editor,
"media.provenance.news", nil)
if editorTrust.TrustLevel < minEditTrust {
minEditTrust = editorTrust.TrustLevel
}
}
}
result.EditTrust = minEditTrust
// Publisher
for _, ev := range events {
if ev.EventType == "media.published" {
publisher := ev.Payload["publisher"].(string)
pubTrust, _ := org.client.GetTrust(ctx, org.quid, publisher,
"media.provenance.news", nil)
result.PublisherTrust = pubTrust.TrustLevel
}
}
// Fact-checker
for _, ev := range events {
if ev.EventType == "media.fact-checked" {
checker := ev.Payload["checker"].(string)
checkerTrust, _ := org.client.GetTrust(ctx, org.quid, checker,
"media.provenance.news", nil)
if checkerTrust.TrustLevel >= 0.8 {
result.FactCheckBonus = 0.1
}
}
}
result.Overall = min(result.CaptureTrust, result.EditTrust, result.PublisherTrust) + result.FactCheckBonus
return result, nil
}

Different consumers see different overall trust levels for the same asset.

AI-generated content

Synthetic content doesn’t have a “camera” in the capture step. Instead:

eventType: "media.generated"
payload:
generator: "acme-image-model-v2"
prompt: "Sunset over mountains"
seed: 42
guidanceScale: 7.5
modelHash: <hash>
outputHash: <hash>
signer: acme-image-model-v2 (the model itself is a quid)

Chain of editing can still proceed. A consumer can see “this asset was originally generated by a model” and weigh accordingly.

This composes with the ai-model-provenance use case: the model’s own provenance chain is available.

Revocation

Compromised camera key: Canon initiates guardian recovery for the affected device’s quid. Old-epoch signatures stop validating.

Compromised publisher key: Reuters rotates via their own guardian set. Old signatures are now from a stale epoch; consumers that see them via gossip will probe and detect the rotation.

Untrustworthy editor exposed: consumers lower trust edges to that editor. Gossip propagates the change within minutes.

Key Quidnug features

  • Titles for individual media assets.
  • Event streams for the full edit chain.
  • Signed events by each editor’s quid.
  • Relational trust for per-consumer trust evaluation.
  • Guardian recovery for camera / editor / publisher key loss.
  • Push gossip for rapid revocation propagation.
  • Domain hierarchy for scoping (news vs. evidentiary vs. social).

Value delivered

DimensionBeforeWith Quidnug
Capture attestationC2PA hardware certs (centralized PKI)Camera quid + guardian-recoverable key
Editing chainC2PA manifest (cert-chain based)Event stream with per-editor quids
Consumer-specific trustBinary (valid / invalid)Relational, per-domain
Revocation propagationCert revocation lists (slow)Push gossip + trust-edge update
Cross-platform reuseManual per-platform verificationSingle trust graph spans domains
Evidentiary-grade mediaCourt-admissible chain of custody rareProtocol-level tamper-evident chain

What’s in this folder

Runnable POC

Full end-to-end demo at examples/ai-content-authenticity/:

  • content_authenticity.py, pure decision logic: hash-chain check, min-of-chain trust, fact-check bonus, AI-generation policy knob.
  • content_authenticity_test.py, 12 pytest cases.
  • demo.py, three scenarios (authentic news photo, tampered chain, AI-generated) with three policy variants for the AI case.
Terminal window
cd examples/ai-content-authenticity
python demo.py

Implementation

Concrete API calls, pseudocode, signing shape.

Implementation: AI Content Authenticity

1. Identity setup

Camera manufacturer

Terminal window
curl -X POST $NODE/api/identities -d '{
"quidId":"canon-corp",
"name":"Canon Inc.",
"homeDomain":"media.provenance.news",
"creator":"canon-corp","updateNonce":1
}'
# Plus guardian set (corporate recovery structure)

Each camera as a quid

Terminal window
# Camera manufacturer registers each produced device
curl -X POST $NODE/api/identities -d '{
"quidId":"canon-5d-mark-iv-serial-123",
"name":"Canon 5D Mark IV #123",
"creator":"canon-corp","updateNonce":1,
"attributes":{
"model":"Canon 5D Mark IV",
"serialNumber":"123",
"manufactureDate":"2024-06-15",
"firmwareVersion":"1.2.3"
}
}'
# Canon publishes a trust edge endorsing the device
curl -X POST $NODE/api/trust -d '{
"truster":"canon-corp",
"trustee":"canon-5d-mark-iv-serial-123",
"trustLevel":1.0,
"domain":"media.provenance.news.capture",
"description":"Authentic Canon device; factory-signed firmware"
}'

Photographer, editor, publisher quids, similar.

2. Capture event

Camera firmware signs internally (via HSM chip) as the shutter fires:

Terminal window
# Camera uploads the capture event on reconnection
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"photo-canon-capture-a1b2c3",
"subjectType":"TITLE",
"eventType":"media.captured",
"payload":{
"captureDevice":"canon-5d-mark-iv-serial-123",
"originalHash":"<sha256 of raw image>",
"captureParams":{"iso":200,"aperture":2.8,"shutter":"1/125"},
"timestamp":1713400000,
"geoHash":"9q8zn..."
},
"creator":"canon-5d-mark-iv-serial-123","signature":"<sig>"
}'
# Title created with photographer as owner
curl -X POST $NODE/api/v1/titles -d '{
"assetId":"photo-canon-capture-a1b2c3",
"domain":"media.provenance.news",
"titleType":"media-asset",
"owners":[{"ownerId":"photographer-jane-doe","percentage":100.0}],
"attributes":{
"assetType":"photo",
"capturedAt":1713400000,
"captureDevice":"canon-5d-mark-iv-serial-123",
"assetContentHash":"<sha256>"
},
"signatures":{"photographer-jane-doe":"<sig>"}
}'

3. Edits

Each edit creates a new event:

Terminal window
# Photographer crops in Lightroom
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"photo-canon-capture-a1b2c3",
"subjectType":"TITLE",
"eventType":"media.cropped",
"payload":{
"editor":"photographer-jane-doe",
"cropBox":{"x1":100,"y1":200,"x2":2000,"y2":1500},
"inputHash":"<sha256 prev>",
"outputHash":"<sha256 cropped>",
"software":"Adobe Lightroom 13.0.1"
},
"creator":"photographer-jane-doe","signature":"<sig>"
}'
# Reuters staff editor color-grades
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"photo-canon-capture-a1b2c3",
"subjectType":"TITLE",
"eventType":"media.color-graded",
"payload":{
"editor":"editor-reuters-staff-mark",
"gradeParams":{"exposure":-0.3,"shadows":0.5},
"inputHash":"<sha256>","outputHash":"<sha256>"
},
"creator":"editor-reuters-staff-mark","signature":"<sig>"
}'
# Publishing
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"photo-canon-capture-a1b2c3",
"subjectType":"TITLE",
"eventType":"media.published",
"payload":{
"publisher":"reuters",
"storyID":"story-12345",
"publishedAt":1713500000
},
"creator":"reuters","signature":"<sig>"
}'

4. Consumer verification

A news aggregator’s trust-aware verifier:

type ContentVerifier struct {
client QuidnugClient
selfQuid string
domain string
}
func (v *ContentVerifier) Verify(ctx context.Context, assetID string) (*VerificationResult, error) {
title, err := v.client.GetTitle(ctx, assetID)
if err != nil { return nil, err }
events, err := v.client.GetSubjectEvents(ctx, assetID, "TITLE")
if err != nil { return nil, err }
result := &VerificationResult{AssetID: assetID, Chain: []ChainStep{}}
for _, ev := range events {
step := ChainStep{
Timestamp: ev.Timestamp,
EventType: ev.EventType,
Actor: ev.Creator,
}
trust, err := v.client.GetTrust(ctx, v.selfQuid, ev.Creator, v.domain, nil)
if err != nil {
step.TrustLevel = 0
} else {
step.TrustLevel = trust.TrustLevel
}
// Verify signature (already done server-side on event storage;
// re-verify here if skeptical)
step.SignatureValid = v.VerifySignature(ev)
result.Chain = append(result.Chain, step)
}
// Overall trust = min over the chain (chain is only as strong as weakest link)
result.OverallTrust = 1.0
for _, s := range result.Chain {
if s.TrustLevel < result.OverallTrust {
result.OverallTrust = s.TrustLevel
}
}
return result, nil
}

5. AI-generated content

When the asset is generated by a model:

Terminal window
# Asset title
curl -X POST $NODE/api/v1/titles -d '{
"assetId":"synthetic-image-xyz",
"domain":"media.provenance.social",
"titleType":"media-asset",
"owners":[{"ownerId":"acme-user-alice","percentage":100.0}],
"attributes":{
"assetType":"photo",
"origin":"generated"
}
}'
# Generation event
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"synthetic-image-xyz",
"subjectType":"TITLE",
"eventType":"media.generated",
"payload":{
"generator":"acme-image-model-v2",
"prompt":"Sunset over mountains",
"modelHash":"<sha256>",
"seed":42,
"outputHash":"<sha256>"
},
"creator":"acme-image-model-v2","signature":"<sig>"
}'

6. Counter-claims (deepfake detection)

A third-party detection service believes an asset is AI-generated despite claiming capture:

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"photo-suspect-deepfake",
"subjectType":"TITLE",
"eventType":"media.authenticity.disputed",
"payload":{
"disputer":"deepfake-detector-org-x",
"evidence":"<hash of analysis report>",
"confidence":0.92,
"claim":"Likely AI-generated; mismatched lighting vectors"
},
"creator":"deepfake-detector-org-x","signature":"<sig>"
}'

7. Revocation

Canon discovers a batch of compromised cameras:

Terminal window
# Canon invalidates trust in the specific device
curl -X POST $NODE/api/trust -d '{
"truster":"canon-corp",
"trustee":"canon-5d-mark-iv-serial-123",
"trustLevel":0.0,
"domain":"media.provenance.news.capture",
"nonce":<next>,
"description":"Device firmware compromise; do not trust captures"
}'

Consumer verifiers checking trust now see trust=0 for this device → assets claiming capture from it have zero capture trust → overall trust tanks.

8. Testing

func TestMedia_CaptureAndChain(t *testing.T) {
// Create title + capture event + 2 edits + publish
// Verifier sees 5-step chain
// Overall trust = min(trust in each actor)
}
func TestMedia_CompromisedCameraPropagates(t *testing.T) {
// Canon revokes trust in device
// Consumer verifier sees capture trust = 0
// Overall trust = 0 (chain broken at origin)
}
func TestMedia_DisputeEvent(t *testing.T) {
// Fake authenticity disputed event
// Verifier flags overall
}

Where to go next

Threat model

Adversaries, assumed capabilities, mitigations.

Threat Model: AI Content Authenticity

Assets

  1. Attribution integrity, the chain of who captured, edited, and published an asset.
  2. Consumer decision quality, ability to correctly identify authentic vs. manipulated vs. generated media.
  3. Trust roots, camera manufacturers, editing software, publishers.

Attackers

AttackerCapabilityGoal
Deepfake producerCan generate synthetic mediaPass off as captured
Propaganda operatorResources to forge chainsUndermine public trust
Compromised editor keyHas valid editor signing keyForge edit events
Camera firmware exploitHack device to sign arbitrary capturesCreate fake “real” photos
Publisher compromiseValid publisher signing keyPublish manipulated content

Threats

T1. Deepfake passed as real photo

Attack. Attacker generates an image and submits a fake media.captured event claiming a real camera captured it. Mitigation. Camera’s signing key is hardware-bound; an attacker doesn’t have the camera’s private key. A capture event signed by a quid that isn’t actually a registered camera fails consumer’s trust evaluation. Residual risk. If the attacker compromises an actual camera’s key (T4), they can generate signed captures. See T4.

T2. Edit-chain forgery

Attack. Attacker creates a chain of media.edited events impersonating legitimate editors. Mitigation. Each event signed by editor’s quid; attacker lacks those keys. Relational-trust evaluation by consumer checks each editor’s quid against their trust graph.

T3. Compromised editor’s key

Attack. A legitimate editor’s key is stolen. Attacker forges edit events. Mitigation. Guardian recovery rotates to new key. Post-rotation, old signatures invalid. GuardianResignation (QDP-0006) lets someone who left remove themselves.

T4. Camera firmware exploit

Attack. Firmware bug/exploit lets attacker use a real camera’s signing key to sign arbitrary images. Mitigation. Camera manufacturer can revoke the specific device’s trust (set trust=0). Push gossip propagates the revocation. Consumers then evaluate the device’s trust as 0 → captures from it are not trusted. Residual risk. Window between exploit discovery and revocation.

T5. Publisher collusion with fake content

Attack. Publisher knowingly publishes manipulated content, uses their own signing authority. Mitigation. Publisher’s reputation visible to all. Independent fact-checkers can emit media.authenticity.disputed. Consumers weigh the publisher’s trust vs. the fact-checker’s in reaching conclusion.

T6. AI-generated content passed as authentic

Attack. An AI tool is used to generate content; attacker signs it with a trusted editor’s key and claims media.edited but not media.generated. Mitigation. Without a preceding media.captured event from a camera’s key, the chain has no authentic origin. A verifier looking for the capture event at the beginning of the chain rejects the asset. Residual risk. If the forger also forges a media.captured event (requires camera key compromise T4), the defense collapses to T4’s mitigation.

T7. Event reordering / replay

Attack. Attacker replays a captured edit event on a different asset. Mitigation. Events bound to subject (asset) ID + anchor-nonce monotonicity. Replay to different asset has wrong subject; replay on same asset has used-up nonce.

T8. Cross-domain trust confusion

Attack. Asset with capture from media.provenance.evidentiary is displayed as if it’s from media.provenance.news where different trust rules apply. Mitigation. Domain scoping is explicit in each title. Consumers should filter by expected domain.

T9. Synthetic content denial (reverse of T1)

Attack. AI-generated content publisher signs it as authentic; later claims it was labeled. Creates deniability. Mitigation. Event stream’s append-only history, media.generated either exists or doesn’t. Either the publisher signed an attestation at generation time, or they didn’t. No “retroactively added” event.

Not defended against

  1. Sufficiently skilled forgery without key compromise, no system can detect content that was cryptographically generated with legitimate keys and a misleading narrative (a real camera photographing a deceptive scene).
  2. Context manipulation, valid photo, false caption. Fact-checkers provide this counter-weight at the application layer.
  3. Trust system Sybils, attacker spins up many “fake” fact-checker quids and endorses their own content. Mitigation: consumer’s trust graph reflects only third-parties they independently trust.

References