Skip to content

jco: digest-kind virtualizing adapter — platform SHA-2 + in-guest sha1-checked via composition #252

Description

@lann

Motivation

The jco targets declare sha1-checked missing: platform WebCrypto carries no sha1dc, and shipping sha1dc in JS would defeat platform-backing (targets.toml already frames the remedy as "a composition needing more must supply another provider"). Composing the full in-guest provider does deliver sha1-checked on Node today, but at the price of de-platforming all crypto. This issue records a designed middle path: platform-backed keyed crypto plus wasm sha1dc, on Node (and in principle any jco host).

Why a stripped provider profile is not the answer

A provider profile exporting only the digest island {digest, sha2, sha1-checked} composes cleanly (the island is severable: digest.digest is used only by sha2 and sha1-checked, and the island touches none of the wrap-input/unwrap-input/derive-input resources that entangle the whole keyed surface). But it forces sha2 capture: the provider must own the digest resource, so host-served sha2 becomes unlinkable and all SHA-2 runs in-guest — plausibly a 4-10x throughput regression vs the platform's native hashing. The profile's first act would be de-platforming SHA-2.

Per-algorithm cargo features on the provider are the wrong mechanism generally: features cannot vary the exported world, the severable unit is the resource-ownership island (not the algorithm), and nearly every combination a flag matrix advertises is an invalid composition (the class-D gate's dangling-alias failure mode).

The design: a digest-kind virtualizing adapter

A small component that imports lann:webcrypto/{digest, sha2} (the platform's, via the jco host) and exports lann:webcrypto/{digest, sha2, sha1-checked}:

  • The exported digest resource is the adapter's own type wrapping a two-armed enum: platform(imported digest) / sha1dc(local state from lann-webcrypto-core).
  • sha2.make-digest proxies to the imported mint and wraps; sha1-checked's two mints serve sha1dc locally.
  • compute on a platform-wrapped digest forwards the stream<u8> handle to the imported compute — stream ends are transferable, so the data plane bypasses the adapter entirely (bytes never transit it for SHA-2).

Composition: guest <- wac plug adapter (internalizes the digest kind; the adapter's digest/sha2/types imports propagate outward) <- jco host serves them.

Verified with wasm-tools 1.247 (world elaboration + dummy component build):

  • A world may import and export the same interface; the exported sha2/sha1-checked use of digest binds to the exported copy (elaboration shadowing), which is exactly what the design needs.
  • types is resource-free, so the exported interfaces' use types.{error} binds to the imported types — no captured copy, and error passthrough is the identical structural type (no conversion boilerplate).

Why the general mediation taxes do not apply here: the closure under resource use-edges is 3 interfaces; nothing else in the package consumes a digest resource, so cross-provider operand mixing stays unrepresentable (no new runtime failure mode); the bulk data path is zero-touch.

Costs and open items

  • New component artifact + world; a supported configuration means a new conformance target (a jco-composed adapter — the current jco adapter runs the uncomposed guest), targets.toml entry, matrix column. This is the dominant cost.
  • Tooling to prototype-verify: wit-bindgen generating both directions of digest/sha2 in one crate, and wac plug propagating the plug's own imports. Neither is exercised in this repo today.
  • The artifact's raison d'etre is an @unstable interface; consumers must thread the sha1-checked feature gate through their builds (silent-drop failure mode if forgotten).
  • Timing policy: no new work — digest input is public, sha1dc branches only on it (class-exempt, already classified).
  • The adapter needs no capabilities (digests draw no randomness).

Non-generalization

This works because digest resources have no external consumers. Every other kind is entangled through wrap-input/unwrap-input/derive-input, so a mediating adapter there would pay total mediation of the keyed surface and introduce a representable provenance-mismatch misuse the one-owner design currently excludes. This adapter is a one-off, not the first instance of a pattern.

Trigger

Build when a consumer needs sha1dc and platform-backed keyed crypto in the same component on a jco host, not before. Until then the full-provider composition covers the functional need; documenting that recipe costs a paragraph and no CI lanes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions