v0.4.0 — read the C2PA 2.x shapes real files use
Reads the C2PA 2.x shapes that current writers — OpenAI's and Google's signers among them — actually put in a file, and that the library previously read back empty or as malformed.
Fixed — timestamps in a bare CMS ContentInfo
sigTst and sigTst2 may hold either an RFC 3161 TimeStampResp or a bare CMS ContentInfo. Both descents probed with a two-RawValue struct, which matches any two-element SEQUENCE — including a ContentInfo — so a ContentInfo token was descended into as a response, failed, and was rejected. Read returned a zero SignedAt and Validate recorded timeStamp.mismatch ("malformed timestamp token") for every timestamp of that shape, which is what Google- and OpenAI-signed images carry.
The two shapes are now told apart by their first element (an OID for a ContentInfo, a SEQUENCE for a PKIStatusInfo) in one shared helper used by both call sites.
signingTime also looked only at sigTst, while Validate's extraction already handled sigTst2. A c2pa.claim.v2 signature carries its timestamp only in sigTst2, so Read's SignedAt stayed zero for exactly those files. It now reads either container.
Fixed — claim_generator_info
An array of entries in 1.x, a single entry in 2.x. A c2pa.claim.v2 carries only the 2.x shape and no flat claim_generator, so Info.ClaimGenerator came back empty for every such file. Both shapes are now read.
Added — Info.SoftwareAgent
softwareAgent names the model rather than the signing service (e.g. gpt-image 2.0 on OpenAI's c2pa.created action) and had no field. It is read as the 1.x string, the 2.x inline entry, or a 2.x softwareAgentIndex into the assertion's softwareAgents array, and is assigned per manifest the way ClaimGenerator is, so the two fields always describe the same manifest.
Tests
New fixture testdata/c2pa_2x_openai.png — an OpenAI-generated PNG carrying all three 2.x shapes; reverting the fixes under TestC2PA2xOpenAIFixture reproduces all three symptoms. The timestamp tests unwrap the existing signed fixture's own token to get the bare ContentInfo shape and assert it reaches timeStamp.validated; the remaining cases synthesise CBOR.
No API removals or behaviour changes beyond the above.
Full changelog: v0.3.0...v0.4.0