Skip to content

feat: expose Builder.signDataHashedEmbeddable#1

Open
deblanco wants to merge 2 commits into
mainfrom
feat/sign-data-hashed-embeddable
Open

feat: expose Builder.signDataHashedEmbeddable#1
deblanco wants to merge 2 commits into
mainfrom
feat/sign-data-hashed-embeddable

Conversation

@deblanco
Copy link
Copy Markdown

@deblanco deblanco commented May 9, 2026

Summary

  • Exposes the c2pa-rs Builder::sign_data_hashed_embeddable API in the JS wrapper as Builder.signDataHashedEmbeddable (sync, LocalSigner) and Builder.signDataHashedEmbeddableAsync (async, CallbackSigner).
  • Produces a signed embeddable manifest from a pre-computed DataHash (e.g. raw SHA-256 of the asset) without needing the asset bytes at sign time. Targets sidecar .c2pa and remote-manifest workflows.
  • Adds a DataHash type to js-src/types.d.ts mirroring the c2pa-rs DataHash serde struct: { name?, alg?, hash, pad?, exclusions? }. hash/pad accept Buffer | Uint8Array | number[]; exclusions accepts { start, length }[].
  • Native binding calls upstream Builder::data_hashed_placeholder internally before the sign call (required by upstream c2pa-rs to reserve the DataHash assertion slot in the builder definition); placeholder bytes are discarded since callers in this flow do not embed them.
  • README: new "Signing a data-hashed embeddable manifest" subsection under ### Builder with sync + async examples.

Files

  • src/neon_builder.rs — two new Neon methods.
  • src/lib.rs — registers builderSignDataHashedEmbeddable + builderSignDataHashedEmbeddableAsync.
  • js-src/index.node.d.ts — neon function declarations.
  • js-src/types.d.tsDataHash interface + two methods on BuilderInterface.
  • js-src/Builder.ts — public wrapper methods + serializeDataHash helper that normalizes Buffer/Uint8Array to number[] (c2pa-rs DataHash uses serde_bytes, which deserializes JSON arrays of u8, not the default Buffer.toJSON() shape).
  • js-src/Builder.spec.ts — two new vitest cases (sync + async).
  • README.md — usage docs.

API

// Sync, LocalSigner
const manifestBytes = builder.signDataHashedEmbeddable(
  signer,
  { name: 'raw asset', alg: 'sha256', hash, exclusions: [] },
  'image/jpeg',
);

// Async, CallbackSigner
const manifestBytes = await builder.signDataHashedEmbeddableAsync(
  callbackSigner,
  { alg: 'sha256', hash, exclusions: [] },
  'image/jpeg',
);

With exclusions: [] and alg: "sha256", the resulting c2pa.hash.data.hash equals SHA-256(asset) — a verifier with the original file recomputes the same digest and the manifest validates.

Test plan

  • cargo build — clean
  • cargo clippy — clean
  • tsc -b — clean
  • eslint — clean (Builder.ts, Builder.spec.ts)
  • vitest run js-src/Builder.spec.ts23/23 pass (incl. 2 new tests for sync + async DataHash signing)
  • Manual end-to-end with c2patool verify against a fixture + sidecar produced by the new API

Notes

  • Naming: c2pa-rs 0.82.1's public Builder method is sign_data_hashed_embeddable (no _manifest suffix — that lives on the internal Store::get_data_hashed_embeddable_manifest). This PR binds to the public API.
  • The async variant uses Builder::sign_data_hashed_embeddable_async, available in c2pa-rs 0.82.1 via the async_generic macro on the same method.

deblanco added 2 commits May 9, 2026 17:48
Adds Neon bindings + TS wrappers for c2pa-rs
`Builder::sign_data_hashed_embeddable` (sync) and its async variant.
Produces a signed embeddable manifest from a pre-computed `DataHash`,
without needing the asset bytes at sign time. Targets sidecar `.c2pa`
and remote-manifest workflows.

The binding calls `data_hashed_placeholder` internally before signing
(required by upstream to reserve the DataHash assertion slot); the
placeholder bytes are discarded since callers in this flow do not
embed them.
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.

1 participant