@svta/cml-c2pa changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Changed
- Every code example in the guides now typechecks (#565).
- The README no longer describes the peer dependencies. The website lists them from
package.jsonon the package page.
1.7.0 - 2026-10-22
Fixed
validateC2paInitSegment,validateC2paSegment,validateC2paManifestBoxSegment, andvalidateC2paMerkleSegmentnow hash a root box with alargesizeheader or a size of 0. The content hash also covers every root box after such a box (#566).validateC2paInitSegment,validateC2paSegment,validateC2paManifestBoxSegment, andvalidateC2paMerkleSegmentnow exclude only the nested box for an xpath such as/moov/udta. The content hash keeps the other bytes of the root box (#567).BmffHashExclusionnow has thelength,subset,version,flags, andexactfields of C2PA section 18.6.2. Thelength,version,flags, andexactfields narrow the match to a box with those values. Thesubsetfield excludes only the listed byte ranges of the box (#567).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow fail withC2paStatusCode.ASSERTION_BMFFHASH_MALFORMEDfor a malformed exclusion. Malformed means an xpath outside the grammar of C2PA section 18.6.2, or a field of the wrong type (#567).
Changed
- The manifest box guide states that
C2paStatusCode.CLAIM_HARD_BINDINGS_MISSINGappears only for a manifest with a claim that decodes. - The README now has a When to use section. The section names the tasks that the package covers and the entry point of each task.
1.6.0 - 2026-10-21
Security
validateC2paInitSegmentnow fails an init segment without ac2pa.hash.bmff.v3assertion withLiveVideoStatusCode.INIT_INVALID. The result for a manifest without any hard binding assertion also includesC2paStatusCode.CLAIM_HARD_BINDINGS_MISSING(#552).validateC2paInitSegmentnow fails withLiveVideoStatusCode.INIT_INVALIDif the init content hash does not include themoovbox (#552).
Fixed
validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow fail a segment if the CBOR or JSON of an assertion does not decode. The result includes the newC2paStatusCode.ASSERTION_CBOR_INVALIDorC2paStatusCode.ASSERTION_JSON_INVALID.- The validation functions now reject a CBOR map with a duplicate key. A claim or an assertion with such a map fails with
C2paStatusCode.CLAIM_CBOR_INVALIDorC2paStatusCode.ASSERTION_CBOR_INVALID. Keys such as1and"1"count as duplicates (#516). validateC2paInitSegmentno longer reportsLiveVideoStatusCode.SESSIONKEY_INVALIDfor an init segment without ac2pa.session-keysassertion (#555). The C2PA Manifest Box method of C2PA 2.4 section 19.3 needs no session keys. Ac2pa.session-keysassertion with an invalid key, or with no key, still reportsSESSIONKEY_INVALID.validateC2paInitSegmentno longer reportsSESSIONKEY_INVALIDfor an init segment whose session keys have all expired. The result has an emptysessionKeyslist, and each VSI segment then fails withLiveVideoStatusCode.SEGMENT_INVALID, as C2PA 2.4 section 19.7.3 requires.SESSIONKEY_INVALIDno longer means that the init segment has no valid session key. To detect that case, checksessionKeys.length === 0on the result ofvalidateC2paInitSegment.
Changed
validateC2paManifestBoxSegmentnow reports a missing hard binding only for a manifest with a claim that decodes. A manifest with a claim code such asC2paStatusCode.CLAIM_MISSINGno longer also reportsC2paStatusCode.CLAIM_HARD_BINDINGS_MISSING(#552).
1.5.0 - 2026-10-21
Security
validateC2paManifestBoxSegmentnow fails a segment whose manifest has noc2pa.hash.bmff.v3assertion. The result includesLiveVideoStatusCode.SEGMENT_INVALID(#522).validateC2paManifestBoxSegmentnow fails a segment whosec2pa.hash.bmff.v3assertion is not a map, has nohash, or has only avaluefield. The result includesC2paStatusCode.ASSERTION_BMFFHASH_MALFORMED.validateC2paInitSegmentnow fails withC2paStatusCode.ASSERTION_BMFFHASH_MALFORMEDfor a malformedc2pa.hash.bmff.v3assertion (#550). Malformed means not a map, nohashand no Merkle maps, or only avaluefield.validateC2paInitSegmentno longer returnssessionKeysormerkleMapsfor an init segment that fails a content hash check. This covers a flat hash or a MerkleinitHashthat does not match, and a Merkle map that does not conform.
Added
C2paStatusCodenow hasCLAIM_HARD_BINDINGS_MISSING(claim.hardBindings.missing).validateC2paManifestBoxSegmentreports it for a manifest without any hard binding assertion.
Fixed
validateC2paSegmentno longer fails a segment after a gap in the sequence numbers, so the authentic segment after a tampered segment passes (#546).result.sequenceResultstill reportsSequenceValidationReason.GAP_DETECTEDwithmissingFromandmissingTo.validateC2paSegmentnow reportsLiveVideoStatusCode.SEGMENT_INVALIDinstead ofLiveVideoStatusCode.ASSERTION_INVALIDfor a duplicate, out-of-order, or below-minimum sequence number. The order check is a library rule with the code of the C2PA 2.5 draft. If your application handleslivevideo.assertion.invalidfor VSI segments, handlelivevideo.segment.invalidinstead.validateC2paManifestBoxSegmentnow chainspreviousManifestIdto the manifest identifier of the previous segment, theurn:c2pa:JUMBF label, and compares the identifiers exactly (#545). It no longer removes anxmp:iid:orurn:uuid:prefix or ignores case.- The JUMBF label counts as the manifest identifier only if the claim
signatureURI names it, in the absolute formself#jumbf=/c2pa/<label>/c2pa.signature. For a URI relative to the manifest, such asself#jumbf=c2pa.signature,validateC2paManifestBoxSegmentfails withLiveVideoStatusCode.SEGMENT_INVALIDandvalidateC2paInitSegmentreturns anullmanifestId. A changed label therefore cannot take the identity of an omitted segment. - A claim without a
signatureURI now fails withC2paStatusCode.CLAIM_SIGNATURE_MISSING. So does a URI that does not resolve to the signature box of the same manifest.validateC2paInitSegmentandvalidateC2paManifestBoxSegmentboth report it. - A claim
instanceID,claim_generator, ordc:titlethat is not a text string now counts as absent.C2paManifest.instanceIdisnullfor such aninstanceID(#521).
Changed
nextManifestIdandInitSegmentValidation.manifestIdnow hold theurn:c2pa:URN of the manifest instead of the claiminstanceID. ThemanifestIdof a Verifiable Segment Info now matches themanifestIdof its init segment. If your application stores alastManifestIdbetween sessions, discard values that it stored with an earlier version.validateC2paInitSegmentreports ac2pa.hash.bmff.v3assertion with ahashthat is not a byte string withC2paStatusCode.ASSERTION_BMFFHASH_MALFORMEDinstead ofLiveVideoStatusCode.INIT_INVALID. The same code replacesINIT_INVALIDfor exclusions that do not conform to C2PA section 18.6. HandleASSERTION_BMFFHASH_MALFORMEDwhere you handleINIT_INVALID.- A flat content hash that does not match now adds
C2paStatusCode.ASSERTION_BMFFHASH_MISMATCHnext toLiveVideoStatusCode.INIT_INVALID.
1.4.1 - 2026-10-18
Changed
- Narrow the
@svta/cml-iso-bmffand@svta/cml-utilspeer dependency ranges from>=to^. The published package no longer accepts a later major version of either peer.
1.4.0 - 2026-10-10
Security
validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest whose assertion store holds two or more boxes with the same label. The result includesC2paStatusCode.ASSERTION_MISSINGwhen the claim references that label, orASSERTION_UNDECLAREDwhen it does not (GHSA-7jrg-x839-w8wv).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest with more than one hard binding assertion. The result includes the newC2paStatusCode.ASSERTION_MULTIPLE_HARD_BINDINGS(GHSA-7jrg-x839-w8wv).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest whose assertion store holds an assertion the claim does not reference. The result includes the newC2paStatusCode.ASSERTION_UNDECLARED(GHSA-7jrg-x839-w8wv).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest with more than one claim box. The result includes the newC2paStatusCode.CLAIM_MULTIPLE(GHSA-7jrg-x839-w8wv).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest with more than one assertion store. The result includesC2paStatusCode.ASSERTION_MISSING(GHSA-7jrg-x839-w8wv).validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reject a manifest with more than one signature box. The result includesC2paStatusCode.CLAIM_SIGNATURE_MISSING(GHSA-7jrg-x839-w8wv).validateC2paManifestBoxSegmentnow readssequenceNumber,streamId,previousManifestId,continuityMethod, andbmffHashHexonly from assertions whose hash matches a claim reference. Each field isnullotherwise. CheckisValidbefore you use any field (GHSA-7jrg-x839-w8wv).validateC2paInitSegmentnow returns emptysessionKeysandmerkleMapswhen any manifest integrity check fails. CheckisValidbefore you use either list (GHSA-7jrg-x839-w8wv).validateC2paManifestBoxSegmentno longer updatesnextManifestIdandnextStatefrom a segment that fails verification, is on another stream, or does not move the sequence number forward. A segment that fails only the continuity check still updates them, so a stream recovers after a lost or rejected segment. After a seek, an encoder restart, or a new stream ID, passnullaslastManifestIdandundefinedasstateto start a new state (GHSA-jjpj-wgh4-rm9c).validateC2paSegmentno longer updatesnextSequenceStatefrom a segment whose signature or content hash does not verify. The same applies to a segment whose session key is missing, not yet active, or expired. A replayed or forged sequence number from such a segment no longer affects later segments (GHSA-jjpj-wgh4-rm9c).
Added
C2paStatusCodenow hasASSERTION_MULTIPLE_HARD_BINDINGS(assertion.multipleHardBindings) for a manifest with more than one hard binding assertion.C2paStatusCodenow hasASSERTION_UNDECLARED(assertion.undeclared) for an assertion the claim does not reference.C2paStatusCodenow hasCLAIM_MULTIPLE(claim.multiple) for a manifest with more than one claim box.C2paStatusCodenow hasCLAIM_CBOR_INVALID(claim.cbor.invalid) for a claim box whose CBOR does not decode.C2paStatusCodenow hasCLAIM_MALFORMED(claim.malformed) for a claim box whose CBOR is not a map.
Changed
- A manifest-box segment whose claim does not verify the
c2pa.livevideo.segmentassertion now also reportsLiveVideoStatusCode.ASSERTION_INVALIDandLiveVideoStatusCode.CONTINUITY_METHOD_INVALID. This includes a claim box that does not decode. ManifestBoxValidationResult.errorCodesno longer repeats aC2paStatusCodevalue.C2paAssertion.datanow holds an unsigned integer up toNumber.MAX_SAFE_INTEGERas a number instead of a BigInt. The same applies to the assertion data that aManifestBoxContinuityValidatorreceives. See Working with Manifest Data in the Results and Error Codes guide.- A CBOR tag other than tag 0 and tag 1 is now a plain
{ tag, value }object inC2paAssertion.data. This includes tags 2, 3, 65 to 87, 258, and 55799, which were a BigInt, a typed array, aSet, or the tagged value. Tag 0 and tag 1 are still aDate. - A map key
__proto__in a CBOR assertion is now an own property of the decoded object. - A byte string with CBOR tag 64 no longer counts as a byte string in any field, such as the
hashof ac2pa.hash.bmff.v3assertion. If your signer uses the cbor-x encoder in Node.js, settagUint8Array: false. - A
COSE_Sign1structure must now carry CBOR tag 18 (COSE_Sign1_Tagged). A claim signature without the tag fails withC2paStatusCode.CLAIM_SIGNATURE_MISMATCH, and a signer binding without the tag makes its session key invalid. If your signer writes the bare array, writeCOSE_Sign1_Taggedinstead. validateC2paSegmentnow throws an error for a Verifiable Segment Info without CBOR tag 18.- CBOR nested deeper than 128 levels of arrays, maps, and tags no longer decodes.
- A CBOR array of integers no longer counts as a byte string in any field, such as a
kid, asignerBinding, or a hash. - The VSI/EMSG Validation guide now describes the session key validity period, the accepted signer binding payloads with CBOR tag 18, and the sequence number range.
- The Results and Error Codes guide now has an Invalid Session Keys section and describes the value shapes of
C2paAssertion.data. Its error code table now describes the validity period of session keys. - The Manifest Box Validation guide now states when
sequenceNumberisnulland names the error code for a malformed hash. - The examples in the README and the VSI/EMSG Validation guide now check
isValidbefore they usesessionKeys. Do not use the session keys of a result withisValid: false. - The code examples in the validation guides now typecheck.
Removed
- The package no longer has the
cbor-xpeer dependency. If you installedcbor-xfor this package only, remove it from your dependencies.
Fixed
validateC2paInitSegmentnow keeps a session key that is not yet active. Segments signed with that key no longer fail withLiveVideoStatusCode.SEGMENT_INVALID.- The signer binding check now accepts the certificate itself or the certificate as a CBOR byte string as the signed payload.
- The payload field of a signer binding must now be nil, an empty byte string, or an exact copy of the signed payload. Other content makes the session key invalid.
validateC2paInitSegmentnow reportsLiveVideoStatusCode.SESSIONKEY_INVALIDfor an invalid session key in thec2pa.session-keysassertion. An expired session key does not count as invalid. The function does not check every rule of C2PA section 18.25.2.validateC2paInitSegmentnow reads thekidof a session key only from its COSE key.minSequenceNumberandvalidityPeriodmust now be unsigned integers up toNumber.MAX_SAFE_INTEGER, andcreatedAtmust be CBOR tag 0 with an RFC 3339 date-time. A session key that breaks one of these rules is invalid. If an init segment now fails, check the fields of its session keys and the signer binding.validateC2paInitSegmentnow reportsSESSIONKEY_INVALIDfor a session key that it cannot verify, such as an unsupported key type. It no longer throws an error in that case.validateC2paInitSegmentnow reportsSESSIONKEY_INVALIDwhen thec2pa.session-keysassertion has no session key or holds CBOR that does not decode. This also applies in VOD Merkle mode.validateC2paSegmentnow reportsLiveVideoStatusCode.SEGMENT_INVALIDfor a segment whose session key is not yet active or has expired. It no longer reportsSESSIONKEY_INVALIDfor such a segment. HandleSEGMENT_INVALIDinstead, and update dashboards that countlivevideo.sessionkey.invalidfor media segments.validateC2paSegmentnow accepts asequenceNumberof 2^32 or more, up toNumber.MAX_SAFE_INTEGER. A larger value, a negative value, or a fraction causes an error that names the supported range.validateC2paManifestBoxSegmentnow accepts asequenceNumberof 2^32 or more, up toNumber.MAX_SAFE_INTEGER. A larger value, or a value that is not an unsigned integer, counts as a missing value and yieldssequenceNumber: null.- The validation functions now fail fast on a CBOR item that declares more content than the input holds. Such a segment no longer costs seconds of processor time or gigabytes of memory.
validateC2paManifestBoxSegmentnow reportsC2paStatusCode.ASSERTION_BMFFHASH_MALFORMEDwhen thehashof thec2pa.hash.bmff.v3assertion is not a byte string.- A
c2pa.hash.bmff.v3exclusion must now have anxpathtext string, and eachdataconstraint must have an unsigned integeroffsetand a byte stringvalue.validateC2paManifestBoxSegmentreportsC2paStatusCode.ASSERTION_BMFFHASH_MALFORMEDfor an exclusion that breaks this rule.validateC2paInitSegmentreportsASSERTION_BMFFHASH_MALFORMEDfor a Merkle assertion andLiveVideoStatusCode.INIT_INVALIDfor a flat hash, andvalidateC2paSegmentthrows an error. validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reportC2paStatusCode.CLAIM_CBOR_INVALIDfor a claim box whose CBOR does not decode. Such a manifest no longer validates on the claim signature alone.validateC2paInitSegmentandvalidateC2paManifestBoxSegmentnow reportC2paStatusCode.CLAIM_MALFORMEDfor a claim box whose CBOR is not a map.- A bundle that imports only a constant from the package no longer keeps a
TextDecoderconstruction or the certificate and signature constants of the package.
1.3.0 - 2026-09-29
Fixed
C2paStatusCode.CLAIM_SIGNATURE_MISMATCHnow has the valueclaimSignature.mismatchfrom the C2PA specification (#469). The old value wasclaim.signature.mismatch. The constant name does not change. If your code compareserrorCodeswith the old value, compare withC2paStatusCode.CLAIM_SIGNATURE_MISMATCHinstead. Also update logs, dashboards, and alert rules that store the old value.
1.2.0 - 2026-09-25
Security
validateC2paManifestBoxSegmentandvalidateC2paInitSegmentno longer returnisValid: truefor a manifest without a verifiable claim signature. An attacker who controlled the media bytes could forge provenance without a signing key. See GHSA-h5r3-7p8g-g3q2. A manifest without ac2pa.signaturebox now fails withC2paStatusCode.CLAIM_SIGNATURE_MISSING. A manifest whose claim signature has no certificate now fails withC2paStatusCode.CLAIM_SIGNATURE_MISMATCH. A manifest without a claim box now fails withC2paStatusCode.CLAIM_MISSING.
Added
C2paStatusCode.CLAIM_SIGNATURE_MISSING(claimSignature.missing) andC2paStatusCode.CLAIM_MISSING(claim.missing).ManifestBoxValidationResult.certificate: the DER-encoded end-entity certificate from the claim signature (#468). The value isnullwhen the claim signature is absent or has no certificate. You can now compare this certificate with your trust list.
Changed
- Validation guides and README: the library does not compare the certificate from the claim signature with a trust list. Check
isValidbefore you usemerkleMaps. See the new Signer Trust section of the Results and Error Codes guide. - Validation guides and README: rewritten prose for readers who do not read English as a first language. The validation guides define the term Merkle tree at its first use. Empty table cells and a code comment in the validation guides no longer use an em dash.
- README: the code examples are complete. The code examples take the segment URLs as function parameters.
1.1.3 - 2026-09-15
Changed
- Update
@svta/cml-iso-bmffto 1.0.6 - Update
@svta/cml-utilsto 1.6.1
1.1.2 - 2026-08-11
Changed
- Update
@svta/cml-iso-bmffto 1.0.5
1.1.1 - 2026-07-28
Changed
- Update
@svta/cml-iso-bmffto 1.0.4 - Update
@svta/cml-utilsto 1.6.0
1.1.0 - 2026-07-21
Added
- Custom continuity method support in manifest-box validation (C2PA §19.3.2 / §19.7.2):
validateC2paManifestBoxSegmentaccepts an optionalcontinuityValidatorto verify implementer-defined continuity methods; segments declaring an unregistered method keep failing withlivevideo.continuityMethod.invalidper spec LiveVideoStatusCode.CONTINUITY_METHOD_UNSUPPORTED(livevideo.continuityMethod.unsupported), emitted alongsidecontinuityMethod.invalidso consumers can distinguish an unverifiable custom method from a broken chainManifestBoxValidationOptionsandManifestBoxContinuityValidatortypes- VOD Merkle validation (C2PA §15.12.2 / §18.6):
validateC2paMerkleSegmentverifies fragmented MP4 media segments against the merkle maps from the init manifest — per-track Merkle proof verification withlocationcontinuity enforced via caller-held state MerkleMap,MerkleSegmentState, andMerkleSegmentValidationtypesvalidateC2paInitSegmentextracts merkle maps from thec2pa.hash.bmff.v3assertion, validates each entry'sinitHashbinding, and returns them asmerkleMapsC2paStatusCodeentriesassertion.bmffHash.malformedandassertion.bmffHash.mismatch
Changed
InitSegmentValidationgains amerkleMapsfield;SESSIONKEY_INVALIDis no longer raised for VOD Merkle streams
Fixed
validateC2paManifestBoxSegmentnow enforces the 8-byte box-offset prefix (C2PA §18.6.2) when verifying the flatc2pa.hash.bmff.v3assertion hash, matching c2pa-rs; unprefixed flat hashes are no longer accepted. The VSI path (§19.7.3) keeps dual-mode validation since its hash comes from the VSI map, not a §18.6.2 assertion.validateC2paInitSegmentnow enforces the 8-byte box-offset prefix (C2PA §18.6.2) when verifying the flatc2pa.hash.bmff.v3assertion hash, matching c2pa-rs; unprefixed flat hashes are no longer accepted- The module-scope hex lookup table in
bytesToHexis now marked side-effect free so consumer bundlers can drop it when unused (follow-up to the module-scope side-effect audit in #382) - Top-level
TextDecoderandTextEncoderinstantiations are now marked side-effect free so consumer bundlers can drop them when unused (#382)
1.0.1 - 2026-05-13
Changed
- Update
@svta/cml-iso-bmffto 1.0.2 - Update
@svta/cml-utilsto 1.5.0
1.0.0 - 2026-04-14
Added
- Package scaffold with core C2PA types (
C2paManifest,C2paAssertion,C2paSignatureInfo) LiveVideoStatusCode— standardized C2PA §19.7 error code constants- JUMBF box parsing (ISO 19566-5)
- COSE_Sign1 cryptographic support (RFC 9052): decoding, signature verification, key conversion, signer binding
- X.509 certificate parsing (issuer, validity period)
- BMFF content hash computation and validation
- EMSG box parsing (ISO 14496-12, v0 and v1)
- VSI (Verifiable Segment Info) CBOR map decoding
- Sequence number validation (monotonic, gap/duplicate detection)
validateC2paInitSegment(bytes)— validate init segment: manifest, certificate, BMFF hash, session keysvalidateC2paSegment(bytes, sessionKeys, state?)— validate media segment via VSI/EMSG method (§19.7.3)validateC2paManifestBoxSegment(bytes, lastId, state?)— validate manifest-box segment (§19.7.2)- All validation results return
isValid+errorCodeswith C2PA failure codes