Skip to content

guest SDK: wrap the full signature surface; the docs stop denying exports - #378

Merged
lann merged 1 commit into
mainfrom
sdk-signature-surface
Aug 12, 2026
Merged

guest SDK: wrap the full signature surface; the docs stop denying exports#378
lann merged 1 commit into
mainfrom
sdk-signature-surface

Conversation

@lannbot

@lannbot lannbot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The drift (#228's actively misleading half)

Two SDK doc comments (SigningKeyOptions::extractable, SigningKey::extractable) stated there is no signing-key export operation. The WIT defines export-key-jwk/-pkcs8 on signature.signing-key (wit/webcrypto.wit:730,734) and both Rust implementations serve them. The docs now name the real exports and keep the keystore note.

The gap (resolved as: close it)

Ruling per the issue's option 1 — the surface is stable pre-publication, the wrappers are thin forwarders, and an SDK wrapping import-verifying-key-raw but not -spki/-jwk pushes consumers back to raw bindings for adjacent calls:

  • VerifyingKey::export_key_spki/_jwk beside the existing _raw
  • SigningKey::export_key_jwk/_pkcs8 + to_wrap_input_jwk/_pkcs8 (matching Mac/CipherKey/DecryptionKey's gated-export shape)
  • ed25519/ecdsa: import_verifying_key_spki/_jwk, import_signing_key_pkcs8/_jwk, unwrap_signing_key_pkcs8/_jwk — the unwrap mints included for symmetry with the sibling modules (aes_gcm, hmac_sha2), which all wrap theirs

Demo coverage (every new wrapper executes)

  • ed25519 (all legs, composed included — the provider exports ed25519-sign): public spki/jwk imports verify RFC 8032 test 2; raw/SPKI exports round-trip byte-for-byte (canonical encodings), JWK export re-imports; private pkcs8/jwk imports from the test-2 seed sign to the known signature byte-for-byte (Ed25519 is deterministic); non-extractable export fails NotExtractable; extractable exports round-trip semantically (PKCS#8 v1/v2 differs across providers); both wrap tours transit the key through an AEAD kek without the material reaching the caller.
  • ecdsa: public spki/jwk imports + exports in the always-on check; the signing mints (ecdsa-sign-mints) join the host-only build — the provider withholds ecdsa-sign (class D) — with RFC 6979 A.2.5's key: each minted key's signature verifies against the paired public half (ECDSA bytes are implementation-dependent by design, so no byte KAT).
  • The demo feature/artifact rename rsa-oaephost-only (after the conformance architecture's host-only suite): the opt-in set now has two members, so it is named for the category — checks whose imports the in-guest provider withholds. Polarity unchanged from crypto-demo: exercise the rsa_oaep wrappers in an opt-in build #377: off by default, serving hosts opt in.

Found while building: the signing-key wrap tours cannot use AES-KW — RFC 3394 wraps only 8-byte multiples and the P-256 PKCS#8 form is 138 bytes (the WIT's stated contract, surfaced by the gate) — so they wrap under AES-GCM with per-wrap nonces.

Verification

  • just demo::test-composed: 21 checks (extended ed25519 surface runs in-guest)
  • cargo test -p wasmtime-demo + just demo::wasmtime: 23 checks incl. ecdsa-sign-mints, rsa-oaep-key-transport
  • just demo::test-node (Node 24.19): 23 checks
  • just fmt-check, just clippy clean; cargo doc -p polymorph-webcrypto-guest adds no new warnings; artifact WITs: 0 withheld-interface mentions in the default build, 6 in host-only

Fixes #228

…orts

Two doc comments (SigningKeyOptions::extractable, SigningKey::
extractable) said no signing-key export operation exists. The WIT
defines export-key-jwk/-pkcs8 on signature.signing-key and both Rust
implementations serve them — an SDK consumer reading those docs would
conclude the operations are absent from the package, which is false.
The docs now name the real exports.

The gap behind the drift closes rather than being declared: the SDK
wraps the whole signature surface it sat inside of —

- VerifyingKey::export_key_spki/_jwk beside the existing _raw;
- SigningKey::export_key_jwk/_pkcs8 and to_wrap_input_jwk/_pkcs8,
  matching the other extractability-gated key types;
- ed25519/ecdsa import_verifying_key_spki/_jwk,
  import_signing_key_pkcs8/_jwk, and unwrap_signing_key_pkcs8/_jwk,
  mirroring the sibling modules' constructor-per-WIT-function shape.

crypto-demo executes every new wrapper, per its harness role. The
ed25519 checks extend in place (RFC 8032 test 2's seed: deterministic
signatures make the round trips exact) and run on every leg including
composed. The ECDSA signing mints join the host-only build (the
provider withholds ecdsa-sign — class D), so the demo feature and
artifact rename from rsa-oaep to host-only, after the conformance
architecture's host-only suite; RFC 6979 A.2.5's key signs and the
paired public half verifies (ECDSA signatures are not deterministic
across implementations). The signing-key wrap tours use an AEAD kek:
AES-KW takes only 8-byte multiples, which the private serializations
do not promise (the P-256 PKCS#8 form is 138 bytes), and private
export round-trips compare semantically — PKCS#8 v1/v2 forms differ
across providers — via signatures instead of bytes.

Fixes #228
@lann
lann merged commit f71cd6d into main Aug 12, 2026
10 checks passed
@lann
lann deleted the sdk-signature-surface branch August 12, 2026 13:42
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.

guest SDK: signing/verifying-key docs contradict the WIT; signature import/export surface unwrapped

2 participants