Skip to content

feat(c2pa-wasm): add fromBytes verification entry point for non-browser runtimes - #148

Open
SilyNoMeta wants to merge 1 commit into
contentauth:mainfrom
SilyNoMeta:feat/wasm-reader-from-bytes
Open

feat(c2pa-wasm): add fromBytes verification entry point for non-browser runtimes#148
SilyNoMeta wants to merge 1 commit into
contentauth:mainfrom
SilyNoMeta:feat/wasm-reader-from-bytes

Conversation

@SilyNoMeta

@SilyNoMeta SilyNoMeta commented Jul 6, 2026

Copy link
Copy Markdown

PR: Add WasmReader.fromBytes for non-browser C2PA verification - Refs #147

Problem

WasmReader today exposes only fromBlob / fromBlobFragment. Both wrap the asset in a
BlobStream (packages/c2pa-wasm/src/stream/blob_stream.rs) which reads bytes through
web_sys::FileReaderSync — a WorkerGlobalScope-only Web API. As a result, verification is only
usable inside a browser Web Worker. It cannot run in server/edge JavaScript runtimes that have no
Blob/FileReaderSync/Worker:

  • Cloudflare Workers (workerd) — no Worker constructor, no FileReaderSync.
  • Node.js (outside a DOM), Deno, Bun — same.

These runtimes already have the asset bytes in memory (an upload, a fetch response, a KV/R2
object), so requiring a Blob + FileReaderSync round-trip is both a portability blocker and,
per #93, a per-read JS↔WASM boundary cost.

Solution

Add a sibling entry point that takes the bytes directly:

#[wasm_bindgen(js_name = fromBytes)]
pub async fn from_bytes(
    format: &str,
    bytes: Vec<u8>,
    context_json: Option<String>,
) -> Result<WasmReader, JsString> {
    let stream = Cursor::new(bytes);
    WasmReader::from_stream(format, stream, context_json).await
}

It reuses the existing private from_stream(format, impl Read + Seek + Send, context_json)
helper (the same path fromBlob uses), feeding it a std::io::Cursor<Vec<u8>> instead of a
BlobStream. No new dependency, no browser API, and the returned WasmReader and its
validation results are identical to fromBlob. Cursor, Read, and Seek are already imported.

// TypeScript
const reader = await WasmReader.fromBytes("image/jpeg", new Uint8Array(bytes));

Runtimes unblocked

Node.js, Deno, Bun, and Cloudflare Workers (workerd). Verified end-to-end on workerd (via
wrangler dev) and Node.js with the pure-Rust crypto build (rust_native_crypto): a valid
asset validates (signature + hash bindings), an asset with no C2PA errors with JumbfNotFound, a
tampered signature yields claimSignature.mismatch (Invalid), and a tampered asset yields
assertion.dataHash.mismatch (Invalid) — byte-identical between Node and workerd. getRandomValues
resolves via globalThis.crypto on workerd; no FileReaderSync/Blob/Worker is touched.

Tests

Adds two wasm_bindgen_tests in wasm_reader.rs that run in the default (non-worker) context
(unlike the BlobStream tests, which require run_in_dedicated_worker) — this is itself the proof
that fromBytes needs no Web Worker:

  • from_bytes_reads_active_manifest — a signed JPEG (tests/fixtures/C.jpg) yields an active manifest.
  • from_bytes_without_manifest_errors — a buffer with no C2PA data is rejected, not silently accepted.

Notes

Checklist (contributor)

  • Individual Adobe CLA signed (one-time)
  • Changeset added (.changeset/add-wasm-reader-frombytes.md, minor bump for @contentauth/c2pa-wasm)
  • Adobe license header present on touched files
  • nx test c2pa-wasm green in CI (headless Chromium)

Wrap the asset bytes in an in-memory Cursor and reuse the existing from_stream helper, so verification works without Blob/FileReaderSync on Node.js, Deno, Bun, and Cloudflare Workers (workerd).
@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 11a221e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@contentauth/c2pa-wasm Minor
@contentauth/c2pa-web Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tmathern

tmathern commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

For node-based workers, why not use https://github.com/contentauth/c2pa-js/tree/main/packages/c2pa-node ?

@tmathern
tmathern requested review from ale-adobe and tmathern July 6, 2026 18:35
@SilyNoMeta

Copy link
Copy Markdown
Author

Good call, i actually dug into that one before going the wasm route!
c2pa-node builds c2pa-rs into a native c2pa.node addon (via cargo + a postinstall), plus it pulls in sharp/libvips, so it needs a full Node runtime with native addon loading. Workers (workerd) only runs JS + WASM, no native .node stuff, so it can't load there (same story for Deno/Bun by default)

@scouten-adobe
scouten-adobe requested a review from cdmurph32 July 31, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants