Add C2PA Content Credentials support for JPEG files - #3
Merged
Conversation
Adds a working C2PA 2.2 claim generator and validator to the editor, running entirely in the tab. Opening a JPEG checks any credential it carries; exporting a JPEG can attach a new signed manifest recording every operation the pipeline performed, with the previous credential carried forward as a parentOf ingredient. Written against the specification rather than linking c2pa-rs, which carries a trust-list and OCSP stack this has no use for and whose wasm path does not go through wasm-bindgen. The layers are small enough to read: deterministic CBOR (RFC 8949 4.2.1), JUMBF boxes (ISO 19566-5), APP11 embedding, a minimal DER reader, and COSE_Sign1 over the claim. The hard binding's circular dependency - the manifest hashes a file it lives inside - is broken the way section 10.4 prescribes, with fixed-width placeholders and two renders. Every placeholder is exactly as wide as the value replacing it, and the two lengths are asserted equal rather than assumed. wasm32-unknown-unknown has neither a clock nor an RNG, so the host supplies timestamps and UUIDs and p256's std feature is off to keep getrandom 0.2 out of the tree. Signing is deterministic per RFC 6979, which also makes the tests reproducible. Scope is JPEG in, JPEG out: the hard binding commits to a byte range, so embedding and exclusions are per-format. Other formats edit and export exactly as before, and the UI says so rather than implying a file was checked. On trust: the signing key is compiled into a module served to browsers, so it is public and cannot be otherwise. That still proves the pixels are unaltered; it cannot prove who signed. The UI reports the two claims separately, never shows a tick beside a signer, and names the specification status code for every check. GitHub secrets are wired up for the one thing they buy - keeping a key out of git history - and signing/README.md is explicit that they cannot make a browser key secret, with two designs that would. Verified against c2patool, which reports validation_state Valid with signingCredential.untrusted as the only failure, and reports assertion.dataHash.mismatch on a tampered file exactly as this validator does. 76 new tests cover the layers and the round trip. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzDZWjq5uHHgQdp84gXYnw
Clippy 1.98 adds chunks_exact_to_as_chunks, which CI runs as an error under -D warnings. The path also collected into a Vec only to iterate it again, so this drops that too; as_chunks yields whole pairs directly and still discards a trailing odd byte, which is the behaviour a malformed BMPString wants. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzDZWjq5uHHgQdp84gXYnw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds comprehensive Content Credentials (C2PA 2.2) support to the image editor, enabling users to verify and sign JPEG files with cryptographic manifests that record the complete edit history.
Summary
The implementation provides a complete C2PA claim generator that runs entirely in the browser, with no external dependencies or network calls. Users can now:
Key Changes
Core C2PA Implementation (
crates/imagecore/src/c2pa/)manifest.rs: Complete manifest builder implementing the two-render pattern to solve the circular dependency between manifest size and file hash (§10.4 of spec)jumbf.rs: ISO/IEC 19566-5 JUMBF box container format with proper superbox handling and hashingjpegxt.rs: JPEG XT APP11 segment embedding with exclusion range calculation for hard binding (§A.3.1, §18.5.3)cbor.rs: Deterministic CBOR encoding (RFC 8949 §4.2.1) with fixed-width placeholders for offsetscose.rs: COSE_Sign1 signatures (RFC 8152/9360) with detached payloads and x5chain certificate handlingx509.rs: Minimal DER parser to extract certificate details without full validationsigner.rs: Integration with build-time signing credentialsBuild & Signing Infrastructure
build.rs: Embeds signing certificate and key from environment or demo filessigning/generate.sh: Script to generate demo P-256 ECDSA certificates following C2PA profile (§14.5.1)UI & Integration
src/credentials.ts: New credentials panel with manifest validation display, action history, and signer informationsrc/editor.css: Styling for credential states (valid/invalid/unsigned) with clear visual hierarchyindex.html: Credentials panel UI with manifest details and thumbnail displaysrc/types.ts: TypeScript interfaces for credential reports and signer informationsrc/main.ts,src/engine.ts,src/worker.ts: Integration points for credential reading/writingTesting
crates/imagecore/tests/c2pa.rs: End-to-end tests verifying manifest generation, validation, and edit chainingNotable Implementation Details
c2pa.hash.dataassertion excludes the manifest's own bytes from the file hash, with precise exclusion ranges covering JPEG APP11 markers and length fieldsLimitations
signing/README.md)https://claude.ai/code/session_01QzDZWjq5uHHgQdp84gXYnw