@svta/cml-c2pa 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE.md ADDED
@@ -0,0 +1,2 @@
1
+ Streaming Video Technology Alliance Common Media Library Copyright (c) 2026
2
+ Streaming Video Technology Alliance
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @svta/cml-c2pa
2
+
3
+ C2PA (Coalition for Content Provenance and Authenticity) live video validation for BMFF/MP4 containers.
4
+
5
+ Supports both segment validation methods defined in the C2PA specification:
6
+
7
+ - **§19.3 — Per-segment C2PA Manifest Box**: each segment embeds a full C2PA manifest with COSE signature
8
+ - **§19.4 — Verifiable Segment Info (VSI/EMSG)**: the init segment carries the manifest and session keys; each media segment carries a lightweight signed EMSG box
9
+
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm i @svta/cml-c2pa
15
+ ```
16
+
17
+ > **Note:** `@svta/cml-iso-bmff`, `@svta/cml-utils`, and `cbor-x` are peer dependencies. Most package managers install them automatically, but you may need to add them explicitly.
18
+
19
+ > **Note:** This library uses the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) (`crypto.subtle`) for COSE signature verification and BMFF hash computation. In Node.js 20+ this is available globally. In browsers, `crypto.subtle` requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS or `localhost`).
20
+
21
+ ## Usage
22
+
23
+ ### Manifest Box method (per-segment manifests)
24
+
25
+ ```typescript
26
+ import { validateC2paManifestBoxSegment } from '@svta/cml-c2pa'
27
+ import type { ManifestBoxValidationState } from '@svta/cml-c2pa'
28
+
29
+ let lastManifestId: string | null = null
30
+ let state: ManifestBoxValidationState | undefined
31
+
32
+ for (const segmentUrl of segmentUrls) {
33
+ const bytes = new Uint8Array(await fetch(segmentUrl).then(r => r.arrayBuffer()))
34
+ const { result, nextManifestId, nextState } = await validateC2paManifestBoxSegment(
35
+ bytes,
36
+ lastManifestId,
37
+ state,
38
+ )
39
+ lastManifestId = nextManifestId
40
+ state = nextState
41
+
42
+ console.log(result.isValid, result.errorCodes)
43
+ }
44
+ ```
45
+
46
+ ### VSI/EMSG method (init segment + media segments)
47
+
48
+ ```typescript
49
+ import { validateC2paInitSegment, validateC2paSegment } from '@svta/cml-c2pa'
50
+
51
+ const initBytes = new Uint8Array(await fetch(initUrl).then(r => r.arrayBuffer()))
52
+ const init = await validateC2paInitSegment(initBytes)
53
+
54
+ const segmentBytes = new Uint8Array(await fetch(segmentUrl).then(r => r.arrayBuffer()))
55
+ const validated = await validateC2paSegment(segmentBytes, init.sessionKeys)
56
+ ```
57
+
58
+ ## Docs
59
+
60
+ - [VSI/EMSG Validation](https://github.com/streaming-video-technology-alliance/common-media-library/blob/main/libs/c2pa/docs/vsi-validation.md) — Validate using Verifiable Segment Info (§19.4)
61
+ - [Manifest Box Validation](https://github.com/streaming-video-technology-alliance/common-media-library/blob/main/libs/c2pa/docs/manifest-box-validation.md) — Validate using per-segment manifests (§19.3)
62
+ - [Results and Error Codes](https://github.com/streaming-video-technology-alliance/common-media-library/blob/main/libs/c2pa/docs/results-and-error-codes.md) — Interpret results, error codes, and manifest data
63
+
64
+ ## References
65
+
66
+ - [C2PA Specification v2.3](https://c2pa.org/specifications/specifications/2.3/specs/C2PA_Specification.html)
67
+ - [JUMBF — ISO 19566-5](https://www.iso.org/standard/84635.html)
68
+ - [COSE — RFC 9052](https://www.rfc-editor.org/rfc/rfc9052)
@@ -0,0 +1,339 @@
1
+ import { ValueOf } from "@svta/cml-utils";
2
+
3
+ //#region src/C2paAssertion.d.ts
4
+
5
+ /**
6
+ * A C2PA assertion within a manifest
7
+ *
8
+ * @public
9
+ */
10
+ type C2paAssertion = {
11
+ readonly label: string;
12
+ readonly data: unknown;
13
+ };
14
+ //#endregion
15
+ //#region src/C2paSignatureInfo.d.ts
16
+ /**
17
+ * Signature information extracted from a C2PA claim
18
+ *
19
+ * @public
20
+ */
21
+ type C2paSignatureInfo = {
22
+ readonly issuer: string | null;
23
+ readonly certNotBefore: string | null;
24
+ };
25
+ //#endregion
26
+ //#region src/C2paManifest.d.ts
27
+ /**
28
+ * A C2PA manifest (the active manifest within a manifest store)
29
+ *
30
+ * @public
31
+ */
32
+ type C2paManifest = {
33
+ readonly label: string;
34
+ readonly instanceId: string | null;
35
+ readonly claimGenerator: string | null;
36
+ readonly signatureInfo: C2paSignatureInfo;
37
+ readonly assertions: readonly C2paAssertion[];
38
+ };
39
+ //#endregion
40
+ //#region src/C2paStatusCode.d.ts
41
+ /**
42
+ * Standard C2PA validation status codes for manifest integrity checks,
43
+ * as defined in the C2PA specification chapters 15 and 18.
44
+ *
45
+ * @see {@link https://c2pa.org/specifications/specifications/2.3/specs/C2PA_Specification.html#_claim_validation | C2PA Spec §15.10.3}
46
+ *
47
+ * @enum
48
+ *
49
+ * @public
50
+ */
51
+ declare const C2paStatusCode: {
52
+ /** Assertion hash does not match the hashed URI in the claim (§15.10.3.1) */
53
+ readonly ASSERTION_HASHEDURI_MISMATCH: "assertion.hashedURI.mismatch";
54
+ /** An assertion referenced in the claim is missing from the assertion store (§15.10.3.1) */
55
+ readonly ASSERTION_MISSING: "assertion.missing";
56
+ /** An action requiring an ingredient reference does not have one (§18.15.4.7) */
57
+ readonly ASSERTION_ACTION_INGREDIENT_MISMATCH: "assertion.action.ingredientMismatch";
58
+ /** Claim signature verification failed (§15.7) */
59
+ readonly CLAIM_SIGNATURE_MISMATCH: "claim.signature.mismatch";
60
+ };
61
+ /**
62
+ * Union type of all {@link (C2paStatusCode:variable)} values.
63
+ *
64
+ * @public
65
+ */
66
+ type C2paStatusCode = ValueOf<typeof C2paStatusCode>;
67
+ //#endregion
68
+ //#region src/cose/CoseKeyJwk.d.ts
69
+ /**
70
+ * A JWK (JSON Web Key) representation of a COSE public key,
71
+ * suitable for use with the WebCrypto `importKey` API.
72
+ *
73
+ * @public
74
+ */
75
+ type CoseKeyJwk = {
76
+ readonly kty: string;
77
+ readonly crv: string;
78
+ readonly x: string;
79
+ readonly y?: string;
80
+ };
81
+ //#endregion
82
+ //#region src/LiveVideoStatusCode.d.ts
83
+ /**
84
+ * Standard C2PA failure status codes for live video validation,
85
+ * as defined in the C2PA specification section 19.7.
86
+ *
87
+ * @see {@link https://c2pa.org/specifications/specifications/2.3/specs/C2PA_Specification.html#_live_video_validation_process | C2PA Spec §19.7}
88
+ *
89
+ * @enum
90
+ *
91
+ * @public
92
+ */
93
+ declare const LiveVideoStatusCode: {
94
+ /** Init segment contains an `mdat` box (§19.7.1) */
95
+ readonly INIT_INVALID: "livevideo.init.invalid";
96
+ /** C2PA Manifest Box failed standard validation (§19.7.1) */
97
+ readonly MANIFEST_INVALID: "livevideo.manifest.invalid";
98
+ /** Segment structure invalid: missing Manifest Box/emsg, signature/hash/key failure (§19.7) */
99
+ readonly SEGMENT_INVALID: "livevideo.segment.invalid";
100
+ /** Live video assertion field invalid: sequenceNumber or streamId mismatch (§19.7.2) */
101
+ readonly ASSERTION_INVALID: "livevideo.assertion.invalid";
102
+ /** continuityMethod absent, unsupported, or companion fields incorrect (§19.7.2) */
103
+ readonly CONTINUITY_METHOD_INVALID: "livevideo.continuityMethod.invalid";
104
+ /** Session key invalid: signerBinding failed or required fields absent (§19.7.3) */
105
+ readonly SESSIONKEY_INVALID: "livevideo.sessionkey.invalid";
106
+ };
107
+ /**
108
+ * Union type of all {@link (LiveVideoStatusCode:variable)} values.
109
+ *
110
+ * @public
111
+ */
112
+ type LiveVideoStatusCode = ValueOf<typeof LiveVideoStatusCode>;
113
+ //#endregion
114
+ //#region src/init/InitSegmentValidation.d.ts
115
+ /**
116
+ * A session key extracted and verified from a C2PA `c2pa.session-keys` assertion.
117
+ *
118
+ * Only keys whose signer binding is valid and whose validity period has not expired
119
+ * are included in {@link InitSegmentValidation.sessionKeys}.
120
+ *
121
+ * @public
122
+ */
123
+ type ValidatedSessionKey = {
124
+ readonly kid: string;
125
+ readonly jwk: CoseKeyJwk;
126
+ readonly minSequenceNumber: number;
127
+ readonly validityPeriod: number;
128
+ readonly createdAt: string;
129
+ };
130
+ /**
131
+ * Result of validating a C2PA init segment.
132
+ *
133
+ * Returned by {@link validateC2paInitSegment}.
134
+ *
135
+ * @public
136
+ */
137
+ type InitSegmentValidation = {
138
+ readonly manifest: C2paManifest | null;
139
+ readonly certificate: Uint8Array | null;
140
+ readonly manifestId: string | null;
141
+ readonly sessionKeys: readonly ValidatedSessionKey[];
142
+ readonly isValid: boolean;
143
+ readonly errorCodes: readonly (LiveVideoStatusCode | C2paStatusCode)[];
144
+ };
145
+ //#endregion
146
+ //#region src/init/validateC2paInitSegment.d.ts
147
+ /**
148
+ * Validates a C2PA init segment: parses the manifest, extracts and verifies
149
+ * the certificate, validates the BMFF hard binding hash, verifies all
150
+ * session keys from the `c2pa.session-keys` assertion, and performs
151
+ * manifest integrity checks (assertion hashes, missing assertions,
152
+ * action ingredients, and claim signature verification).
153
+ *
154
+ * Only session keys with a valid signer binding and an unexpired validity period
155
+ * are included in the result.
156
+ *
157
+ * @param bytes - Raw init segment bytes
158
+ * @returns Structured validation result (with `INIT_INVALID` error code if `mdat` box is present)
159
+ * @throws If no C2PA UUID box is found
160
+ *
161
+ * @example
162
+ * {@includeCode ../../test/init/validateC2paInitSegment.test.ts#example}
163
+ *
164
+ * @public
165
+ */
166
+ declare function validateC2paInitSegment(bytes: Uint8Array): Promise<InitSegmentValidation>;
167
+ //#endregion
168
+ //#region src/vsi/SequenceState.d.ts
169
+ /**
170
+ * State and result types for monotonic sequence number validation
171
+ * per C2PA Live Streaming Specification §18.4.
172
+ *
173
+ * @public
174
+ */
175
+ /**
176
+ * Immutable state snapshot for a single stream's sequence number history.
177
+ *
178
+ * Pass to {@link validateC2paSegment} via the `sequenceState` parameter.
179
+ *
180
+ * @public
181
+ */
182
+ type SequenceState = {
183
+ readonly lastSequenceNumber: number | null;
184
+ readonly seenSequences: ReadonlySet<number>;
185
+ };
186
+ /**
187
+ * Reason codes for sequence number validation outcomes.
188
+ *
189
+ * @see {@link SequenceValidationResult}
190
+ *
191
+ * @enum
192
+ *
193
+ * @public
194
+ */
195
+ declare const SequenceValidationReason: {
196
+ /** Sequence number is the next expected value */
197
+ readonly VALID: "valid";
198
+ /** Sequence number was already seen */
199
+ readonly DUPLICATE: "duplicate";
200
+ /** One or more sequence numbers were skipped */
201
+ readonly GAP_DETECTED: "gap_detected";
202
+ /** Sequence number is less than the last seen */
203
+ readonly OUT_OF_ORDER: "out_of_order";
204
+ /** Below the session key's minSequenceNumber */
205
+ readonly SEQUENCE_NUMBER_BELOW_MINIMUM: "sequence_number_below_minimum";
206
+ };
207
+ /**
208
+ * Union type of all {@link (SequenceValidationReason:variable)} values.
209
+ *
210
+ * @public
211
+ */
212
+ type SequenceValidationReason = ValueOf<typeof SequenceValidationReason>;
213
+ /**
214
+ * Result of validating a single sequence number against the current stream state.
215
+ *
216
+ * Discriminated on `reason` — narrow to `'gap_detected'` to access
217
+ * `missingFrom` / `missingTo`.
218
+ *
219
+ * @public
220
+ */
221
+ type SequenceValidationResult = {
222
+ readonly isValid: true;
223
+ readonly reason: typeof SequenceValidationReason.VALID;
224
+ } | {
225
+ readonly isValid: false;
226
+ readonly reason: typeof SequenceValidationReason.SEQUENCE_NUMBER_BELOW_MINIMUM | typeof SequenceValidationReason.DUPLICATE | typeof SequenceValidationReason.OUT_OF_ORDER;
227
+ } | {
228
+ readonly isValid: false;
229
+ readonly reason: typeof SequenceValidationReason.GAP_DETECTED;
230
+ readonly missingFrom: number;
231
+ readonly missingTo: number;
232
+ };
233
+ //#endregion
234
+ //#region src/segment/SegmentValidation.d.ts
235
+ /**
236
+ * The result of validating a single C2PA live stream segment (VSI/EMSG method).
237
+ *
238
+ * Returned by {@link validateC2paSegment}.
239
+ *
240
+ * @public
241
+ */
242
+ type SegmentValidationResult = {
243
+ readonly sequenceNumber: number;
244
+ readonly manifestId: string;
245
+ readonly bmffHashHex: string | null;
246
+ readonly kidHex: string | null;
247
+ readonly sequenceResult: SequenceValidationResult;
248
+ readonly isValid: boolean;
249
+ readonly errorCodes: readonly LiveVideoStatusCode[];
250
+ };
251
+ //#endregion
252
+ //#region src/segment/validateC2paSegment.d.ts
253
+ /**
254
+ * Validates a C2PA live stream segment using the VSI/EMSG method (§19.7.3).
255
+ *
256
+ * Extracts the EMSG box, decodes the COSE_Sign1 and VSI map, matches the
257
+ * session key by kid, then performs all cryptographic checks: signature
258
+ * verification, BMFF content hash, sequence number floor, and key validity.
259
+ *
260
+ * Returns `null` if the segment does not contain a C2PA EMSG box.
261
+ *
262
+ * @param segmentBytes - Raw segment bytes
263
+ * @param sessionKeys - Available session keys from the init segment
264
+ * @param sequenceState - Current sequence state for this stream
265
+ * @returns Validation result and updated sequence state, or `null` if no C2PA EMSG box
266
+ *
267
+ * @example
268
+ * {@includeCode ../../test/segment/validateC2paSegment.test.ts#example}
269
+ *
270
+ * @public
271
+ */
272
+ declare function validateC2paSegment(segmentBytes: Uint8Array, sessionKeys: readonly ValidatedSessionKey[], sequenceState?: SequenceState): Promise<{
273
+ readonly result: SegmentValidationResult;
274
+ readonly nextSequenceState: SequenceState;
275
+ } | null>;
276
+ //#endregion
277
+ //#region src/manifestbox/ManifestBoxValidation.d.ts
278
+ /**
279
+ * The result of validating a single C2PA manifest-box live stream segment.
280
+ *
281
+ * Returned by {@link validateC2paManifestBoxSegment}.
282
+ *
283
+ * @public
284
+ */
285
+ type ManifestBoxValidationResult = {
286
+ readonly manifest: C2paManifest | null;
287
+ readonly issuer: string | null;
288
+ readonly sequenceNumber: number | null;
289
+ readonly previousManifestId: string | null;
290
+ readonly streamId: string | null;
291
+ readonly continuityMethod: string | null;
292
+ readonly bmffHashHex: string | null;
293
+ readonly isValid: boolean;
294
+ readonly errorCodes: readonly (LiveVideoStatusCode | C2paStatusCode)[];
295
+ };
296
+ /**
297
+ * State to carry between consecutive manifest-box segment validations.
298
+ *
299
+ * Pass the `nextState` returned by {@link validateC2paManifestBoxSegment}
300
+ * into the next call to enable streamId, sequenceNumber, and continuity checks.
301
+ *
302
+ * @public
303
+ */
304
+ type ManifestBoxValidationState = {
305
+ readonly lastStreamId?: string | null;
306
+ readonly lastSequenceNumber?: number | null;
307
+ };
308
+ //#endregion
309
+ //#region src/manifestbox/validateC2paManifestBoxSegment.d.ts
310
+ /**
311
+ * Validates a C2PA manifest-box live stream segment.
312
+ *
313
+ * Parses the C2PA manifest embedded in the segment and validates per §19.7.1 and §19.7.2.
314
+ * Recomputes the `c2pa.hash.bmff.v3` content hash from the raw segment bytes and compares
315
+ * it against the expected hash in the manifest assertion. Checks live-video assertions
316
+ * (sequenceNumber, streamId, continuityMethod) and manifest-ID chain continuity.
317
+ *
318
+ * This function is **pure** — it does not access any external state. The
319
+ * caller is responsible for persisting `nextManifestId` and `nextState`
320
+ * between calls.
321
+ *
322
+ * @param bytes - Raw segment bytes
323
+ * @param lastManifestId - Manifest ID from the previous segment, or null for the first segment
324
+ * @param state - Optional state from the previous segment for streamId/sequenceNumber checks
325
+ * @returns Validation result, the manifest ID, and state to persist for the next call
326
+ *
327
+ * @example
328
+ * {@includeCode ../../test/manifestbox/validateC2paManifestBoxSegment.test.ts#example}
329
+ *
330
+ * @public
331
+ */
332
+ declare function validateC2paManifestBoxSegment(bytes: Uint8Array, lastManifestId: string | null, state?: ManifestBoxValidationState): Promise<{
333
+ readonly result: ManifestBoxValidationResult;
334
+ readonly nextManifestId: string | null;
335
+ readonly nextState: ManifestBoxValidationState;
336
+ }>;
337
+ //#endregion
338
+ export { C2paAssertion, C2paManifest, C2paSignatureInfo, C2paStatusCode, CoseKeyJwk, InitSegmentValidation, LiveVideoStatusCode, ManifestBoxValidationResult, ManifestBoxValidationState, SegmentValidationResult, type SequenceState, SequenceValidationReason, type SequenceValidationResult, ValidatedSessionKey, validateC2paInitSegment, validateC2paManifestBoxSegment, validateC2paSegment };
339
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/C2paAssertion.ts","../src/C2paSignatureInfo.ts","../src/C2paManifest.ts","../src/C2paStatusCode.ts","../src/cose/CoseKeyJwk.ts","../src/LiveVideoStatusCode.ts","../src/init/InitSegmentValidation.ts","../src/init/validateC2paInitSegment.ts","../src/vsi/SequenceState.ts","../src/segment/SegmentValidation.ts","../src/segment/validateC2paSegment.ts","../src/manifestbox/ManifestBoxValidation.ts","../src/manifestbox/validateC2paManifestBoxSegment.ts"],"sourcesContent":[],"mappings":"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"}