Skip to main content

c2pie is a Python library that provides C2PA standard functionality.

Project description


CI c2pa coverage latest


c2pie is an open‑source Python library for constructing C2PA Content Credentials manifests that validate with c2patool and other common C2PA consumers.

The package supports building claims, assertions, and COSE signatures and embedding the manifest store into JPG/JPEG and PDF files.

Latest version: 0.1.0a5

🔸 Supported file extensions: JPG, JPEG, PDF

🔸 Supported Python versions: 3.9.2 - 3.14.0

🔸 C2PA Spec Version: 1.4

For more detailed feature specification, please look at the Features section.

[!WARNING] This library helps you build valid manifests, but trust decisions (anchors, allow/deny lists, TSA) are your responsibility. For production, you must provide a certificate chain anchored to an accepted trust root and configure validation policy accordingly.

For more information on generating certificates and keys for file signing proceed to the Certificates section.

Table of Contents


🥧 Quick start

Running example apps with Docker Compose

For a quick test of c2pie's functionality with pre-prepared environment, test files and credentials, you can run our example apps.

[!IMPORTANT] Docker is essential for running example apps.

Follow the steps:

  1. Clone the c2pie repository.

  2. Go to example_app directory:

    cd example_app
    
  3. To test signing a JPG file, run:

    docker compose up c2pie-test-signing-jpg
    

    To test signing a PDF file, run:

    docker compose up c2pie-test-signing-pdf
    

    After running either of these commands, you'll see a resulting signed file appear in example_app/test_files directory with a signed- prefix and a corresponding message with c2patool validation results in your terminal like this:

    Successfully signed the file test_files/test_image.jpg!
    The result was saved to test_files/signed_test_image.jpg. 
    c2patool_validation_results:
    {
        "active_manifest": "urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9",
        "manifests": {
        "urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9": {
            "claim_generator": "c2pie",
        ................
    },
    "validation_results": {
        "activeManifest": {
        "success": [
            {
                "code": "claimSignature.insideValidity",
                "url": "self#jumbf=/c2pa/urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9/c2pa.signature",
                "explanation": "claim signature valid"
            },
        ................
        },
        "validation_state": "Valid" 
    }
    

You can also set up a Jupyter Lab environment and test c2pie there by running:

docker compose up c2pie-notebooks

After running this command you should be able to access Jupyter Lab at localhost:8888 from your browser.

The existing notebooks directory already contains an example notebook with commands to test signing functionality.


Running from your own environment

Prerequisites

  1. Python environment. Currently supported Python versions: 3.9.2 - 3.14.0.

  2. Private key and certificate chain pair. You can go to Certificates for instructions on how to generate one.

    The repo contains pre-generated mock credentials in tests/credentials. You can use them for a quick start.

  3. Key and certificate filepaths exported into the current environment with:

    export C2PIE_KEY_FILEPATH=<path/to/private_key_file>
    export C2PIE_CERT_FILEPATH=<path/to/certificate_chain_file>
    
  4. c2pie package installed in your current environment:

    pip install c2pie
    

Usage

Command Line Interface

You can run the following command to sign an input JPG or PDF file:

c2pie sign --input_file <path/to/input_file>

By default, signed file will be saved to the same directory as the input file with the signed_ prefix. To explicitly set output path, use:

c2pie sign --input_file <path/to/input_file> --output_file <path/to/output_file>

If the file has been successfully signed, you'll see a message like this:

Successfully signed the file tests/test_files/test_doc.pdf!
The result was saved to tests/test_files/signed_test_doc.pdf.

Code

To sign a file and save the output to the same directory:

from c2pie.signing import sign_file

input_file_path = "path/to/file"
sign_file(input_path=input_file_path)

To set a custom output path:

from c2pie.signing import sign_file

input_file_path = "path/to/file"
output_file_path = "path/to/another/file/"
sign_file(input_path=input_file_path, output_path=output_file_path)

If the file has been successfully signed, you'll see a message like this:

Successfully signed the file tests/test_files/test_doc.pdf!
The result was saved to tests/test_files/signed_test_doc.pdf.

Validation

c2patool

Output files can be validated with:

c2patool path/to/your_output.jpg
c2patool path/to/your_output.pdf

If the file has been correctly signed and validation is successful, the results you'll see in the terminal will look similar to this:

c2patool_validation_results:
{
    "active_manifest": "urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9",
    "manifests": {
    "urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9": {
        "claim_generator": "c2pie",
    ................
},
"validation_results": {
    "activeManifest": {
    "success": [
        {
            "code": "claimSignature.insideValidity",
            "url": "self#jumbf=/c2pa/urn:uuid:f0ce8560b76342d1bb3085cfbe6cc5e9/c2pa.signature",
            "explanation": "claim signature valid"
        },
    ................
    },
    "validation_state": "Valid" 
}

C2PA Verify Tool

You can also verify signed files using Verify tool.

Simply upload the file you'd like to verify.

[!IMPORTANT] Files embedded with self-signed certificates (like the ones this repository contains) won't be verified.

You'll get the following message:

The Content Credential issuer couldn’t be recognized. This file may not come from where it claims to.

Please proceed to production credentials section to find out about generating verifiable credentials.


🥧 For developers

First steps

To contribute to the c2pie package development, you can use one of the following approaches after cloning the repository.

Using Dev Containers

  1. Make sure you have installed Docker and Dev Containers extension for VS code.

  2. Open the repo in VS Code and Reopen in Container. The container installs Python, Poetry, the package in editable mode, and configures Ruff as a default formatter, which provides linting and formatting and enables auto-fixing files on save (see .devcontainer/devcontainer.json).

Using a Local Environment

[!NOTE] We strongly recommend using Dev Containers in order to automatically create an isolated Python environment with all dependencies installed, environment variables exported and some helpful development tools included.

  1. Make sure the environment you're currently in has Python and Poetry installed and their versions meet the requirements of the project. You can verify that by running:

    python --version
    poetry --version
    
  2. Go to the repository's folder in terminal and run:

    poetry install
    

    This will automatically create and activate a poetry shell with project's dependencies installed.

  3. To run any Python command related to the project's dependencies, remember to add poetry run in front of the command. For example:

    poetry run c2pie sign --input_file tests/test_files/test_doc.pdf
    
    poetry run ruff check
    

[!WARNING] Commands in further sections don't include poetry run by default as they are intended to be run from a Dev Container. Remember to add poetry run.

Run test applications

To run test applications, you need to fill out TEST_PDF_PATH and/or TEST_IMAGE_PATH in values in .env. Test scripts use these filepaths as input files for signing.

Also make sure that you have test certificate chain and public key in tests/credentials. They should be there by default if you've cloned the repository. If needed, you can change their filepaths in .env as well.

You can test the signing workflow with the following VS Code tasks:

🔸 Run JPG test application

🔸 Run PDF test application

Run tests

Run from terminal:

pytest

Or use the VC Code task Run unit tests. Note that the task excludes the e2e test.

Or if you'd like to get info on test coverage, use:

pytest --cov

Lint & format

You can check if there are any issues to deal with them manually:

ruff format --check .
ruff check .

Or check and automatically fix where possible:

ruff format .
ruff check . --fix

The latter option is also available via the VC Code task Lint and Format


🥧 Features

🔸 C2PA Claim (c2pa.claim) with canonical CBOR, dc:format, alg, and hashed‑URIs for assertions.

🔸 C2PA Signature (c2pa.signature) using COSE_Sign1 (PS256) with detached payload and x5chain in protected header.

🔸 Assertion Store with common assertions (e.g., c2pa.hash.data hard‑binding, schema.org CreativeWork, etc.).

🔸 Embedding

  • JPG via APP11 segments (size‑driven iterative layout).
  • PDF via incremental update at EOF (xref/trailer preserved; /AF + /Names/EmbeddedFiles).

🔸 Validation with c2patool (structure + signatures).

Workflow of test applications

  1. Load a sample asset (tests/test_files/..);

  2. Build a manifest with c2pie_GenerateAssertion, c2pie_GenerateHashDataAssertion, c2pie_GenerateManifest;

  3. Embed the manifest (c2pie_EmplaceManifest);

  4. Write a new asset with C2PA.

Notes for PDF vs JPG/JPEG

🔸 PDF: we append an incremental update. The c2pa.hash.data exclusion starts at len(original_pdf) and its length equals the final tail size (computed iteratively).

🔸 JPG/JPEG: we insert APP11 segments. The exclusion start is the APP11 insertion offset; the length is the final APP11 payload length (also computed iteratively).

The library takes care of iterative sizing, so the c2pa.hash.data matches exactly, otherwise validators return assertion.dataHash.mismatch.


🥧 Certificates

Example certificate and key are located in tests/credentials.

[!WARNING] This repository's credentials are suitable for development only!

Generating your own mock credentials

You can generate your own mock credentials for testing and developing the package follow these steps:

  1. Generate a private key:

    openssl genrsa -out credentials/<private-key-filename>.pem 2048
    
  2. Generate a Certificate Signing Request (CSR):

    openssl req -new \
    -key credentials/<private-key-filename>.pem \
    -out csr.pem
    
  3. Generate a Self-Signed Certificate:

    openssl x509 -req -days 365 \
    -in csr.pem \
    -signkey  credentials/<private-key-filename>.pem \
    -out credentials/<certificate-filename>.pem
    

[!IMPORTANT] Remember to update environment variables to use your newly generated credentials.

[!NOTE] You can change certificate's validity period with --days option at the last step.

Certificate Signing Request file (csr.pem) can be deleted after the certificate has been generated.

Getting credentials for production

🔸 Use a real document‑signing certificate (RSA‑PSS or ECDSA per C2PA);

🔸 Provide a leaf + intermediates bundle (no root);

🔸 Configure trust anchors/allow‑lists in your validator environment.

For detailed information on signing and certificates please explore the corresponding section in the Content Authenticity Initiative (CAI) documentation.


🥧 Relevant links

CAI documentation

C2PA spec

c2patool for validation

C2PA Verify Tool


🥧 Contributing

🔸 Use Conventional Commits (e.g., feat:, fix:, style(ruff):, ci:).

🔸 Run Lint and Format task before committing.

🔸 Add unit tests for new behavior.


🥧 License

Apache License. See c2pie repository's license for more information.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

c2pie-0.1.0a6.tar.gz (29.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

c2pie-0.1.0a6-py3-none-any.whl (31.1 kB view details)

Uploaded Python 3

File details

Details for the file c2pie-0.1.0a6.tar.gz.

File metadata

  • Download URL: c2pie-0.1.0a6.tar.gz
  • Upload date:
  • Size: 29.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for c2pie-0.1.0a6.tar.gz
Algorithm Hash digest
SHA256 53a561f50c543e09fcce8bdc8b767bacdb886671e187a3be301bad4bdc035019
MD5 cc759488970d47c8f3f63837eb8ef68e
BLAKE2b-256 07939cc07fe66acebedb89c9763c9e8ffd219bd6279265e7092e709c196e6543

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pie-0.1.0a6.tar.gz:

Publisher: publish-package.yml on TourmalineCore/c2pie

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pie-0.1.0a6-py3-none-any.whl.

File metadata

  • Download URL: c2pie-0.1.0a6-py3-none-any.whl
  • Upload date:
  • Size: 31.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for c2pie-0.1.0a6-py3-none-any.whl
Algorithm Hash digest
SHA256 a53f039a4b35ecec851ced75bacf865e9f60de20ff1a87200475e0310248a53d
MD5 793cd4cb97223036598d52aa52ecb19f
BLAKE2b-256 bf35ae5b67b6b7e3f74e7d9637966294021c29ddaef444fd295d3c20b2d7a8ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pie-0.1.0a6-py3-none-any.whl:

Publisher: publish-package.yml on TourmalineCore/c2pie

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page