Every identity assertion WAI wrote was reported cawg.x509.signature.mismatch by the C2PA SDK's validator. Read at their sources: Identity Assertion 1.0 to 1.3 §8.2.1 (the payload is serialized in RFC 8949 §4.2.1's core deterministic encoding and those bytes are signed; no later version or erratum), and the c2pa crate at 0.91.7 and 0.91.11, its newest release, which signs and verifies its own serialization of the payload's members in the schema's order (referenced_assertions, sig_type, role; url, alg, hash) with the writer that keeps declaration order. The bytes signed were the whole difference: the COSE structure, protected header, sig_type, hashed URIs and chain of a WAI assertion and of one the SDK's builder writes are the same. c2pa-binding §4.10, §4.11, §6.2 step 12, §6.3, §9.2, §9.3 and decisions D6 (revised): a writer signs the schema's order by default and may be asked for the core deterministic encoding (identity::Signed); the stored assertion stays deterministic either way. The verifier accepts a signature over either serialization of the payload's members, states which, and refuses one over a third order, over other members, or by another key. On the SDK's path an identity assertion is read in any well-formed encoding (one the SDK's builder wrote is accepted) and a map with a key twice is refused. Tests run the SDK's validator, configured with the corpora's anchors, on manifests signed through the SDK and on the store writer's stores beside their envelopes: trusted, no failure code, cawg.x509.credential.trusted, cawg.x509.signature.validated and cawg.identity.well-formed for each identity assertion. The strict form gets exactly cawg.x509.signature.mismatch, pinned. c2pa-sdk-identity/vectors.json pins the validator's report of every file at the SDK's version. Both corpora regenerated with their tools (c2pa-identity byte for byte on x86_64 and aarch64); new vectors for the strict form, the SDK builder's assertion, a signature over a third order and over other members. Both Python verifiers compute the two serializations themselves and are caught without either and when they take the members in any order. The wire break and the three API changes are acknowledged in the change note. Co-Authored-By: Claude Fable 5.2 <[email protected]>
6.9 KiB
live-conformance/c2pa-identity — trust lists, soft bindings, the ISCC soft binding and identifier, and identities
The corpus of extensions/c2pa-binding.md §9.2: C2PA Manifest
Stores beside stored WAI envelopes that carry, after the three assertions of §4.1, soft bindings of
wai-mark:tent64 (§4.8), the ISCC soft binding io.iscc.v0 and the ISCC identifier (§4.9) and
the Creator Assertions Working Group's
identity assertions (§4.10), verified against trust lists pinned by digest (§6.4); and what a
verifier states of each.
Files. vectors.json lists the vectors, each its name, envelope, store, receipts (as
in c2pa-envelope/), claimLists and identityLists (the trust lists its verifier is configured
with, each its file, the uri and type the verifier names it by, and the digest it pinned),
robustness (the robustness record and claims its verifier was given, in order) and expect.
trust/ holds the lists in the form the C2PA Trust List is published in; robustness/ the
record of wai-mark:tent64 over the corpus's first two pictures, the claim of every attack it
shows survived, and a claim of one attack more; objects/ the four envelopes, the receipts, and
for each envelope its first good store, as an origin serves them (a second good store over one
envelope is in its vector's own directory). A file another vector already wrote is named by that
vector's path. anchors is empty: every anchor comes from a list. time is the instant T.
Equivalence. A conforming verifier given a vector's files and T reaches the outcome
expect states (§6): for refuse, the reason of §7 and the code §7 gives it (c2pa, the
C2PA 2.4 code for §7.1's reasons and the Identity Assertion's for an identity's, null where §7
gives none); for accept, what §6.3 states, among it the trustLists that held the claim
signer's anchor, the softBindings, the isccBinding (its alg, its sequence in its canonical
form as iscc, and its generator), the iscc identifier in its canonical form and the
identities with the identity list that held each anchor.
The vectors.
- Good:
bound-full(a soft binding, the ISCC identifier and the creator's identity over the hard binding, the actions, the WAI assertion and the soft binding),bound-two-identities(the publisher's identity referencing the creator's),bound-identity-only(the unmarked picture's store with one identity),bound-identity-deterministic(the same store with the identity signed over the payload's core deterministic encoding, the form a writer may ask for; every other identity of the corpus is signed over the payload's members in the schema's order, §4.10's default, and each accepted identity'ssignedsays which),bound-iscc-binding(the first picture's store with its watermark's soft binding, the ISCC soft binding, the identifier and the creator's identity over the three assertions and both soft bindings),bound-iscc-binding-only(the unmarked picture's store with the ISCC soft binding alone: an Image-Code, a Data-Code and an Instance-Code) andbound-featureless-picture(a picture of one grey: the ISCC soft binding holds a Data-Code and an Instance-Code and no Image-Code, and the identifier holds the featureless Image-Code). - An identity's signature is refused (
identity_signature_mismatch) when it is the creator's under the publisher's certificates (identity-credential-swapped), over the payload's members in a third order, which is neither serialization of §4.10 (identity-signed-third-order), or over a payload with another role than the one the assertion holds (identity-signed-other-members). - One negative for each reason of §7.2, and three of §7.1's
credential_untrusted: a signer under an anchor no list holds, an anchor its list withdrew, and an anchor whose trust starts after T; each name says its defect. - The ISCC soft binding's negatives,
iscc-binding-*: computed from another envelope; its Data-Code the envelope's and its Instance-Code that of the envelope with one bit changed, which iscc §5 would match and §6.2 step 11 refuses; a unit missing; units of 64 bits; a featureless Image-Code included (iscc_mismatch, each); its sequence cut by a byte; its value the sequence's text; no generator; a second ISCC binding; the ISCC binding before the watermark's (manifest_malformed, each); and the watermark's robustness claim named as its own (soft_binding_unevidenced).
Provenance. wai_c2pa_identity_vectors gen writes every file but this one from fixed seeds:
every key is SHA-256 of a label and no key is one anyone holds; the certificates are issued under
the corpus's own four test anchors; the lists are the corpus's own; the pictures are rings and
ramps the generator draws, two of them marked with wai-mark:tent64, and one picture of a single
grey, each written as PNG by the
packaging corpus's still writer, so each store states the source type algorithmicMedia. The
robustness record's table is the harness's (robustness-evidence §6), run over the two unmarked
pictures, and is reproduced by anyone who holds them.
Checked by. wai_c2pa_identity_vectors verify and tests/c2pa_identity.rs in wai-rs
(every file regenerates byte for byte and no other is here; every vector gets its outcome; every
reason of §7.2 refuses one), and ../c2pa_live_verify.py, the second implementation, written from
the extension's text, which reads the lists and the identity assertions itself, detects the mark
with _wai_mark.py, verifies the robustness evidence with robustness_verify.py and recomputes
the ISCC units with iscc_verify.py, requires every mutated copy of a good vector to be refused,
and requires every one of its own checks, dropped, to be caught.
Read by a C2PA SDK's validator. Every good store here that carries an identity assertion is
given to a C2PA SDK's validator beside its envelope, with the certificates of the vector's trust
lists as its anchors for claim signers and for identity credentials, by the test
the_sdks_validator_accepts_the_stores_the_store_writer_writes of c2pa_emit::identity_corpus
(wai-rs, features c2pa_emit,c2pa_envelope; it runs in the conformance workflow's
c2pa-emitter job). Of each store whose identities are signed over the schema's order the
validator's state is trusted, it reports the claim signature validated, the data hash matched
and no failure code, and for each identity assertion cawg.x509.credential.trusted,
cawg.x509.signature.validated and cawg.identity.well-formed. Of
bound-identity-deterministic it reports cawg.x509.signature.mismatch and nothing else: that
validator does not verify the core deterministic encoding (c2pa-binding §4.10). It reads the
claim, the hard binding, the hashed URIs and the identity assertions; the soft bindings, the
ISCC assertions and WAI's own assertion it carries as assertions whose hashes it checks and
whose content it does not judge.