Skip to content
c2pa

go · no cgo · content credentials

C2PA provenance, pure Go.

Read, verify and write C2PA / Content Credentials manifests in JPEG, PNG, MP4/HEIC, WebP, TIFF, GIF, MP3, SVG and PDF — a fast claim reader for triage, a full cryptographic verifier, and a signer checked against c2pa-rs — with no c2pa-rs dependency and no cgo.

MIT licensed · pure-Go crypto/CBOR/COSE · Go 1.26+

read.go
info := c2pa.Read(ctx, c2pa.JPEG, f) // or c2pa.PNG, c2pa.BMFF, c2pa.PDF …
if info.Present {
    fmt.Println(info.ClaimGenerator) // "Adobe Firefly"
    fmt.Println(info.AIGenerated)    // declared AI-generated?
    fmt.Println(info.SignedBy)       // claimed signer (unverified)
}

what you get

Three modes, one pure-Go library.

A fast reader that surfaces what a file claims, a full verifier that proves it, and a signer that writes it — the complete C2PA validation algorithm and a writer checked against c2pa-rs, no c2pa-rs dependency and no cgo.

Pure Go, no cgo

No c2pa-rs dependency and no cgo — pure-Go crypto, CBOR, and COSE all the way down, so it cross-compiles and vendors like any other Go package.

Read for triage

Fast, best-effort, never-errors surfacing of what a file claims: creating tool, title, format, AI-generated flag, claimed signer, and signing time.

Validate in full

The complete C2PA validation algorithm, executed in pure Go, reporting per-step C2PA status codes — success, informational, and failure — for every check.

Sign in pure Go

Write c2pa.claim.v2 manifests into all nine containers with your own key — COSE_Sign1, optional RFC 3161 timestamps, provenance chained on re-sign — every output validated before it is written, and checked against c2pa-rs's c2patool in CI.

Signatures & chains

Verifies COSE signatures (ES256/384/512, PS256/384/512, EdDSA), the certificate chain against the C2PA profile, and assertion plus hard-binding hashes.

Timestamps & revocation

RFC 3161 timestamp verification, opt-in OCSP/CRL revocation, and recursive ingredient validation with cycle detection for nested manifests.

Batteries included

Official C2PA conformance trust lists embedded in the binary, overridable anchors, and tunable options — across JPEG, PNG, BMFF, RIFF, TIFF, GIF, MP3, SVG and PDF.

usage

Triage fast, or verify in full.

Read tells you what a file claims — accurate-as-recorded, not proven. Validate runs the whole algorithm and reports C2PA status codes you can trust.

// Read — fast, unverified triage
info := c2pa.Read(ctx, c2pa.JPEG, f)
if info.Present {
    fmt.Println(info.ClaimGenerator) // tool
    fmt.Println(info.AIGenerated)    // AI?
    fmt.Println(info.SignedBy)       // claimed
}
// Validate — full cryptographic proof
res := c2pa.Validate(ctx, c2pa.JPEG, f, opts...)
if res.Valid {
    fmt.Println("verified, signed at", res.SignedAt)
}
for _, s := range res.Statuses {
    fmt.Println(s.Code, s.Severity) // C2PA codes
}
  • claimSignature.validated
  • signingCredential.trusted
  • assertion.dataHash.match
  • timeStamp.validated
  • signingCredential.untrusted
  • assertion.dataHash.mismatch

sign

Write Content Credentials.

A crypto.Signer and its certificate chain in, a signed asset out — the same nine containers, the COSE algorithm inferred from the key, an RFC 3161 timestamp when you ask for one. An asset that already carries a manifest keeps it as the new manifest's parentOf ingredient. Nothing is written unless the output already validates.

signer, err := c2pa.NewSigner(key, chain,
    c2pa.WithClaimGenerator("my-app", "1.2.0"),
    c2pa.WithTimestampAuthority("https://timestamp.digicert.com")) // optional

err = signer.Sign(ctx, c2pa.JPEG, in, out, c2pa.Manifest{
    Title: "photo.jpg",
    Actions: []c2pa.Action{{
        Action:            c2pa.ActionCreated,
        DigitalSourceType: c2pa.DigitalSourceTypeDigitalCapture,
    }},
})
  • c2patool: Valid
  • trust --trust_anchors: Trusted
  • timeStamp.trusted
  • ingredient parentOf

install

Add it to your module.

Pure-Go dependencies only, no cgo. Requires Go 1.26+.

go get go get github.com/richardwooding/c2pa
import import "github.com/richardwooding/c2pa"

Requires Go 1.26+. Full API docs on pkg.go.dev.