Skip to main content

C2PA manifest embedding, referencing, and hard binding for HTML documents

Project description

crates.io docs.rs CI OpenSSF Scorecard License

Overview

Implements the Embedding Manifests into HTML section of the C2PA Technical Specification: a C2PA Manifest Store carried inline as the Base64 content of a <script type="application/c2pa"> element, or referenced externally by a <link rel="c2pa-manifest"> element, both in the document head.

<head>
    <script type="application/c2pa">...Base64 Manifest Store...</script>
</head>
[dependencies]
c2pa-html = "0.1"

HTML has its own embedding clause, so the structured-text method explicitly does not apply to it — that method covers text/* formats "not already covered by a format-specific embedding section".

This crate owns two things:

  1. The association — discover, embed, reference, and remove the script or link element.
  2. The hard binding — the exact c2pa.hash.data coverage for HTML, with compute and verify.

Signature verification, certificate trust, assertion validation, and resolution of an external manifest URI are not reimplemented here.

Not certified or conformance-tested by the C2PA. It implements the embedding and hard binding as specified.

What it does

document discover, embed, reference, and remove a manifest association
hardbinding the exact c2pa.hash.data coverage, with compute and verify

Signature verification, certificate trust, assertion validation, and resolution of an external manifest URI are out of scope.

Reference an external manifest

The specification prefers this form.

use c2pa_html::document;

let page = b"<html>\n<head>\n    <title>Example</title>\n</head>\n<body></body>\n</html>";
let out = document::embed_reference(page, "https://fabrikam.example/m.c2pa")?;

let manifest = document::extract(&out)?;
assert_eq!(manifest.href(), Some("https://fabrikam.example/m.c2pa"));
# Ok::<(), c2pa_html::Error>(())

Carry the manifest inline

use c2pa_html::document;

let page = b"<html>\n<head>\n    <title>Example</title>\n</head>\n<body></body>\n</html>";
let out = document::embed(page, b"manifest-store-bytes")?;

assert_eq!(
    document::extract(&out)?.store(),
    Some(&b"manifest-store-bytes"[..])
);
// Removing the manifest restores the document byte for byte.
assert_eq!(document::remove(&out)?, page);
# Ok::<(), c2pa_html::Error>(())

Bind the document

use c2pa_html::document;
use c2pa_html::hardbinding::{compute_data_hash, verify_data_hash, Algorithm, Sha2};

let page = b"<html>\n<head>\n    <title>Example</title>\n</head>\n<body></body>\n</html>";
let out = document::embed(page, b"manifest-store-bytes")?;

let binding = compute_data_hash(&out, Algorithm::Sha256, &Sha2)?;
assert!(verify_data_hash(&out, &binding, &Sha2).is_ok());
# Ok::<(), c2pa_html::Error>(())

Binding coverage

  • Inline — one exclusion range covering the entire script element, <script through </script> inclusive. The hash is over the document with that range removed.
  • External — no exclusion range at all. The hash is over the entire document, link element included, so the manifest URI cannot be swapped without breaking the binding.

The hash is over the bytes of the document as stored, with no normalization. Anything that re-serializes the HTML — a CMS, a CDN, a formatter that rewrites quote styles or collapses whitespace — shifts byte offsets and invalidates the binding. That is by design: re-serialization is a content modification. Embed after the final serialization step, or use an external manifest.

The inline hash can be computed before the manifest exists

Because the exclusion covers the entire script element, the covered bytes are the document with the element cut out — and embed adds no bytes outside the element, so that is the original document. Signing an inline HTML manifest needs no placeholder-reserve-then-fill dance: hash, sign, embed.

use c2pa_html::hardbinding::inline_hash_before_embed;

An external manifest has no exclusion, so the link element is inside the hash and the order reverses: insert the link first, then hash.

Discovery

Scoped to the document head, byte-oriented, and tolerant of how HTML is actually written:

  • all three attribute quoting forms — rel="x", rel='x', rel=x
  • ASCII case-insensitive tag names, attribute names, type, and rel
  • rel as a space-separated token list, per HTML
  • link matched on rel alone; type="application/c2pa" is optional
  • comments, doctypes, and the contents of other script and style elements are never mistaken for markup
  • an implied head (both tags omitted) is still searched

A document shall carry at most one association. Two script elements, two link elements, or one of each are all rejected with the specified manifest.html.multipleManifests failure — the manifests were located, and the document is rejected for carrying too many.

Finding nothing carries no status code (it is simply an unsigned document), and neither does an element that matches but yields no Manifest Store — the specification names no code for that, and the fail-safe reading is that nothing was obtained. Error::is_no_manifest_located() draws the line a caller actually needs: "carries no provenance" versus "carried provenance that was rejected".

Bytes, not text

The specification directs a validator to treat the document "as a series of bytes (vs. text)", so every entry point takes &[u8] and returns Vec<u8>. A document in a legacy ASCII-compatible encoding scans correctly, and byte offsets mean what the hard binding says they mean.

Zero dependencies, no features

Discovery, embedding, Base64, SHA-2, and the binding algorithm are all in-crate. There is nothing to enable and nothing to pull in — the dependency list is empty in every configuration.

SHA-256/384/512 are implemented against FIPS 180-4 and tested against the NIST vectors. A hash is the one primitive where writing it yourself is uncontroversial: fully specified, no key, public input, no timing side channel, and published vectors that pin every path.

What that gives up is speed — no SHA-NI, no NEON. Immaterial for an HTML document; it would not be for a multi-gigabyte asset, which is why hashing goes through the Hasher trait. A caller with that problem injects an accelerated implementation and never touches the built-in one.

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-http HTTP: the c2pa-manifest Link header, with a Tower middleware
c2pa-text-binding Soft binding and content fingerprinting for text assets
c2pa-vtt WebVTT caption and subtitle embedding
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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

c2pa_html-0.2.0.tar.gz (51.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

c2pa_html-0.2.0-cp39-abi3-win_amd64.whl (137.1 kB view details)

Uploaded CPython 3.9+Windows x86-64

c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (272.7 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (268.0 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

c2pa_html-0.2.0-cp39-abi3-macosx_11_0_arm64.whl (239.6 kB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

c2pa_html-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl (243.2 kB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file c2pa_html-0.2.0.tar.gz.

File metadata

  • Download URL: c2pa_html-0.2.0.tar.gz
  • Upload date:
  • Size: 51.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for c2pa_html-0.2.0.tar.gz
Algorithm Hash digest
SHA256 eda00475b0afd5e734b2cd0245de583e3821bbd6d32af891976a3478ec968799
MD5 4098af53545e66c922f50d9f764a3c63
BLAKE2b-256 146cf29cb5e558135b80aa6cefb37dc1d4954d673a1d5be1b1708b4d008990c6

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0.tar.gz:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_html-0.2.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: c2pa_html-0.2.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 137.1 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for c2pa_html-0.2.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 f0502af10bd4e0e7e568e8c14b8d46466fd8f1223ebe260d0bd652abe7f3d021
MD5 cb602773209d0cea34f116ad5654015e
BLAKE2b-256 e4cb657d3f008c8dd8c864d0826ca1ec7b04051508052cd536fbcfeec38dbfcb

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0-cp39-abi3-win_amd64.whl:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4c8941a91faccec4ecadb665d033f3296ee8e5021ad3088e4f0841ae323e822c
MD5 fa586d7670b42f5af1cddf83f61b259e
BLAKE2b-256 69b5552c78d3511f9c433d23e1a197e814807d059e31bf214bacf3855230b49d

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 cde1172f58d957a923d8cd4ef4da8527dd0c34808a2b1e677c861a0ce183107f
MD5 e1273e512c95c463156f3b7569c2cb3e
BLAKE2b-256 57b229fd3775ec9d59285a8db04d5d45cd335f47e163cbad19941c1d4cd90284

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_html-0.2.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for c2pa_html-0.2.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5d8bb326fdcbde8757adc54432f971e4fc974173b4413c5a0f0cc3b98a7c5abd
MD5 1e0a28da35a9c88218b7fccbd5a1a8b2
BLAKE2b-256 caaae8a5cef555e7ecfaa4a35046a40ea846e8caf6b9b9be9e986f6c9679fb41

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file c2pa_html-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for c2pa_html-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 928977754e46c226ea6a41bcb6c506af3d8fc281f1bea2770ffe20217c23a2bd
MD5 8f3443453320481ceda5526314a33ea3
BLAKE2b-256 9c5747fed66ef2167c24d335f00ab3aef215187b8e276fdd8e54fa8520f673ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for c2pa_html-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on writerslogic/c2pa-html

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page