What changed
v0.2.0 adds C2PA 2.4 validation for Annex A.8 text and changes the signed
manifest bytes from embed().
Upgrade: Existing v0.1.2 marks still verify; do not rewrite them. New
validation rules can change verdicts either way. Review the breaking changes
if you useEmbedContext, catch
MarkCorruptError, import resource-limit constants, or treatVALIDas
proof of an AI disclosure.
pip install --upgrade c2patxt==0.2.0c2patxt supports Python 3.10 through 3.14. Its base install depends on
cryptography~=48.0.
Breaking changes
- Signed output:
embed()moves padding from signedc2pa.hash.datato the
unprotected COSE header. The A.8 wrapper version stays1; the signed manifest
bytes change. - Meaning of
VALID:verify()may now returnVALIDwithout
c2pa.ai-disclosureordigitalSourceType; it no longer checksmodelType.
embed()still writes all three. If your application requires
an AI disclosure, check the authenticated assertions.VALIDdoes not prove
an AI disclosure. - Public API:
MarkCorruptError(msg, document, pos, code)becomes
MarkCorruptError(msg, pos, document_length=None, code=...);
.document_lengthreplaces.doc. v0.2.0 removesMAX_JUMBF_DEPTHand adds
MAX_CBOR_DEPTH,MAX_NONSTARTERS, andTextNormalizationError.
at_least()andraise_for_state()raiseValueErrorforUNMARKEDand
INVALIDthresholds. - Inputs to
embed():media_typerequires an exact lowercasetext/
prefix and no parameters.embed()rejectstext/htmlandtext/markdown.
For customEmbedContextvalues,manifest_uuidmust be UUIDv4;
instance_idandgenerator_namemust use 1 to 1,000,000 UTF-8 bytes; and
whenmust use a whole-minute UTC offset. - Certificates and
strip():Signerchecks issuer links and, by default,
each certificate's role profile.embed()checks certificate validity at
EmbedContext.when.strip()raisesMarkCorruptErrorrather than guess how
much to remove when a corrupt wrapper has no known length.
Validation changes
- The verifier accepts valid non-deterministic CBOR but rejects duplicate keys.
It accepts ES256, ES384, ES512, PS256, PS384, PS512, and Ed25519 claim
signatures;embed()signs with Ed25519. It accepts integer and legacy string
x5chainlabels in either the protected or unprotected COSE header and checks
critical headers. - The verifier reads
c2maand legacyc2mdStandard Manifests. It considers
c2cmandc2umduring selection, then reports them as unsupported. It reads
c2pa.actionsv1;embed()writes v2. - For text with several wrappers, the verifier requires the chosen wrapper's
signed exclusion to name its span. It permits extra ordered exclusions and
records them inVerdict.informational. New checks cover action relationships,
self-redaction, soft bindings, icons, cloud and external data, time-stamp
shape, and multiple parents. - The verifier checks every carried certificate's role profile and validity
window, but not issuer links, chain order, or trust. The caller's
TrustEvaluatormust build the RFC 5280 path and set anchor policy.
Safety, performance, and limits
- NFC normalization rejects more than 30 consecutive non-starters after NFKD
decomposition.embed()raisesTextNormalizationError;verify()reports
a malformed data hash. - The padding search prepares one manifest and signature per exclusion candidate.
The CodSpeed comparison shows the 1 MB embed simulation change
from 1,394.2 ms to 30.4 ms. It also shows four
regressions: 12-byte embed simulation and memory, valid 1 MB verification
memory, and hostile-decoy simulation. It contains no wall-clock measurement.
c2patxt includes no credential, trust anchors, path builder, or network
retrieval. A correct self-signed mark returns VALID with
signingCredential.untrusted, which does not mean validation failed. c2patxt
supports no post-quantum signature algorithms.
c2patxt is our implementation of C2PA 2.4 Annex A.8; the C2PA consortium did
not publish it. The verifier does not check sigTst2, rVals, revocation, full
ingredient validation, cross-manifest history, or other text and media carriers.
It returns general.error for a non-empty c2pa.time-stamp or a linked
ingredient assertion. See implementation scope and
C2PA compatibility.
Checks and links
make test passes 1,313 tests with 95.95% line coverage; make lint also passes.
Implementation scope · C2PA compatibility ·
Known deviations · Robustness measurements
Full changelog: v0.1.2...v0.2.0