This Python script is a small wrapper around the sign_file function from the c2pa-python library that helps generate the manifest JSON necessary before signing and attaching the manifest to a media file.
- Coalition for Content Provenance and Authenticity (C2PA)
- Content Authenticity Initiative (CAI)
- Open-source tools for content authenticity and provenance
- Working with manifests
- Manifest store reference
- Signing manifests
This script extracts metadata from a media file (e.g., EXIF, IPTC, and XMP) and generates the manifest JSON with C2PA's assertions and actions. It uses this generated manifest JSON to sign and attach the manifest to a media file, optionally using your own certificate.
Once the media file is signed, end users can use something like the C2PA's online Verify tool to upload the media file and view its signed content credentials (i.e., the manifests.) There's also a command line tool called c2patool available.
Important
This script was designed to be modified to fit your individual needs. It includes support for the metadata below and assumes certain things — like your media file never being signed before (see Considerations).
For example, you might need to support custom IPTC fields not handled below, change the order in how you search for the "author" field, or want to remove the GPS data assertion from being included for privacy reasons.
Out of the box, this script handles a lot of the actions, assertions, and organizational information (i.e., website and social networks) that are displayed on the C2PA's online Verify tool when uploading and validating content credentials found inside a media file.
If any of the following metadata is found in the media file, it's included in the generated manifest:
Metadata | Description |
---|---|
Title | Extracted from XMP:Title (or defaults to filename) |
Author | First occurance of EXIF:Artist , XMP:Creator , or XMP:Credit (in that order) |
Camera make and model | Extracted from EXIF:Make and EXIF:Model |
GPS information | Extracted from EXIF:GPS* fields |
Original date and time | Extracted from EXIF:DateTimeOriginal |
In addition to the above, the following is also included in the manifest:
- Assertions for your organization's website, Instagram page, and LinkedIn page (shown in the Verify tool)
- These values are edited in your
.env
file (see Configuration)
- These values are edited in your
- Do not train assertions, to specify the media file shouldn't be used for data mining, machine learning (ML) training, or inference purposes
Note
Depending on your OS and environment, you may need to modify some of these commands.
git clone https://github.com/rob/c2pa-helper.git && \
cd c2pa-helper && \
python3 -m venv .venv && \
source .venv/bin/activate && \
pip install -r requirements.txt
Copy .env.sample
to .env
and modify the variable values.
Note
All of the variables found in .env
are optional — remove any that you don't need.
Variable | Description |
---|---|
CLAIM_GENERATOR |
Specifies the generator of the claim (e.g., "org-name/0.1.0") (docs) |
CERT_TYPE |
The type of certificate, e.g., "ps256" (docs) |
CERT |
Path to the certificate file (docs) |
CERT_PRIVATE_KEY |
Path to the certificate's private key file (docs) |
CERT_TIMESTAMP_URL |
URL for the certificate timestamping service (docs) (You shouldn't need to modify this in most cases.) |
ORGANIZATION_URL |
The URL of the organization's homepage (docs) |
LINKEDIN_NAME |
The name of the organization on LinkedIn (docs) |
LINKEDIN_URL |
URL to the organization's LinkedIn page (docs) |
INSTAGRAM_NAME |
The organization's Instagram username (docs) |
INSTAGRAM_URL |
URL to the organization's Instagram page (docs) |
Important
If you don't have your own certificate, remove all of the CERT
variables and sign_file
will use the built-in certificate found in certs/sample
. This certificate is pulled directly from the C2PA repository and will show as "C2PA Test Signing Cert" in the C2PA's online Verify tool.
Assertions are functions stored in lib/assertions.py
that return a Dict
if they're valid, or None
if the assertion isn't valid (e.g., you're looking for the EXIF:DateTimeOriginal
field but it isn't found in the metadata.)
You can modify these assertion functions or add new ones.
These assertion functions are called from lib/manifest.py
in the potential_assertions
List
in the order you specify. They're only included in the final manifest JSON if they return a Dict
(i.e., not None
.)
Assuming your .env
is set up and the potential_assertions
List
in lib/manifest.py
contains all of the assertions you want to include, you can run the script via cli.py
(for a single file) or batch.py
(to batch process multiple files inside a directory.)
python cli.py /path/to/original_file /path/to/signed_file
[...]
Right now, this script assumes you are generating a manifest and signing a media file for the first time, so everything signed gets the c2pa.published action included in its manifest.
This might not make sense if you're trying to re-sign a media file after making changes to it and have already signed it previously. You would want to use a different action, most likely.
For example, you might want to use c2pa.resized
, c2pa.filtered
, or c2pa.color_adjustments
instead.