Skip to content

Release 4.5.6

Choose a tag to compare

@dylanuys dylanuys released this 21 Mar 20:01
· 68 commits to main since this release
298a9e5

C2PA trust anchor validation for c2pa-python >= 0.29.0

Background

c2pa-python==0.29.0 (released March 17 2026) changed c2pa-rs so that trust anchor checking is always enforced by default. Any certificate not chaining to a root CA in c2pa-rs's built-in store fires signingCredential.untrusted and hard-fails validation.

We had pinned c2pa-python>=0.25.0,<0.29.0 as a short-term workaround. This restores previous behavior but leaves a cert-forgery gap: our code only string-matched on the issuer field, which an attacker could fake with a self-signed cert set to "Stability AI Ltd".

What this PR does

1. Extracts and saves trust anchor PEM files

Scanned miner-submitted media in /workspace/.cache/sn34 (88k entries, source_type = 'miner') to find one sample file per AI provider. Extracted the full DER certificate chains embedded in each file's C2PA JUMBF box and saved them to gas/verification/trust_anchors/.

All major providers turned out to use private or non-CAI root CAs that are not in c2pa-rs's built-in store:

Provider Cert chain root File
Stability AI GlobalSign Root CA - R6 (via GlobalSign GCC R6 SMIME CA 2023) stability_ai.pem
Runway (pre-Gen4) GlobalSign Root CA - R6 (same intermediate) runway.pem
Runway Gen4 CN=Stability AI, O=Stability AI, C=US (private self-signed) runway_gen4.pem
Black Forest Labs GlobalSign Root CA - R6 (same intermediate) black_forest_labs.pem
Microsoft CN=Microsoft Supply Chain RSA Root CA 2022 (private self-signed) microsoft.pem
OpenAI (via Truepic) CN=Truepic WebClaimSigningCA chain openai_truepic.pem
Adobe CN=Adobe Product Services G4 chain adobe.pem
Google CN=Google C2PA Root CA G3 (fetched from pki.goog/c2pa/root-g3.crt) google_c2pa.pem

GlobalSign Root CA - R6 was sourced from the certifi bundle. Google's C2PA Root CA G3 was fetched directly from Google's PKI at http://pki.goog/c2pa/root-g3.crt.

2. Updates c2pa_verification.py

Three additions at the top of the file, zero changes to existing verification logic:

  • _load_trust_anchors() — concatenates all PEM files at import time into a single string
  • _TRUST_ANCHORS_PEM / _C2PA_HAS_CONTEXT_API — cached module-level constants (no per-call file I/O)
  • _open_c2pa_reader(file_path) — transparent wrapper around c2pa.Reader:
    • On >= 0.29.0: builds a Settings object with user_anchors (additive — preserves the built-in store) and wraps the reader in a Context
    • On < 0.29.0: returns a bare c2pa.Reader, identical to previous behaviour

The one-line change in verify_c2pa():

# before
with c2pa.Reader(file_path) as reader:

# after
with _open_c2pa_reader(file_path) as reader:

3. Bumps the version pin

# pyproject.toml
- "c2pa-python>=0.25.0",
+ "c2pa-python>=0.29.0",

Verification

Ran gascli g verify-c2pa against one sample file per provider on c2pa-python 0.29.0:

Adobe         (Adobe Inc.)
Black Forest Labs  (Black Forest Labs Inc.)
Runway Gen4   (Runway — private Stability AI root)
Google        (Google LLC — Google C2PA Root CA G3)
Google        (Google C2PA Core Generator Library)
Microsoft     (Microsoft Corporation — video)
Microsoft     (Microsoft Corporation — image)
OpenAI        (Sora / Truepic chain)
OpenAI        (OpenAI-API)
Runway        (RUNWAY AI, INC. — pre-Gen4)

One Stability AI sample showed assertion.dataHash.mismatch — the cached file was modified after signing, which is correct behaviour (hash mismatch ≠ trust failure).

Security improvement

With c2pa-python>=0.25.0,<0.29.0, c2pa-rs skipped the trust chain check entirely. Our code fell back to string-matching on issuer, meaning an attacker could submit media signed with their own self-signed cert and set issuer = "Stability AI Ltd" to pass validation.

With this PR, c2pa-rs performs full cryptographic chain verification against the extracted root certs. Forging a cert that chains to CN=Stability AI, O=Stability AI (Runway Gen4's root) or GlobalSign Root CA - R6 requires breaking RSA-2048/PSS — the issuer string alone is no longer sufficient.