C2PA manifest embedding, referencing, and hard binding for HTML documents
Project description
c2pa-html
C2PA manifest embedding, referencing, and hard binding for HTML documents.
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.
[dependencies]
c2pa-html = "0.1"
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
scriptelement,<scriptthrough</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,
linkelement 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, andrel relas a space-separated token list, per HTMLlinkmatched onrelalone;type="application/c2pa"is optional- comments, doctypes, and the contents of other
scriptandstyleelements 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
| crate | method |
|---|---|
c2pa-structured-text |
structured text: ASCII-armoured manifest in a comment |
c2pa-unstructured-text |
unstructured text: Unicode variation selectors |
HTML is a file format, so its binding hashes stored bytes with no normalization. The text methods differ deliberately: structured text also hashes raw bytes but has no element to exclude, and unstructured text normalizes to NFC because clipboard-portable text may arrive in any normalization form.
License
MIT OR Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file c2pa_html-0.1.0.tar.gz.
File metadata
- Download URL: c2pa_html-0.1.0.tar.gz
- Upload date:
- Size: 43.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1964afbc9d2d2899f90eb32ec71cc509b1437cd7572e0f0ee6a40088e5213284
|
|
| MD5 |
ef6f38927ea9370e9bc366afd2fa5cc6
|
|
| BLAKE2b-256 |
ad1681d8376660d4a73fe001baa30c05472750f3de2d8b0474193395fe7ca9e1
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0.tar.gz:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0.tar.gz -
Subject digest:
1964afbc9d2d2899f90eb32ec71cc509b1437cd7572e0f0ee6a40088e5213284 - Sigstore transparency entry: 2305479984
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type:
File details
Details for the file c2pa_html-0.1.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: c2pa_html-0.1.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 136.3 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aafc0fdf773ad9096e63d24bd8d5c7102c45200d66bf87c12d6534e8d8d31bbf
|
|
| MD5 |
1dfb3fe84dfc536da0448fd66f859247
|
|
| BLAKE2b-256 |
d37d97aafa3314e91ca97973ce5c9fd484fba784e6f60fb8449a75fad0c7755b
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0-cp39-abi3-win_amd64.whl -
Subject digest:
aafc0fdf773ad9096e63d24bd8d5c7102c45200d66bf87c12d6534e8d8d31bbf - Sigstore transparency entry: 2305480332
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type:
File details
Details for the file c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 271.9 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1efe08c5a06f040785d05df232b06d621b74d8964dbfd1e96c8e0f3e9c4b8ae8
|
|
| MD5 |
ac8c3819b1d6d2b325f72dcb2d37724b
|
|
| BLAKE2b-256 |
3918c635ad517e18d224bf48605467fb6303df434dc1eec345f0d21281447d93
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
1efe08c5a06f040785d05df232b06d621b74d8964dbfd1e96c8e0f3e9c4b8ae8 - Sigstore transparency entry: 2305480222
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type:
File details
Details for the file c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 267.1 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
191f01de351c75ce19689843611a2bceeba60c02836c170dab5e3d967d1ac3a8
|
|
| MD5 |
97b0a21a3779e4a2e31f5ed6f76675f5
|
|
| BLAKE2b-256 |
8e317daaaa0d58cdbdc10084e7bcac023e576c2a5286fe620fffbfe20a6da5f9
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
191f01de351c75ce19689843611a2bceeba60c02836c170dab5e3d967d1ac3a8 - Sigstore transparency entry: 2305480116
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type:
File details
Details for the file c2pa_html-0.1.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: c2pa_html-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 238.8 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d6c1a7877dd3aed784c32664dce3ffba22938fa5ab8544a739bde6b16540b78
|
|
| MD5 |
3caaed170cce7795c5206a8b5e541f60
|
|
| BLAKE2b-256 |
82e31c432ba277f19dd0a21cd70064851fb2f15eb2e5e44975479e1598c58f48
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
6d6c1a7877dd3aed784c32664dce3ffba22938fa5ab8544a739bde6b16540b78 - Sigstore transparency entry: 2305480523
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type:
File details
Details for the file c2pa_html-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: c2pa_html-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 242.4 kB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cd41fab3641c826c14d4f39de7bedda080e7ec8a163412e74797eb7c4d5546c
|
|
| MD5 |
0ddf609723f65dc4e23dd8910e721a60
|
|
| BLAKE2b-256 |
cd2b0d0f9767756fb7d032296a1661aba864ceb1067f1302fc8886213c82b84c
|
Provenance
The following attestation bundles were made for c2pa_html-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on writerslogic/c2pa-html
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
c2pa_html-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
1cd41fab3641c826c14d4f39de7bedda080e7ec8a163412e74797eb7c4d5546c - Sigstore transparency entry: 2305480412
- Sigstore integration time:
-
Permalink:
writerslogic/c2pa-html@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/writerslogic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4da0ea2e767f0e4eff91b641c167fd23ae2d1bff -
Trigger Event:
push
-
Statement type: