Metadata-Version: 2.4
Name: c2pa-structured-text
Version: 0.3.0
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Classifier: License :: OSI Approved :: MIT License
Classifier: License :: OSI Approved :: Apache Software License
License-File: LICENSE-APACHE
License-File: LICENSE-MIT
Summary: C2PA manifest embedding, hard binding, and validation for structured text formats
Keywords: c2pa,provenance,text,content-credentials
Author-email: WritersLogic <hello@writerslogic.com>
License: MIT OR Apache-2.0
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Repository, https://github.com/writerslogic/c2pa-structured-text

<p align="center">
  <a href="https://crates.io/crates/c2pa-structured-text"><img src="https://img.shields.io/crates/v/c2pa-structured-text.svg" alt="crates.io"></a>
  <a href="https://docs.rs/c2pa-structured-text"><img src="https://docs.rs/c2pa-structured-text/badge.svg" alt="docs.rs"></a>
  <a href="https://github.com/writerslogic/c2pa-structured-text/actions/workflows/ci.yml"><img src="https://github.com/writerslogic/c2pa-structured-text/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://scorecard.dev/viewer/?uri=github.com/writerslogic/c2pa-structured-text"><img src="https://api.securityscorecards.dev/projects/github.com/writerslogic/c2pa-structured-text/badge" alt="OpenSSF Scorecard"></a>
  <a href="#license"><img src="https://img.shields.io/crates/l/c2pa-structured-text.svg" alt="License"></a>
</p>

## Overview

