Documentation
¶
Overview ¶
Package c2pa is a pure-Go, read-only reader for C2PA / Content Credentials (https://c2pa.org) provenance manifests embedded in media files.
It surfaces what a file CLAIMS about its provenance — the creating tool, title, declared media type, whether it declares AI-generated content, and the signer identity + signing time — by parsing the embedded JUMBF manifest (ISO 19566-5), CBOR-decoding the active manifest's claim and c2pa.actions assertion, and decoding the COSE_Sign1 signature envelope.
This is UNVERIFIED ¶
The reader is deliberately read-only: it does NOT validate the COSE cryptographic signature, and it does NOT check the signer's certificate chain against the C2PA trust list. Full validation requires the Rust c2pa-rs library via CGO, which this pure-Go package intentionally avoids.
Treat every field like EXIF or an email From header: accurate-as-recorded, not authenticated. SignedBy is who the file CLAIMS signed it, not a verified identity. A file with no manifest yields Info{Present:false}; absence of a signal (e.g. AIGenerated) does not prove its negation.
All parsing is best-effort and never panics: malformed or truncated input yields zero values rather than an error. Every input-scaled loop honours the supplied context.Context, so a cancelled call surrenders promptly.
Example ¶
Example reads the Content Credentials a JPEG claims, and surfaces the (unverified) creating tool, AI-generated flag, and signer identity.
package main
import (
"context"
"fmt"
"os"
"github.com/richardwooding/c2pa"
)
func main() {
f, err := os.Open("testdata/c2pa_signed.jpg")
if err != nil {
panic(err)
}
defer func() { _ = f.Close() }()
info := c2pa.Read(context.Background(), c2pa.JPEG, f)
if !info.Present {
fmt.Println("no Content Credentials")
return
}
fmt.Println("title:", info.Title)
fmt.Println("ai-generated:", info.AIGenerated)
// SignedBy is the CLAIMED signer — not cryptographically verified.
fmt.Println("signed by:", info.SignedBy)
}
Output: title: CA.jpg ai-generated: false signed by: C2PA Signer
Index ¶
Examples ¶
Constants ¶
const MaxScan = 16 << 20
MaxScan caps how many leading bytes Read consumes looking for a manifest. C2PA manifests sit in the file header (before image data) and rarely exceed a few MB even with embedded thumbnails; past the cap Read gives up.
Variables ¶
This section is empty.
Functions ¶
func WalkBoxes ¶
WalkBoxes recursively walks a JUMBF box tree, invoking fn(label, tbox, content) for every leaf box. label is the nearest enclosing superbox's jumd label, tbox is the 4-character box type, and content is the box payload. Nesting is capped at an internal depth limit so adversarial input cannot exhaust the stack; ctx is honoured at the top of every iteration.
This is a lower-level primitive — most callers want Read. It is exported for advanced use (e.g. surfacing assertions Read does not model).
Types ¶
type Container ¶
type Container string
Container identifies the carrier file format whose C2PA manifest to read.
type Info ¶
type Info struct {
// Present is true when a C2PA manifest was found and parsed.
Present bool
// ClaimGenerator is the tool that created/edited the asset (e.g.
// "Adobe Firefly", "make_test_images/0.33.1 c2pa-rs/0.33.1").
ClaimGenerator string
// Title is the claim's dc:title.
Title string
// Format is the claim's dc:format (declared media type).
Format string
// AIGenerated is true when a c2pa.actions assertion declares a
// digitalSourceType of trainedAlgorithmicMedia or
// compositeWithTrainedAlgorithmicMedia.
AIGenerated bool
// SignedBy is the COSE_Sign1 signer's leaf x509 certificate common name
// (Subject CN, falling back to the first Organization). UNVERIFIED — the
// certificate chain is not validated against the C2PA trust list.
SignedBy string
// SignedAt is the signing time from the RFC 3161 timestamp embedded in the
// signature (sigTst). Zero when absent. UNVERIFIED.
SignedAt time.Time
}
Info is the surfaced, CLAIMED, UNVERIFIED subset of a C2PA manifest. See the package doc: these are the file's assertions, not authenticated facts.
func Read ¶
Read reads up to MaxScan bytes from r and, for the given container, locates and parses the embedded JUMBF manifest. It returns a zero Info (Present=false) when there's no manifest. It never returns an error — provenance is best-effort metadata, surfaced like EXIF.
ctx is honoured at entry and inside the input-scaled scan loops, so a cancelled call surrenders promptly mid-scan rather than parsing a full adversarial header.