c2pa-structured-text 0.2.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/README.md +156 -0
- package/c2pa-structured-text.d.ts +83 -0
- package/c2pa-structured-text.js +9 -0
- package/c2pa-structured-text_bg.js +455 -0
- package/c2pa-structured-text_bg.wasm +0 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">c2pa-structured-text</h1>
|
|
3
|
+
<p align="center">C2PA manifest embedding, hard binding, and validation for structured text formats</p>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<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>
|
|
8
|
+
<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>
|
|
9
|
+
<a href="https://docs.rs/c2pa-structured-text"><img src="https://docs.rs/c2pa-structured-text/badge.svg" alt="docs.rs"></a>
|
|
10
|
+
<a href="#license"><img src="https://img.shields.io/crates/l/c2pa-structured-text.svg" alt="License"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
The manifest block uses fixed ASCII armour-style delimiters modelled on [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880#section-6.2):
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
-----BEGIN C2PA MANIFEST----- <reference> -----END C2PA MANIFEST-----
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This crate owns three things:
|
|
24
|
+
|
|
25
|
+
1. **Embed / Extract** — place a reference or an inline manifest as a comment or front matter block, and locate and resolve it again.
|
|
26
|
+
2. **Hard binding** — define and compute the exact `c2pa.hash.data` coverage for structured text, and verify it.
|
|
27
|
+
3. **A validation bridge** to [`c2pa-rs`](https://crates.io/crates/c2pa) for signature, trust, and assertion validation — which this crate does *not* reimplement.
|
|
28
|
+
|
|
29
|
+
> 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`.
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
[dependencies]
|
|
35
|
+
c2pa-structured-text = "0.1"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Embed a manifest reference
|
|
39
|
+
|
|
40
|
+
```rust
|
|
41
|
+
use c2pa_structured_text::{embed_manifest, ManifestRef};
|
|
42
|
+
|
|
43
|
+
let signed = embed_manifest(
|
|
44
|
+
"print('hello')\n",
|
|
45
|
+
ManifestRef::Url("https://example.com/manifests/abc.c2pa"),
|
|
46
|
+
"#", // comment prefix
|
|
47
|
+
None, // no comment suffix
|
|
48
|
+
);
|
|
49
|
+
// # -----BEGIN C2PA MANIFEST----- https://example.com/manifests/abc.c2pa -----END C2PA MANIFEST-----
|
|
50
|
+
// print('hello')
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`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.
|
|
54
|
+
|
|
55
|
+
### Extract a manifest reference
|
|
56
|
+
|
|
57
|
+
```rust
|
|
58
|
+
use c2pa_structured_text::{extract_manifest, classify_reference, Reference};
|
|
59
|
+
|
|
60
|
+
let text = "# -----BEGIN C2PA MANIFEST----- https://example.com/m.c2pa -----END C2PA MANIFEST-----
|
|
61
|
+
print('hello')
|
|
62
|
+
";
|
|
63
|
+
let result = extract_manifest(text).unwrap();
|
|
64
|
+
assert_eq!(result.reference, "https://example.com/m.c2pa");
|
|
65
|
+
|
|
66
|
+
// A `data:application/c2pa;base64,` reference decodes to the manifest bytes;
|
|
67
|
+
// anything else is treated as an external URI.
|
|
68
|
+
match classify_reference(&result.reference).unwrap() {
|
|
69
|
+
Reference::Url(url) => { /* fetch it */ }
|
|
70
|
+
Reference::Embedded(bytes) => { /* raw JUMBF manifest store */ }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## The Hard Binding
|
|
75
|
+
|
|
76
|
+
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.
|
|
77
|
+
|
|
78
|
+
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.
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
# #[cfg(feature = "hard-binding")] {
|
|
82
|
+
use c2pa_structured_text::hardbinding::{compute_data_hash, verify_data_hash, Algorithm};
|
|
83
|
+
|
|
84
|
+
let signed = c2pa_structured_text::embed_manifest(
|
|
85
|
+
"print('hello')\n",
|
|
86
|
+
c2pa_structured_text::ManifestRef::Url("https://example.com/m.c2pa"),
|
|
87
|
+
"#",
|
|
88
|
+
None,
|
|
89
|
+
);
|
|
90
|
+
let data_hash = compute_data_hash(&signed, Algorithm::Sha256).unwrap();
|
|
91
|
+
verify_data_hash(&signed, &data_hash).unwrap();
|
|
92
|
+
# }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
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`).
|
|
96
|
+
|
|
97
|
+
### Fragility — and the soft-binding recovery path
|
|
98
|
+
|
|
99
|
+
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.
|
|
100
|
+
|
|
101
|
+
## Validating with c2pa-rs
|
|
102
|
+
|
|
103
|
+
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.
|
|
104
|
+
|
|
105
|
+
```rust,ignore
|
|
106
|
+
use c2pa_structured_text::bridge;
|
|
107
|
+
|
|
108
|
+
// Inline (data:) references are decoded automatically; URL references are
|
|
109
|
+
// fetched with the `remote` feature (or resolve them yourself and call
|
|
110
|
+
// `bridge::validate_with_manifest`).
|
|
111
|
+
let reader = bridge::validate(&signed, bridge::DEFAULT_FORMAT)?;
|
|
112
|
+
println!("{:?}", reader.validation_state());
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Features
|
|
116
|
+
|
|
117
|
+
| Feature | Adds | Pulls |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| *(none)* | embed, extract, exclusion-range and covered-byte primitives | — |
|
|
120
|
+
| `hard-binding` | `compute_data_hash` / `verify_data_hash` (SHA2-256/384/512) | `sha2` |
|
|
121
|
+
| `c2pa` | the `bridge` to `c2pa-rs` for signature/trust/assertion validation | `c2pa` |
|
|
122
|
+
| `remote` | HTTP(S) resolution of URL references in the bridge | `c2pa`, `ureq` |
|
|
123
|
+
|
|
124
|
+
No feature is enabled by default; the core API has no dependencies.
|
|
125
|
+
|
|
126
|
+
## Supported Formats
|
|
127
|
+
|
|
128
|
+
Any text format with a comment syntax or front matter convention:
|
|
129
|
+
|
|
130
|
+
| Comment Style | Formats | Example |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `#` | Python, Ruby, Shell, YAML, TOML | `# -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
|
|
133
|
+
| `//` | JavaScript, TypeScript, Go, Rust, C++ | `// -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
|
|
134
|
+
| `--` | SQL, Lua, Haskell | `-- -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST-----` |
|
|
135
|
+
| `/* */` | CSS, C, Java | `/* -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST----- */` |
|
|
136
|
+
| `<!-- -->` | Markdown, XML (non-HTML) | `<!-- -----BEGIN C2PA MANIFEST----- ... -----END C2PA MANIFEST----- -->` |
|
|
137
|
+
| Front matter | Markdown (YAML), TOML | Multi-line form between front matter delimiters |
|
|
138
|
+
|
|
139
|
+
> **WebVTT** is structured text per the specification, but it is owned by the dedicated [c2pa-vtt](https://github.com/writerslogic/c2pa-vtt) crate, not this one. `c2pa-vtt` places the `NOTE` block immediately after the `WEBVTT` header, where it survives HLS/DASH segmentation. Use `c2pa-vtt` for `.vtt` files.
|
|
140
|
+
>
|
|
141
|
+
> **HTML** and **SVG** have their own C2PA embedding methods and are out of scope here.
|
|
142
|
+
|
|
143
|
+
## Related Crates
|
|
144
|
+
|
|
145
|
+
| Crate | Description |
|
|
146
|
+
|---|---|
|
|
147
|
+
| [c2pa-vtt](https://github.com/writerslogic/c2pa-vtt) | **Canonical owner of WebVTT** captions/subtitles (streaming-safe placement) |
|
|
148
|
+
| [c2pa-text-binding](https://github.com/writerslogic/c2pa-text-binding) | Soft binding and content fingerprinting for text assets (recovery path) |
|
|
149
|
+
| [c2pa-text](https://crates.io/crates/c2pa-text) | Unstructured text embedding via Unicode Variation Selectors |
|
|
150
|
+
| [c2pa-rs](https://crates.io/crates/c2pa) | Official C2PA SDK |
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.
|
|
155
|
+
|
|
156
|
+
Built by [WritersLogic](https://writerslogic.com)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A classified reference: `kind` is `"url"` or `"embedded"`; exactly one of
|
|
6
|
+
* `url` / `bytes` is populated.
|
|
7
|
+
*/
|
|
8
|
+
export class ClassifiedReference {
|
|
9
|
+
private constructor();
|
|
10
|
+
free(): void;
|
|
11
|
+
[Symbol.dispose](): void;
|
|
12
|
+
readonly bytes: Uint8Array | undefined;
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
readonly url: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A computed structured-text `c2pa.hash.data` binding.
|
|
19
|
+
*/
|
|
20
|
+
export class DataHash {
|
|
21
|
+
private constructor();
|
|
22
|
+
free(): void;
|
|
23
|
+
[Symbol.dispose](): void;
|
|
24
|
+
/**
|
|
25
|
+
* The assertion serialised to `c2pa-rs`-compatible JSON.
|
|
26
|
+
*/
|
|
27
|
+
toJson(): string;
|
|
28
|
+
readonly alg: string;
|
|
29
|
+
readonly hash: Uint8Array;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The hard-binding exclusion range over the manifest block.
|
|
34
|
+
*/
|
|
35
|
+
export class Exclusion {
|
|
36
|
+
private constructor();
|
|
37
|
+
free(): void;
|
|
38
|
+
[Symbol.dispose](): void;
|
|
39
|
+
readonly length: number;
|
|
40
|
+
readonly start: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Classify and resolve a reference string: decode a `data:` URI to the
|
|
45
|
+
* manifest bytes, or return an external URL.
|
|
46
|
+
*/
|
|
47
|
+
export function classifyReference(reference: string): ClassifiedReference;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compute the hard binding for `text` with `alg` in (`"sha256"`, `"sha384"`,
|
|
51
|
+
* `"sha512"`).
|
|
52
|
+
*/
|
|
53
|
+
export function computeDataHash(text: string, alg: string): DataHash;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Embed a manifest block carrying the manifest store inline as a
|
|
57
|
+
* `data:application/c2pa;base64,` URI.
|
|
58
|
+
*/
|
|
59
|
+
export function embedManifestEmbedded(text: string, manifest: Uint8Array, comment_prefix: string, comment_suffix?: string | null): string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Embed a manifest block referencing an external manifest URL, as the first
|
|
63
|
+
* line of the file.
|
|
64
|
+
*/
|
|
65
|
+
export function embedManifestUrl(text: string, url: string, comment_prefix: string, comment_suffix?: string | null): string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extract the manifest reference string from structured text. Throws if no
|
|
69
|
+
* single manifest block is present.
|
|
70
|
+
*/
|
|
71
|
+
export function extractReference(text: string): string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Compute the `c2pa.hash.data` exclusion range covering the manifest block.
|
|
75
|
+
*/
|
|
76
|
+
export function manifestExclusion(text: string): Exclusion;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Verify a hard binding against `text`. Returns `true` on a match, `false` on
|
|
80
|
+
* a content mismatch, and throws on a malformed assertion or unsupported
|
|
81
|
+
* algorithm.
|
|
82
|
+
*/
|
|
83
|
+
export function verifyDataHash(text: string, data_hash: DataHash): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* @ts-self-types="./c2pa-structured-text.d.ts" */
|
|
2
|
+
import * as wasm from "./c2pa-structured-text_bg.wasm";
|
|
3
|
+
import { __wbg_set_wasm } from "./c2pa-structured-text_bg.js";
|
|
4
|
+
|
|
5
|
+
__wbg_set_wasm(wasm);
|
|
6
|
+
wasm.__wbindgen_start();
|
|
7
|
+
export {
|
|
8
|
+
ClassifiedReference, DataHash, Exclusion, classifyReference, computeDataHash, embedManifestEmbedded, embedManifestUrl, extractReference, manifestExclusion, verifyDataHash
|
|
9
|
+
} from "./c2pa-structured-text_bg.js";
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A classified reference: `kind` is `"url"` or `"embedded"`; exactly one of
|
|
3
|
+
* `url` / `bytes` is populated.
|
|
4
|
+
*/
|
|
5
|
+
export class ClassifiedReference {
|
|
6
|
+
static __wrap(ptr) {
|
|
7
|
+
const obj = Object.create(ClassifiedReference.prototype);
|
|
8
|
+
obj.__wbg_ptr = ptr;
|
|
9
|
+
ClassifiedReferenceFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
10
|
+
return obj;
|
|
11
|
+
}
|
|
12
|
+
__destroy_into_raw() {
|
|
13
|
+
const ptr = this.__wbg_ptr;
|
|
14
|
+
this.__wbg_ptr = 0;
|
|
15
|
+
ClassifiedReferenceFinalization.unregister(this);
|
|
16
|
+
return ptr;
|
|
17
|
+
}
|
|
18
|
+
free() {
|
|
19
|
+
const ptr = this.__destroy_into_raw();
|
|
20
|
+
wasm.__wbg_classifiedreference_free(ptr, 0);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @returns {Uint8Array | undefined}
|
|
24
|
+
*/
|
|
25
|
+
get bytes() {
|
|
26
|
+
const ret = wasm.classifiedreference_bytes(this.__wbg_ptr);
|
|
27
|
+
let v1;
|
|
28
|
+
if (ret[0] !== 0) {
|
|
29
|
+
v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
30
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
31
|
+
}
|
|
32
|
+
return v1;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
get kind() {
|
|
38
|
+
let deferred1_0;
|
|
39
|
+
let deferred1_1;
|
|
40
|
+
try {
|
|
41
|
+
const ret = wasm.classifiedreference_kind(this.__wbg_ptr);
|
|
42
|
+
deferred1_0 = ret[0];
|
|
43
|
+
deferred1_1 = ret[1];
|
|
44
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
45
|
+
} finally {
|
|
46
|
+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @returns {string | undefined}
|
|
51
|
+
*/
|
|
52
|
+
get url() {
|
|
53
|
+
const ret = wasm.classifiedreference_url(this.__wbg_ptr);
|
|
54
|
+
let v1;
|
|
55
|
+
if (ret[0] !== 0) {
|
|
56
|
+
v1 = getStringFromWasm0(ret[0], ret[1]).slice();
|
|
57
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
58
|
+
}
|
|
59
|
+
return v1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (Symbol.dispose) ClassifiedReference.prototype[Symbol.dispose] = ClassifiedReference.prototype.free;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A computed structured-text `c2pa.hash.data` binding.
|
|
66
|
+
*/
|
|
67
|
+
export class DataHash {
|
|
68
|
+
static __wrap(ptr) {
|
|
69
|
+
const obj = Object.create(DataHash.prototype);
|
|
70
|
+
obj.__wbg_ptr = ptr;
|
|
71
|
+
DataHashFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
72
|
+
return obj;
|
|
73
|
+
}
|
|
74
|
+
__destroy_into_raw() {
|
|
75
|
+
const ptr = this.__wbg_ptr;
|
|
76
|
+
this.__wbg_ptr = 0;
|
|
77
|
+
DataHashFinalization.unregister(this);
|
|
78
|
+
return ptr;
|
|
79
|
+
}
|
|
80
|
+
free() {
|
|
81
|
+
const ptr = this.__destroy_into_raw();
|
|
82
|
+
wasm.__wbg_datahash_free(ptr, 0);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
get alg() {
|
|
88
|
+
let deferred1_0;
|
|
89
|
+
let deferred1_1;
|
|
90
|
+
try {
|
|
91
|
+
const ret = wasm.datahash_alg(this.__wbg_ptr);
|
|
92
|
+
deferred1_0 = ret[0];
|
|
93
|
+
deferred1_1 = ret[1];
|
|
94
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
95
|
+
} finally {
|
|
96
|
+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* @returns {Uint8Array}
|
|
101
|
+
*/
|
|
102
|
+
get hash() {
|
|
103
|
+
const ret = wasm.datahash_hash(this.__wbg_ptr);
|
|
104
|
+
var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
105
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
106
|
+
return v1;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The assertion serialised to `c2pa-rs`-compatible JSON.
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
toJson() {
|
|
113
|
+
let deferred1_0;
|
|
114
|
+
let deferred1_1;
|
|
115
|
+
try {
|
|
116
|
+
const ret = wasm.datahash_toJson(this.__wbg_ptr);
|
|
117
|
+
deferred1_0 = ret[0];
|
|
118
|
+
deferred1_1 = ret[1];
|
|
119
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
120
|
+
} finally {
|
|
121
|
+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (Symbol.dispose) DataHash.prototype[Symbol.dispose] = DataHash.prototype.free;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The hard-binding exclusion range over the manifest block.
|
|
129
|
+
*/
|
|
130
|
+
export class Exclusion {
|
|
131
|
+
static __wrap(ptr) {
|
|
132
|
+
const obj = Object.create(Exclusion.prototype);
|
|
133
|
+
obj.__wbg_ptr = ptr;
|
|
134
|
+
ExclusionFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
135
|
+
return obj;
|
|
136
|
+
}
|
|
137
|
+
__destroy_into_raw() {
|
|
138
|
+
const ptr = this.__wbg_ptr;
|
|
139
|
+
this.__wbg_ptr = 0;
|
|
140
|
+
ExclusionFinalization.unregister(this);
|
|
141
|
+
return ptr;
|
|
142
|
+
}
|
|
143
|
+
free() {
|
|
144
|
+
const ptr = this.__destroy_into_raw();
|
|
145
|
+
wasm.__wbg_exclusion_free(ptr, 0);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @returns {number}
|
|
149
|
+
*/
|
|
150
|
+
get length() {
|
|
151
|
+
const ret = wasm.__wbg_get_exclusion_length(this.__wbg_ptr);
|
|
152
|
+
return ret >>> 0;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @returns {number}
|
|
156
|
+
*/
|
|
157
|
+
get start() {
|
|
158
|
+
const ret = wasm.__wbg_get_exclusion_start(this.__wbg_ptr);
|
|
159
|
+
return ret >>> 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (Symbol.dispose) Exclusion.prototype[Symbol.dispose] = Exclusion.prototype.free;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Classify and resolve a reference string: decode a `data:` URI to the
|
|
166
|
+
* manifest bytes, or return an external URL.
|
|
167
|
+
* @param {string} reference
|
|
168
|
+
* @returns {ClassifiedReference}
|
|
169
|
+
*/
|
|
170
|
+
export function classifyReference(reference) {
|
|
171
|
+
const ptr0 = passStringToWasm0(reference, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
172
|
+
const len0 = WASM_VECTOR_LEN;
|
|
173
|
+
const ret = wasm.classifyReference(ptr0, len0);
|
|
174
|
+
if (ret[2]) {
|
|
175
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
176
|
+
}
|
|
177
|
+
return ClassifiedReference.__wrap(ret[0]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Compute the hard binding for `text` with `alg` in (`"sha256"`, `"sha384"`,
|
|
182
|
+
* `"sha512"`).
|
|
183
|
+
* @param {string} text
|
|
184
|
+
* @param {string} alg
|
|
185
|
+
* @returns {DataHash}
|
|
186
|
+
*/
|
|
187
|
+
export function computeDataHash(text, alg) {
|
|
188
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
189
|
+
const len0 = WASM_VECTOR_LEN;
|
|
190
|
+
const ptr1 = passStringToWasm0(alg, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
191
|
+
const len1 = WASM_VECTOR_LEN;
|
|
192
|
+
const ret = wasm.computeDataHash(ptr0, len0, ptr1, len1);
|
|
193
|
+
if (ret[2]) {
|
|
194
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
195
|
+
}
|
|
196
|
+
return DataHash.__wrap(ret[0]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Embed a manifest block carrying the manifest store inline as a
|
|
201
|
+
* `data:application/c2pa;base64,` URI.
|
|
202
|
+
* @param {string} text
|
|
203
|
+
* @param {Uint8Array} manifest
|
|
204
|
+
* @param {string} comment_prefix
|
|
205
|
+
* @param {string | null} [comment_suffix]
|
|
206
|
+
* @returns {string}
|
|
207
|
+
*/
|
|
208
|
+
export function embedManifestEmbedded(text, manifest, comment_prefix, comment_suffix) {
|
|
209
|
+
let deferred5_0;
|
|
210
|
+
let deferred5_1;
|
|
211
|
+
try {
|
|
212
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
213
|
+
const len0 = WASM_VECTOR_LEN;
|
|
214
|
+
const ptr1 = passArray8ToWasm0(manifest, wasm.__wbindgen_malloc);
|
|
215
|
+
const len1 = WASM_VECTOR_LEN;
|
|
216
|
+
const ptr2 = passStringToWasm0(comment_prefix, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
217
|
+
const len2 = WASM_VECTOR_LEN;
|
|
218
|
+
var ptr3 = isLikeNone(comment_suffix) ? 0 : passStringToWasm0(comment_suffix, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
219
|
+
var len3 = WASM_VECTOR_LEN;
|
|
220
|
+
const ret = wasm.embedManifestEmbedded(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
221
|
+
deferred5_0 = ret[0];
|
|
222
|
+
deferred5_1 = ret[1];
|
|
223
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
224
|
+
} finally {
|
|
225
|
+
wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Embed a manifest block referencing an external manifest URL, as the first
|
|
231
|
+
* line of the file.
|
|
232
|
+
* @param {string} text
|
|
233
|
+
* @param {string} url
|
|
234
|
+
* @param {string} comment_prefix
|
|
235
|
+
* @param {string | null} [comment_suffix]
|
|
236
|
+
* @returns {string}
|
|
237
|
+
*/
|
|
238
|
+
export function embedManifestUrl(text, url, comment_prefix, comment_suffix) {
|
|
239
|
+
let deferred5_0;
|
|
240
|
+
let deferred5_1;
|
|
241
|
+
try {
|
|
242
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
243
|
+
const len0 = WASM_VECTOR_LEN;
|
|
244
|
+
const ptr1 = passStringToWasm0(url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
245
|
+
const len1 = WASM_VECTOR_LEN;
|
|
246
|
+
const ptr2 = passStringToWasm0(comment_prefix, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
247
|
+
const len2 = WASM_VECTOR_LEN;
|
|
248
|
+
var ptr3 = isLikeNone(comment_suffix) ? 0 : passStringToWasm0(comment_suffix, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
249
|
+
var len3 = WASM_VECTOR_LEN;
|
|
250
|
+
const ret = wasm.embedManifestUrl(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
251
|
+
deferred5_0 = ret[0];
|
|
252
|
+
deferred5_1 = ret[1];
|
|
253
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
254
|
+
} finally {
|
|
255
|
+
wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Extract the manifest reference string from structured text. Throws if no
|
|
261
|
+
* single manifest block is present.
|
|
262
|
+
* @param {string} text
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
265
|
+
export function extractReference(text) {
|
|
266
|
+
let deferred3_0;
|
|
267
|
+
let deferred3_1;
|
|
268
|
+
try {
|
|
269
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
270
|
+
const len0 = WASM_VECTOR_LEN;
|
|
271
|
+
const ret = wasm.extractReference(ptr0, len0);
|
|
272
|
+
var ptr2 = ret[0];
|
|
273
|
+
var len2 = ret[1];
|
|
274
|
+
if (ret[3]) {
|
|
275
|
+
ptr2 = 0; len2 = 0;
|
|
276
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
277
|
+
}
|
|
278
|
+
deferred3_0 = ptr2;
|
|
279
|
+
deferred3_1 = len2;
|
|
280
|
+
return getStringFromWasm0(ptr2, len2);
|
|
281
|
+
} finally {
|
|
282
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Compute the `c2pa.hash.data` exclusion range covering the manifest block.
|
|
288
|
+
* @param {string} text
|
|
289
|
+
* @returns {Exclusion}
|
|
290
|
+
*/
|
|
291
|
+
export function manifestExclusion(text) {
|
|
292
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
293
|
+
const len0 = WASM_VECTOR_LEN;
|
|
294
|
+
const ret = wasm.manifestExclusion(ptr0, len0);
|
|
295
|
+
if (ret[2]) {
|
|
296
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
297
|
+
}
|
|
298
|
+
return Exclusion.__wrap(ret[0]);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Verify a hard binding against `text`. Returns `true` on a match, `false` on
|
|
303
|
+
* a content mismatch, and throws on a malformed assertion or unsupported
|
|
304
|
+
* algorithm.
|
|
305
|
+
* @param {string} text
|
|
306
|
+
* @param {DataHash} data_hash
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
*/
|
|
309
|
+
export function verifyDataHash(text, data_hash) {
|
|
310
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
311
|
+
const len0 = WASM_VECTOR_LEN;
|
|
312
|
+
_assertClass(data_hash, DataHash);
|
|
313
|
+
const ret = wasm.verifyDataHash(ptr0, len0, data_hash.__wbg_ptr);
|
|
314
|
+
if (ret[2]) {
|
|
315
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
316
|
+
}
|
|
317
|
+
return ret[0] !== 0;
|
|
318
|
+
}
|
|
319
|
+
export function __wbg_Error_92b29b0548f8b746(arg0, arg1) {
|
|
320
|
+
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
321
|
+
return ret;
|
|
322
|
+
}
|
|
323
|
+
export function __wbg___wbindgen_throw_344f42d3211c4765(arg0, arg1) {
|
|
324
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
325
|
+
}
|
|
326
|
+
export function __wbindgen_init_externref_table() {
|
|
327
|
+
const table = wasm.__wbindgen_externrefs;
|
|
328
|
+
const offset = table.grow(4);
|
|
329
|
+
table.set(0, undefined);
|
|
330
|
+
table.set(offset + 0, undefined);
|
|
331
|
+
table.set(offset + 1, null);
|
|
332
|
+
table.set(offset + 2, true);
|
|
333
|
+
table.set(offset + 3, false);
|
|
334
|
+
}
|
|
335
|
+
const ClassifiedReferenceFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
336
|
+
? { register: () => {}, unregister: () => {} }
|
|
337
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_classifiedreference_free(ptr, 1));
|
|
338
|
+
const DataHashFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
339
|
+
? { register: () => {}, unregister: () => {} }
|
|
340
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_datahash_free(ptr, 1));
|
|
341
|
+
const ExclusionFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
342
|
+
? { register: () => {}, unregister: () => {} }
|
|
343
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_exclusion_free(ptr, 1));
|
|
344
|
+
|
|
345
|
+
function _assertClass(instance, klass) {
|
|
346
|
+
if (!(instance instanceof klass)) {
|
|
347
|
+
throw new Error(`expected instance of ${klass.name}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function getArrayU8FromWasm0(ptr, len) {
|
|
352
|
+
ptr = ptr >>> 0;
|
|
353
|
+
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function getStringFromWasm0(ptr, len) {
|
|
357
|
+
return decodeText(ptr >>> 0, len);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let cachedUint8ArrayMemory0 = null;
|
|
361
|
+
function getUint8ArrayMemory0() {
|
|
362
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
363
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
364
|
+
}
|
|
365
|
+
return cachedUint8ArrayMemory0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function isLikeNone(x) {
|
|
369
|
+
return x === undefined || x === null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function passArray8ToWasm0(arg, malloc) {
|
|
373
|
+
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
|
374
|
+
getUint8ArrayMemory0().set(arg, ptr / 1);
|
|
375
|
+
WASM_VECTOR_LEN = arg.length;
|
|
376
|
+
return ptr;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
380
|
+
if (realloc === undefined) {
|
|
381
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
382
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
383
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
384
|
+
WASM_VECTOR_LEN = buf.length;
|
|
385
|
+
return ptr;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let len = arg.length;
|
|
389
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
390
|
+
|
|
391
|
+
const mem = getUint8ArrayMemory0();
|
|
392
|
+
|
|
393
|
+
let offset = 0;
|
|
394
|
+
|
|
395
|
+
for (; offset < len; offset++) {
|
|
396
|
+
const code = arg.charCodeAt(offset);
|
|
397
|
+
if (code > 0x7F) break;
|
|
398
|
+
mem[ptr + offset] = code;
|
|
399
|
+
}
|
|
400
|
+
if (offset !== len) {
|
|
401
|
+
if (offset !== 0) {
|
|
402
|
+
arg = arg.slice(offset);
|
|
403
|
+
}
|
|
404
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
405
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
406
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
407
|
+
|
|
408
|
+
offset += ret.written;
|
|
409
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
WASM_VECTOR_LEN = offset;
|
|
413
|
+
return ptr;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function takeFromExternrefTable0(idx) {
|
|
417
|
+
const value = wasm.__wbindgen_externrefs.get(idx);
|
|
418
|
+
wasm.__externref_table_dealloc(idx);
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
423
|
+
cachedTextDecoder.decode();
|
|
424
|
+
const MAX_SAFARI_DECODE_BYTES = 2146435072;
|
|
425
|
+
let numBytesDecoded = 0;
|
|
426
|
+
function decodeText(ptr, len) {
|
|
427
|
+
numBytesDecoded += len;
|
|
428
|
+
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
|
|
429
|
+
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
430
|
+
cachedTextDecoder.decode();
|
|
431
|
+
numBytesDecoded = len;
|
|
432
|
+
}
|
|
433
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const cachedTextEncoder = new TextEncoder();
|
|
437
|
+
|
|
438
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
439
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
440
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
441
|
+
view.set(buf);
|
|
442
|
+
return {
|
|
443
|
+
read: arg.length,
|
|
444
|
+
written: buf.length
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
let WASM_VECTOR_LEN = 0;
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
let wasm;
|
|
453
|
+
export function __wbg_set_wasm(val) {
|
|
454
|
+
wasm = val;
|
|
455
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "c2pa-structured-text",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"description": "C2PA manifest embedding, hard binding, and validation for structured text formats using ASCII armour delimiters",
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"license": "MIT OR Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/writerslogic/c2pa-structured-text"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"c2pa-structured-text_bg.wasm",
|
|
13
|
+
"c2pa-structured-text.js",
|
|
14
|
+
"c2pa-structured-text_bg.js",
|
|
15
|
+
"c2pa-structured-text.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"main": "c2pa-structured-text.js",
|
|
18
|
+
"types": "c2pa-structured-text.d.ts",
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"./c2pa-structured-text.js",
|
|
21
|
+
"./snippets/*"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"c2pa",
|
|
25
|
+
"provenance",
|
|
26
|
+
"text",
|
|
27
|
+
"embedding"
|
|
28
|
+
]
|
|
29
|
+
}
|