Implements the **Embedding Manifests into Structured Text** section of the [C2PA Technical Specification](https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html#_embedding_manifests_into_structured_text), which associates a C2PA Manifest Store with source code, configuration files, markup, and other text formats that support comment syntax or front matter conventions.

The manifest block uses fixed ASCII armour-style delimiters modelled on [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880#section-6.2):

```
-----BEGIN C2PA MANIFEST----- <reference> -----END C2PA MANIFEST-----
```

This crate owns three things:

1. **Embed / Extract** — place a reference or an inline manifest as a comment or front matter block, and locate and resolve it again.
2. **Hard binding** — define and compute the exact `c2pa.hash.data` coverage for structured text, and verify it.
3. **A validation bridge** to [`c2pa-rs`](https://crates.io/crates/c2pa) for signature, trust, and assertion validation — which this crate does *not* reimplement.

> This crate is not certified or conformance-tested by the C2PA. It implements the structured-text embedding and hard binding as specified, and delegates cryptographic validation to `c2pa-rs`.

## Quick Start

```toml
[dependencies]
c2pa-structured-text = "0.1"
```

### Embed a manifest reference

```rust
use c2pa_structured_text::{embed_manifest, ManifestRef};

let signed = embed_manifest(
    "print('hello')\n",
    ManifestRef::Url("https://example.com/manifests/abc.c2pa"),
    "#",   // comment prefix
    None,  // no comment suffix
);
// # -----BEGIN C2PA MANIFEST----- https://example.com/manifests/abc.c2pa -----END C2PA MANIFEST-----
// print('hello')
```

`embed_manifest_at_end` places the block on the last line (for files whose first line is reserved, e.g. a shebang or XML declaration), and `embed_front_matter` writes the multi-line form inside YAML/TOML front matter.

### Extract a manifest reference

```rust
use c2pa_structured_text::{extract_manifest, classify_reference, Reference};

let text = "# -----BEGIN C2PA MANIFEST----- https://example.com/m.c2pa -----END C2PA MANIFEST-----
print('hello')
";
let result = extract_manifest(text).unwrap();
assert_eq!(result.reference, "https://example.com/m.c2pa");

// A `data:application/c2pa;base64,` reference decodes to the manifest bytes;
// anything else is treated as an external URI.
match classify_reference(&result.reference).unwrap() {
    Reference::Url(url) => { /* fetch it */ }
    Reference::Embedded(bytes) => { /* raw JUMBF manifest store */ }
}
```

## The Hard Binding

A structured-text manifest is bound with a `c2pa.hash.data` assertion carrying a **single exclusion range covering the entire manifest block**. The hash is computed over the **raw bytes** of the file with that range removed.

Unlike the Unicode Variation Selector method for *unstructured* text, this binding applies **no Unicode normalization**: structured text files are byte-stable on disk, and normalizing to NFC would create false mismatches for files that legitimately contain NFD content. Files must be read in binary mode, preserving exact line terminators; bare CR line endings are unsupported.

```rust
# #[cfg(feature = "hard-binding")] {
use c2pa_structured_text::hardbinding::{compute_data_hash, verify_data_hash, Algorithm};

let signed = c2pa_structured_text::embed_manifest(
    "print('hello')\n",
    c2pa_structured_text::ManifestRef::Url("https://example.com/m.c2pa"),
    "#",
    None,
);
let data_hash = compute_data_hash(&signed, Algorithm::Sha256).unwrap();
verify_data_hash(&signed, &data_hash).unwrap();
# }
```

The exclusion-range and covered-byte primitives (`manifest_exclusion`, `hashed_bytes`) are always available and dependency-free; `compute_data_hash` / `verify_data_hash` require the `hard-binding` feature (which pulls `sha2`).

### Fragility — and the soft-binding recovery path

This is a **byte-exact** binding, and it is meant to be. Any change to the covered bytes — reformatting, re-indentation, transcoding, or an LF↔CRLF conversion outside the block — breaks it. Where durability across such transformations matters, pair it with the perceptual soft binding in [c2pa-text-binding](https://github.com/writerslogic/c2pa-text-binding), which re-associates transformed content with its provenance after the hard binding is lost. Do not treat the structured-text hard binding as robust to editing.

## Validating with c2pa-rs

Enable the `c2pa` feature to validate the signature, trust chain, and hard binding via `c2pa-rs`. This crate extracts and resolves the reference; `c2pa-rs` does the cryptography.

```rust,ignore
use c2pa_structured_text::bridge;

// Inline (data:) references are decoded automatically; URL references are
// fetched with the `remote` feature (or resolve them yourself and call
// `bridge::validate_with_manifest`).
let reader = bridge::validate(&signed, bridge::DEFAULT_FORMAT)?;
println!("{:?}", reader.validation_state());
```

## Features

| Feature | Adds | Pulls |
|---|---|---|
| *(none)* | embed, extract, exclusion-range and covered-byte primitives | — |
| `hard-binding` | `compute_data_hash` / `verify_data_hash` (SHA2-256/384/512) | `sha2` |
| `c2pa` | the `bridge` to `c2pa-rs` for signature/trust/assertion validation | `c2pa` |
| `remote` | HTTP(S) resolution of URL references in the bridge | `c2pa`, `ureq` |

No feature is enabled by default; the core API has no dependencies.

## Supported Formats

Any text format with a comment syntax or front matter convention:

| Comment Style | Formats | Example |
|---|---|---|
| `#` | Python, Ruby, Shell, YAML, TOML | `# -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
| `//` | JavaScript, TypeScript, Go, Rust, C++ | `// -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
| `--` | SQL, Lua, Haskell | `-- -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
| `/* */` | CSS, C, Java | `/* -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST----- */` |
| `<!-- -->` | Markdown, XML (non-HTML) | `<!-- -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST----- -->` |
| Front matter | Markdown (YAML), TOML | Multi-line form between front matter delimiters |

The crate is **format-agnostic**: it does not hard-code a fixed list of languages. Any `text/*` asset with a comment introducer or a front matter convention works — you supply the comment prefix/suffix (or front matter fence). The table above is illustrative, not exhaustive.

### Applicability and exclusions

Per the specification, the structured-text method applies to any `text/*` (or plain-text) asset **not** already covered by a format-specific embedding method, provided it has a comment syntax or front matter. The following are **out of scope** and will not round-trip through this crate:

| Not supported | Why | Use instead |
|---|---|---|
| JSON, CSV | No comment or front matter syntax — nothing to carry the block | none (embed in a container) |
| HTML | Has its own C2PA embedding method | the HTML embedding method |
| SVG, TTML | Have their own C2PA embedding methods | the SVG / TTML methods |
| WebVTT | Structured text, but streaming placement is specialised | [c2pa-vtt](https://github.com/writerslogic/c2pa-vtt) |
| Unstructured/plain prose | No stable comment location; use invisible codepoints | [c2pa-text](https://crates.io/crates/c2pa-text) |

Line endings must be LF or CRLF (bare CR is rejected). When structured text is carried inside a container (MP4, PDF, ZIP), prefer embedding in the container.

## Related Crates

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

| Crate | Description |
|---|---|
| [c2pa-unstructured-text](https://crates.io/crates/c2pa-unstructured-text) | Unstructured text: invisible Unicode variation-selector run |
| [c2pa-html](https://crates.io/crates/c2pa-html) | HTML: `script` and `link` elements in the document head |
| [c2pa-http](https://crates.io/crates/c2pa-http) | HTTP: the `c2pa-manifest` `Link` header, with a Tower middleware |
| [c2pa-text-binding](https://crates.io/crates/c2pa-text-binding) | Soft binding and content fingerprinting for text assets |
| [c2pa-vtt](https://crates.io/crates/c2pa-vtt) | WebVTT caption and subtitle embedding |
| [c2pa-zip](https://crates.io/crates/c2pa-zip) | ZIP-based documents: EPUB, DOCX, ODT, OXPS |
| [c2pa-warc](https://crates.io/crates/c2pa-warc) | WARC web archive embedding (ISO 28500) |
| [c2pa-fonts](https://crates.io/crates/c2pa-fonts) | OpenType/TrueType (SFNT) font embedding |
| [c2pa-ml](https://crates.io/crates/c2pa-ml) | ML model containers: GGUF, SafeTensors, ONNX |
| [c2pa](https://crates.io/crates/c2pa) | Official C2PA SDK |

## Security

Found a vulnerability? Please report it privately — see [SECURITY.md](./SECURITY.md).

## License

Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.

Built by [WritersLogic](https://writerslogic.com)

