c2pa-vtt 0.2.0

C2PA manifest embedding and hard binding for WebVTT subtitle and caption files
Documentation

Overview

Embeds, extracts, and hard-binds a C2PA Manifest Store reference in WebVTT files. The manifest is carried in a single-line NOTE comment using the fixed ASCII armour delimiters, placed immediately after the WEBVTT signature (where it survives HLS/DASH segmentation), per the C2PA structured text embedding rules.

Canonical owner of WebVTT. WebVTT is structured text per the specification, so the general c2pa-structured-text crate could embed into it via the NOTE comment style — but this crate owns .vtt. It targets the streaming-safe placement after the WEBVTT signature and validates the header. Use this crate for WebVTT; use c2pa-structured-text for other structured text.

WEBVTT

NOTE -----BEGIN C2PA MANIFEST----- https://example.com/m.c2pa -----END C2PA MANIFEST-----

00:00:00.000 --> 00:00:05.000
Hello world

Scope: this crate vs. c2pa-structured-text

The C2PA structured text embedding section covers WebVTT as one comment style (NOTE), and c2pa-structured-text implements that general method. This crate is the canonical WebVTT implementation; c2pa-structured-text documents the WebVTT delimiter but defers placement and hard binding here. WebVTT needs format awareness that the general method lacks:

  • Placement. The general "prepend a comment line" rule would put the block before the WEBVTT signature and produce an invalid file. WebVTT reserves its first line, so the block goes immediately after it, with blank-line separation.
  • Hard binding. Computing and validating the c2pa.hash.data exclusion range requires locating the NOTE block within valid WebVTT structure (cues, STYLE/REGION blocks, author comments).

There is no silently-overlapping second implementation: c2pa-structured-text points to this crate for WebVTT.

Hard binding

WebVTT is a structured text container, so its hard binding is a byte-exact c2pa.hash.data data hash carrying a single exclusion range that covers the manifest NOTE block. The hash is computed over the raw bytes of the file with that one range removed: the WEBVTT signature, every cue, STYLE/REGION block, and author comment is bound; only the manifest block itself is excluded.

  • No normalization. Unlike the unstructured-text (Unicode Variation Selector) method, structured text hashing applies no Unicode normalization. The file is byte-stable on disk and the ASCII delimiters make the excluded range unambiguous; applying NFC would create false mismatches for legitimate NFD content in cue text.
  • Line terminators. LF or CRLF only; the file's convention is preserved and bare CR is not supported (per the spec).
  • Exclusion range. [offset, offset + length) of the NOTE line including its trailing terminator — exactly the value returned by [data_hash_exclusion] and the offset/length on [extract_manifest].

A byte-exact hard binding is therefore feasible and implemented here. It is fragile under re-encoding that changes bytes outside the manifest block (line-ending conversion, BOM insertion, trailing-whitespace edits) — inherent to any hard binding over a text container. For robustness against such transformations, pair it with a soft binding from c2pa-text-binding.

Quick Start

[dependencies]
c2pa-vtt = "0.2"

Generate: embed a reference and compute the hard binding

use c2pa_vtt::{embed_manifest, compute_data_hash, ManifestRef, HashAlg};

let vtt = "WEBVTT\n\n00:00:00.000 --> 00:00:05.000\nHello\n";
let signed = embed_manifest(vtt, ManifestRef::Url("https://example.com/m.c2pa")).unwrap();

// The value for the c2pa.hash.data assertion's `hash` field (alg = HashAlg::c2pa_id).
let hash = compute_data_hash(&signed, HashAlg::Sha256).unwrap();

A manifest store may instead be embedded inline with ManifestRef::Embedded(&store_bytes), encoded as a data:application/c2pa;base64,... reference.

Verify: extract and check the hard binding

use c2pa_vtt::{extract_manifest, verify_data_hash, HashAlg};

let result = extract_manifest(&signed).unwrap();
assert_eq!(result.reference, "https://example.com/m.c2pa");

let ok = verify_data_hash(&signed, HashAlg::Sha256, &hash).unwrap();

Remove

use c2pa_vtt::remove_manifest;

let clean = remove_manifest(&signed).unwrap();

Validation bridge

Hard-binding validation is owned by this crate (c2pa-rs has no stable native text/vtt handler). Everything else — COSE signature, certificate trust, timestamp, and assertion validation of the manifest store — is delegated to c2pa-rs.

extract_manifest_source (zero dependencies) resolves the reference, decoding an inline data: URI to raw store bytes:

use c2pa_vtt::{extract_manifest_source, ManifestSource};

match extract_manifest_source(&signed).unwrap() {
    ManifestSource::Url(url) => { /* fetch the store; this crate performs no network I/O */ }
    ManifestSource::Embedded(bytes) => { /* raw C2PA Manifest Store */ }
}

With the c2pa feature enabled, bridge::validate(vtt, &store_bytes) hands the store to a c2pa-rs Reader; inspect validation_state() / validation_status(), and validate the WebVTT hard binding with verify_data_hash.

Features

Feature Default Adds
hash sha2-backed compute_data_hash / verify_data_hash
c2pa bridge::validate delegation to c2pa-rs (pulls in the c2pa crate)

Build with default-features = false for a zero-dependency embed/extract build.

Conformance

This crate implements the structured text embedding and data hash for WebVTT as specified, and delegates signature/trust validation to c2pa-rs. It makes no conformance or certification claim; validate against the C2PA specification and a reference C2PA tool for interop.

Related Crates

Part of a family of single-purpose crates, one per C2PA embedding method. Each is standalone and independently versioned.

Crate Description
c2pa-structured-text Structured text: ASCII-armoured manifest in a comment or front matter
c2pa-unstructured-text Unstructured text: invisible Unicode variation-selector run
c2pa-html HTML: script and link elements in the document head
c2pa-http HTTP: the c2pa-manifest Link header, with a Tower middleware
c2pa-text-binding Soft binding and content fingerprinting for text assets
c2pa-zip ZIP-based documents: EPUB, DOCX, ODT, OXPS
c2pa-warc WARC web archive embedding (ISO 28500)
c2pa-fonts OpenType/TrueType (SFNT) font embedding
c2pa-ml ML model containers: GGUF, SafeTensors, ONNX
c2pa Official C2PA SDK

Security

Found a vulnerability? Please report it privately — see SECURITY.md.

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Built by WritersLogic